Skip to content
This repository was archived by the owner on May 12, 2026. It is now read-only.

Commit f434c6c

Browse files
committed
(feat) add loop command, fix shuffle command minor bugs
1 parent 0c4b9cb commit f434c6c

5 files changed

Lines changed: 167 additions & 2 deletions

File tree

@types/DatabaseTypes.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export type Guilds = {
6060
register_join_message: string;
6161
colour_name_of_the_day: string;
6262
join_message: string;
63-
language: string;
63+
language: "en-UK" | "tr-TR";
6464
leave_message: string;
6565
mod_mail_message: string;
6666
bump_leaderboard_channel_id: bigint;

locales/en-GB/commands.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,5 +229,20 @@
229229
"skip_vote": "**{{user}}** started a vote to skip the song **{{track}}**. Users have to react with ✅ or ❌ to vote. {{count}} vote(s) needed to skip the song.",
230230
"skip_vote_success": "The song has been skipped.",
231231
"skip_vote_fail": "The song has not been skipped."
232+
},
233+
"shuffle": {
234+
"no_queue": "Currently, no song is playing.",
235+
"not_enough_tracks": "Not enough tracks in the queue to shuffle.",
236+
"not_in_same_voice": "You need to be in the same voice channel as the bot.",
237+
"shuffle_vote": "**{{user}}** started a vote to shuffle the queue. Users have to react with ✅ or ❌ to vote. {{count}} vote(s) needed to shuffle the queue.",
238+
"shuffle_vote_success": "The queue has been shuffled.",
239+
"shuffle_vote_fail": "The queue has not been shuffled."
240+
},
241+
"loop": {
242+
"no_queue": "Currently, no song is playing.",
243+
"not_in_same_voice": "You need to be in the same voice channel as the bot.",
244+
"loop_vote": "**{{user}}** started a vote to {{state}}. Users have to react with ✅ or ❌ to vote. {{count}} vote(s) needed to loop the song.",
245+
"loop_vote_success": "The song has been looped.",
246+
"loop_vote_fail": "The song has not been looped."
232247
}
233248
}

locales/tr-TR/commands.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,5 +229,20 @@
229229
"skip_vote": "**{{track}}**, **{{user}}** tarafından atlanmak için oylamaya sunuldu. Kullanıcılar ✅ veya ❌ ile oylamak zorundadır. Şarkıyı atlamak için {{count}} oya ihtiyaç var.",
230230
"skip_vote_success": "Şarkı atlandı.",
231231
"skip_vote_fail": "Oylama başarısız oldu."
232+
},
233+
"shuffle": {
234+
"no_queue": "Şu anda oynatılan bir müzik yok.",
235+
"not_enough_tracks": "Şarkı listesinin karıştırılması için yeterli şarkı yok.",
236+
"not_in_same_voice": "Bot ile aynı ses kanalında olmak zorundasın.",
237+
"shuffle_vote": "**{{user}}** tarafından şarkı sırası karıştırılmak için oylama başlatıldı. Kullanıcılar ✅ veya ❌ ile oylamak zorundadır. Şarkı listesini karıştırmak için {{count}} oya ihtiyaç var.",
238+
"shuffle_vote_success": "Şarkı listesi karıştırıldı.",
239+
"shuffle_vote_fail": "Oylama başarısız oldu."
240+
},
241+
"loop": {
242+
"no_queue": "Şu anda oynatılan bir müzik yok.",
243+
"not_in_same_voice": "Bot ile aynı ses kanalında olmak zorundasın.",
244+
"loop_vote": "**{{user}}** {{state}}. Kullanıcılar ✅ veya ❌ ile oylamak zorundadır. Şarkıyı döngüye almak için {{count}} oya ihtiyaç var.",
245+
"loop_vote_success": "Şarkı döngüye alındı.",
246+
"loop_vote_fail": "Oylama başarısız oldu."
232247
}
233248
}

