mirror of
https://github.com/aydenjahola/discord-multipurpose-bot.git
synced 2025-09-21 06:41:35 +01:00
Some checks are pending
Docker / build (push) Waiting to run
* add simple music functionality * update workflow * update Dockerfile * update Dockerfile * update Dockerfile * update Dockerfile * add few more music commands * add lyrics command * update lyrics command * add loop, and add categories to all commands * change discord status * seperate distube and change startup console theme * Update README * UPDATE LICENSE file * fix docker compose image, add better error handling for distube and update tagging workflow * switch to node-alpine image for docker * switch to node-alpine image for docker * update ascii * music commands imporvements, implement live lyrics, some guards and bot leaving on empty * use ffmpeg package rather than ffmpeg-static
98 lines
2.9 KiB
JavaScript
98 lines
2.9 KiB
JavaScript
const { SlashCommandBuilder } = require("discord.js");
|
||
const { requireVC, requireQueue } = require("../../utils/musicGuards");
|
||
|
||
/** Parse "90", "1:30", "01:02:03", "+30", "-10", "+1:00", "-0:30" */
|
||
function parseTime(input) {
|
||
const t = input.trim();
|
||
const sign = t.startsWith("+") ? 1 : t.startsWith("-") ? -1 : 0;
|
||
const core = sign ? t.slice(1) : t;
|
||
|
||
if (/^\d+$/.test(core)) {
|
||
const secs = Number(core);
|
||
return { seconds: sign ? sign * secs : secs, relative: Boolean(sign) };
|
||
}
|
||
|
||
if (/^\d{1,2}:\d{1,2}(:\d{1,2})?$/.test(core)) {
|
||
const parts = core.split(":").map(Number);
|
||
let secs = 0;
|
||
if (parts.length === 3) {
|
||
const [hh, mm, ss] = parts;
|
||
secs = hh * 3600 + mm * 60 + ss;
|
||
} else {
|
||
const [mm, ss] = parts;
|
||
secs = mm * 60 + ss;
|
||
}
|
||
return { seconds: sign ? sign * secs : secs, relative: Boolean(sign) };
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
function fmt(seconds) {
|
||
seconds = Math.max(0, Math.floor(seconds));
|
||
const h = Math.floor(seconds / 3600);
|
||
const m = Math.floor((seconds % 3600) / 60);
|
||
const s = seconds % 60;
|
||
return h > 0
|
||
? `${h}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`
|
||
: `${m}:${String(s).padStart(2, "0")}`;
|
||
}
|
||
|
||
module.exports = {
|
||
data: new SlashCommandBuilder()
|
||
.setName("seek")
|
||
.setDescription("Seek to a timestamp or jump by seconds.")
|
||
.addStringOption((opt) =>
|
||
opt
|
||
.setName("to")
|
||
.setDescription("e.g. 90, 1:30, 01:02:03, +30, -10")
|
||
.setRequired(true)
|
||
),
|
||
category: "Music",
|
||
|
||
async execute(interaction, client) {
|
||
try {
|
||
await interaction.deferReply();
|
||
|
||
requireVC(interaction);
|
||
const queue = requireQueue(client, interaction);
|
||
|
||
const song = queue.songs?.[0];
|
||
if (!song || !Number.isFinite(song.duration) || song.isLive) {
|
||
throw new Error("❌ This stream/track doesn’t support seeking.");
|
||
}
|
||
|
||
const input = interaction.options.getString("to", true);
|
||
const parsed = parseTime(input);
|
||
if (!parsed) {
|
||
throw new Error(
|
||
"❌ Invalid time. Use `90`, `1:30`, `01:02:03`, `+30`, or `-10`."
|
||
);
|
||
}
|
||
|
||
const current = Math.floor(queue.currentTime ?? 0);
|
||
const duration = Math.floor(song.duration);
|
||
|
||
let target = parsed.relative ? current + parsed.seconds : parsed.seconds;
|
||
target = Math.max(0, Math.min(duration - 1, Math.floor(target)));
|
||
|
||
await queue.seek(target);
|
||
|
||
try {
|
||
const live = require("../../utils/liveLyricsManager");
|
||
live.seek(queue, target);
|
||
} catch {}
|
||
|
||
await interaction.followUp(
|
||
`⏭️ Seeked to **${fmt(target)}** (track length \`${fmt(duration)}\`).`
|
||
);
|
||
} catch (e) {
|
||
const msg = e?.message ?? "❌ Failed to seek.";
|
||
if (interaction.deferred || interaction.replied) {
|
||
await interaction.followUp({ content: msg, ephemeral: true });
|
||
} else {
|
||
await interaction.reply({ content: msg, ephemeral: true });
|
||
}
|
||
}
|
||
},
|
||
};
|