src/slash_commands/music/loop.ts

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { SlashCommandBase } from "../../../@types/types";
2+
import { InteractionContextType, MessageFlags, PermissionsBitField, SlashCommandBuilder } from "discord.js";
3+
import { QueueRepeatMode, useQueue } from "discord-player";
4+
import { Guilds } from "../../../@types/DatabaseTypes";
5+
import { toStringId, vote } from "../../utils/utils";
6+
7+
export default {
8+
data: new SlashCommandBuilder()
9+
.setName("loop")
10+
.setNameLocalizations({
11+
tr: "döngü",
12+
})
13+
.setDescription("Loop the current track or queue.")
14+
.setDescriptionLocalizations({
15+
tr: "Şu anki parçayı veya sırayı döngüye al.",
16+
})
17+
.setContexts(InteractionContextType.Guild)
18+
.addNumberOption((option) =>
19+
option
20+
.setName("mode")
21+
.setNameLocalizations({
22+
tr: "mod",
23+
})
24+
.setDescription("Select a loop mode")
25+
.setDescriptionLocalizations({
26+
tr: "Bir döngü modu seçin",
27+
})
28+
.setRequired(true)
29+
.addChoices(
30+
{
31+
name: "OFF",
32+
value: QueueRepeatMode.OFF,
33+
name_localizations: {
34+
tr: "KAPAT",
35+
},
36+
},
37+
{
38+
name: "TRACK",
39+
value: QueueRepeatMode.TRACK,
40+
name_localizations: {
41+
tr: "PARÇA",
42+
},
43+
},
44+
{
45+
name: "QUEUE",
46+
value: QueueRepeatMode.QUEUE,
47+
name_localizations: {
48+
tr: "SIRA",
49+
},
50+
},
51+
),
52+
),
53+
async execute(interaction) {
54+
const { rows } = await interaction.client.pgClient.query<Guilds>("SELECT * FROM guilds WHERE id = $1", [
55+
interaction.guild.id,
56+
]);
57+
const guild_config = rows[0];
58+
if (!guild_config) {
59+
return interaction.reply({
60+
content: "This server is not registered in the database. This shouldn't happen, please contact developers",
61+
flags: MessageFlags.Ephemeral, // MessageFlags.Ephemeral
62+
});
63+
}
64+
const t = interaction.client.i18next.getFixedT(guild_config.language, "commands", "loop");
65+
const queue = useQueue(interaction.guild.id);
66+
if (!queue) {
67+
return interaction.reply({
68+
content: t("no_queue"),
69+
flags: MessageFlags.Ephemeral,
70+
});
71+
}
72+
if (interaction.member.voice.channel?.id !== interaction.guild.members.me!.voice.channel!.id) {
73+
return interaction.reply({
74+
content: t("not_in_same_voice"),
75+
flags: MessageFlags.Ephemeral,
76+
});
77+
}
78+
const loopMode = interaction.options.getNumber("mode", true) as 0 | 1 | 2;
79+
const state = {
80+
"en-UK": {
81+
[QueueRepeatMode.OFF]: "turn off the loop",
82+
[QueueRepeatMode.TRACK]: "loop the current track",
83+
[QueueRepeatMode.QUEUE]: "loop the entire queue",
84+
},
85+
"tr-TR": {
86+
[QueueRepeatMode.OFF]: "döngüyü kapatmak istiyor",
87+
[QueueRepeatMode.TRACK]: "şu anki parçayı döngüye almak istiyor",
88+
[QueueRepeatMode.QUEUE]: "tüm sırayı döngüye almak istiyor",
89+
},
90+
};
91+
const filter = interaction.member.voice.channel.members.filter(
92+
(member) =>
93+
!member.user.bot && !member.voice.selfDeaf && !member.voice.serverDeaf && member.id !== interaction.member.id,
94+
);
95+
if (
96+
filter.size > 0 &&
97+
(!interaction.member.roles.cache.has(toStringId(guild_config.dj_role_id)) ||
98+
!interaction.member.permissions.has(PermissionsBitField.Flags.Administrator))
99+
) {
100+
let requiredVotes: number;
101+
const totalUsers = filter.size;
102+
if (totalUsers <= 2) {
103+
// For 1 or 2 people, just 1 vote needed
104+
requiredVotes = 1;
105+
} else {
106+
// Otherwise, 60% rounded up
107+
requiredVotes = Math.ceil(totalUsers * 0.6);
108+
}
109+
const message = await interaction.reply({
110+
content: t("loop_vote", {
111+
user: interaction.user.toString(),
112+
count: requiredVotes,
113+
state: state[guild_config.language][loopMode],
114+
}),
115+
withResponse: true,
116+
});
117+
const result = await vote(interaction, filter, message.resource!.message!);
118+
if (result) {
119+
await interaction.followUp({
120+
content: t("loop_vote_success"),
121+
});
122+
queue.tracks.shuffle();
123+
} else {
124+
await interaction.followUp({
125+
content: t("loop_vote_fail"),
126+
});
127+
}
128+
} else {
129+
await interaction.reply({
130+
content: t("loop_vote_success"),
131+
flags: MessageFlags.Ephemeral,
132+
});
133+
queue.tracks.shuffle();
134+
}
135+
},
136+
} as SlashCommandBase;

src/slash_commands/music/shuffle.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ export default {
6767
const message = await interaction.reply({
6868
content: t("shuffle_vote", {
6969
user: interaction.user.toString(),
70-
track: queue.currentTrack?.title,
7170
count: requiredVotes,
7271
}),
7372
withResponse: true,

0 commit comments

Comments
 (0)