Skip to content

Commit 3bc3f7d

Browse files
committed
some more changes
1 parent 58fbaca commit 3bc3f7d

File tree

10 files changed

+220
-37
lines changed

10 files changed

+220
-37
lines changed

.eslintrc

+2-1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
"space-unary-ops": "error",
9595
"spaced-comment": "error",
9696
"yoda": "error",
97-
"no-case-declarations" : "off"
97+
"no-case-declarations" : "off",
98+
"no-unsafe-option-chaining": "off"
9899
}
99100
}

src/commands/music/play.js

+10-11
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,14 @@ module.exports = class Play extends Command {
5353
* @param {string[] | CommandInteractionOptionResolver} args
5454
*/
5555
async run(ctx, args) {
56+
if (ctx.isInteraction) {
57+
await ctx.sendDeferMessage();
58+
await ctx.deleteMessage();
59+
}
5660
if (!args.length)
5761
return await ctx.channel.send({
5862
embeds: [
59-
this.client
60-
.embed()
63+
this.client.embed()
6164
.setDescription('Please provide an URL or search query'),
6265
],
6366
});
@@ -67,12 +70,8 @@ module.exports = class Play extends Command {
6770
*/
6871
const query = args.length > 1 ? args.join(' ') : args[0];
6972
const isURL = this.checkURL(query);
70-
const result = await this.client.manager.search(isURL ? query : `ytsearch:${query}`);
71-
const dispatcher = await this.client.manager.spawn(
72-
ctx.guild,
73-
ctx.member,
74-
ctx.channel,
75-
);
73+
const dispatcher = await this.client.manager.spawn(ctx.guild, ctx.member, ctx.channel);
74+
const result = await this.client.manager.search(isURL ? query : `ytsearch:${query}`, ctx.guild.id);
7675
const embed = this.client.embed();
7776
const row = this.client.row;
7877

@@ -264,6 +263,6 @@ module.exports = class Play extends Command {
264263
},
265264
],
266265
}).catch((err) => this.client.logger.error(err));
267-
});
268-
}
269-
};
266+
});
267+
}
268+
};

src/config.js

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
module.exports = {
2-
token: "",//Put the token
3-
clientId: "",//client id
4-
prefix: ".",// Input Of prefix
5-
owners: ["owner id"], //place owner ids in array
6-
color: "BLURPLE", // You can place any color you want to
7-
database: "",//mongodb link
8-
}
2+
token: 'your bot token', // Put the token
3+
clientId: 'your bot id', // client id
4+
prefix: '.', // Input Of prefix
5+
owners: ['your id'], // place owner ids in array
6+
color: 'BLURPLE', // You can place any color you want to
7+
database: 'mongodb url', // mongodb link
8+
nodes: [
9+
{
10+
name: 'Alpha Lavalink', // any name can be given
11+
url: 'lavalink url:lavalink port', // lavalink host and port in this format host:port, ex:- alpha.xyz:6969
12+
auth: 'lavalink pass', // the password for your lavalink server
13+
secure: false, // if ssl set it to true
14+
},
15+
],
16+
};

src/events/player/trackEnd.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const Dispatcher = require('@root/src/structures/Dispatcher');
2+
const Event = require('@structures/Event');
3+
const { TextChannel } = require('discord.js');
4+
const { Player } = require('shoukaku');
5+
6+
module.exports = class TrackEnd extends Event {
7+
constructor(...args) {
8+
super(...args);
9+
}
10+
/**
11+
*
12+
* @param {Player} player
13+
* @param {import('shoukaku').Track} track
14+
* @param {TextChannel} channel
15+
* @param {Dispatcher} dispatcher
16+
*/
17+
async run(player, track, channel, dispatcher) {
18+
if (dispatcher.loop === 'repeat') dispatcher.queue.unshift(track);
19+
if (dispatcher.loop === 'queue') dispatcher.queue.push(track);
20+
for (let i = 0; i < dispatcher.matchedTracks.length; i++) {
21+
dispatcher.matchedTracks.pop();
22+
}
23+
await channel.send({ embeds: [this.client.embed().setDescription('No more tracks have been added in queue so i left the voice channel')] });
24+
dispatcher.play();
25+
}
26+
};

src/events/player/trackStart.js

+131-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
const { formatDuration } = require('@root/src/utils/function');
1+
const Dispatcher = require('@root/src/structures/Dispatcher');
2+
const { formatDuration, shuffle } = require('@root/src/utils/function');
23
const Event = require('@structures/Event');
3-
const { TextChannel, AttachmentBuilder } = require('discord.js');
4+
const { TextChannel, AttachmentBuilder, ButtonStyle, Message, User, SelectMenuComponent, ButtonComponent, ActionRow } = require('discord.js');
45
const { Player } = require('shoukaku');
56

67
module.exports = class TrackStart extends Event {
@@ -12,21 +13,142 @@ module.exports = class TrackStart extends Event {
1213
* @param {Player} player
1314
* @param {import('shoukaku').Track} track
1415
* @param {TextChannel} channel
16+
* @param {import('shoukaku').Track[]} matchedTracks
17+
* @param {Dispatcher} dispatcher
1518
*/
16-
async run(player, track, channel) {
17-
const embed = this.client.embed()
18-
.setTitle('Now Playing')
19-
.setDescription(`${track.info.title} - [\`${formatDuration(track.info.length, true)}\`]`);
19+
async run(player, track, channel, matchedTracks, dispatcher) {
20+
const embed = this.client
21+
.embed()
22+
.setTitle('Now Playing')
23+
.setDescription(
24+
`${track.info.title} - [\`${formatDuration(track.info.length, true)}\`]`,
25+
);
26+
27+
const matchedResults = matchedTracks.filter((v) => v.info.uri !== track.info.uri).map((v) => {
28+
return {
29+
label: v.info.title,
30+
value: v.info.uri,
31+
};
32+
});
2033

2134
const image = `https://img.youtube.com/vi/${track.info.identifier}/hqdefault.jpg`;
2235

23-
const buffer = await this.client.canvas.buildPlayerCard(image, track.info.author, track.info.title, track.info.length, player.position);
36+
const buffer = await this.client.canvas.buildPlayerCard(
37+
image,
38+
track.info.author,
39+
track.info.title,
40+
track.info.length,
41+
player.position,
42+
);
43+
44+
const attachment = new AttachmentBuilder(buffer, {
45+
name: 'nowplaying.png',
46+
});
47+
48+
const buttonsRow = this.client.row()
49+
.addComponents([
50+
this.client.button()
51+
.setEmoji({ name: '🔀' })
52+
.setCustomId('shuffle')
53+
.setStyle(ButtonStyle.Secondary),
54+
this.client.button()
55+
.setEmoji({ name: '◀️' })
56+
.setCustomId('backward')
57+
.setStyle(ButtonStyle.Secondary),
58+
this.client.button()
59+
.setEmoji({ name: '⏯️' })
60+
.setCustomId('pause')
61+
.setStyle(ButtonStyle.Secondary),
62+
this.client.button()
63+
.setEmoji({ name: '▶️' })
64+
.setCustomId('forward')
65+
.setStyle(ButtonStyle.Secondary),
66+
this.client.button()
67+
.setEmoji({ name: '🔁' })
68+
.setCustomId('repeat')
69+
.setStyle(ButtonStyle.Secondary),
70+
]);
2471

25-
const attachment = new AttachmentBuilder(buffer, { name: 'nowplaying.png' });
72+
const menuRow = this.client.row()
73+
.addComponents([
74+
this.client.menu()
75+
.setCustomId('results')
76+
.setPlaceholder('Play Similar songs')
77+
.setMaxValues(1)
78+
.setMinValues(1)
79+
.addOptions(matchedResults),
80+
]);
2681

27-
await channel.send({
82+
const message = await channel.send({
2883
embeds: [embed.setImage('attachment://nowplaying.png')],
2984
files: [attachment],
85+
components: [menuRow, buttonsRow],
86+
});
87+
88+
this.interacte(message, dispatcher.requester, dispatcher, matchedTracks, player, track);
89+
}
90+
91+
/**
92+
*
93+
* @param {Message} msg
94+
* @param {User} author
95+
* @param {Dispatcher} dispatcher
96+
* @param {import('shoukaku').Track[]} results
97+
* @param {Player} player
98+
* @param {import('shoukaku').Track} current
99+
*/
100+
interacte(msg, author, dispatcher, results, player, current) {
101+
const collector = msg.createMessageComponentCollector({
102+
time: current.info.length,
103+
filter: (m) => m.user.id === author.id,
104+
});
105+
106+
collector.on('collect', async (i) => {
107+
if (i.user.id !== author.id)
108+
return await i.reply({
109+
content: `Only **${author.tag}** can operate this buttons, Request one for yourselves.`,
110+
});
111+
112+
switch (i.customId) {
113+
case 'results':
114+
const filtered = results.filter((v) => v.info.uri === i.values[0])[0];
115+
dispatcher.queue.push(filtered);
116+
await i.reply({ embeds: [this.client.embed().setDescription(`Added **${filtered.info.title}** to queue.`).setTitle('Music Queue')], ephemeral: true });
117+
break;
118+
case 'shuffle':
119+
await i.deferUpdate();
120+
shuffle(dispatcher.queue);
121+
break;
122+
case 'backward':
123+
await i.deferUpdate();
124+
player.seekTo(0 * 1000);
125+
break;
126+
case 'forward':
127+
await i.deferUpdate();
128+
player.stopTrack();
129+
break;
130+
case 'pause':
131+
await i.deferUpdate();
132+
player.setPaused(player.paused ? false : true);
133+
break;
134+
case 'repeat':
135+
await i.deferUpdate();
136+
switch (dispatcher.loop) {
137+
case 'repeat':
138+
dispatcher.loop = 'queue';
139+
break;
140+
case 'queue':
141+
dispatcher.loop = 'off';
142+
break;
143+
case 'off':
144+
dispatcher.loop = 'repeat';
145+
break;
146+
}
147+
}
148+
});
149+
150+
collector.on('end', async () => {
151+
await msg.delete();
30152
});
31153
}
32154
};

src/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ signale.config({
1111

1212
const manager = new Manager('./src/moe.js', {
1313
mode: 'process',
14-
shardsPerClusters: 4,
14+
shardsPerClusters: 0,
1515
token,
1616
totalClusters: 'auto',
1717
totalShards: 'auto',

src/structures/Client.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ module.exports = class Alpha extends Client {
7575
* @param {import('discord.js').APISelectMenuComponent} data
7676
* @returns {SelectMenuBuilder}
7777
*/
78-
manu(data) {
78+
menu(data) {
7979
return new SelectMenuBuilder(data);
8080
}
8181
/**

src/structures/Dispatcher.js

+14-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { Guild, TextChannel } = require('discord.js');
1+
const { Guild, TextChannel, User } = require('discord.js');
22
const Moe = require('@structures/Client');
33
const { Player } = require('shoukaku');
44
const { EventEmitter } = require('events');
@@ -10,8 +10,9 @@ class Dispatcher extends EventEmitter {
1010
* @param {Guild} guild
1111
* @param {TextChannel} channel
1212
* @param {Player} player
13+
* @param {User} user
1314
*/
14-
constructor(client, guild, channel, player) {
15+
constructor(client, guild, channel, player, user) {
1516
super();
1617
/**
1718
* @type {Moe}
@@ -45,13 +46,21 @@ class Dispatcher extends EventEmitter {
4546
* @type {'off'|'repeat'|'queue'}
4647
*/
4748
this.loop = 'off';
49+
/**
50+
* @type {import('shoukaku').Track[]}
51+
*/
52+
this.matchedTracks = [];
53+
/**
54+
* @type {User}
55+
*/
56+
this.requester = user;
4857

4958
this.player
5059
.on('start', (data) =>
51-
this.manager.emit('trackStart', this.player, this.current, this.channel, data),
60+
this.manager.emit('trackStart', this.player, this.current, this.channel, this.matchedTracks, this, data),
5261
)
5362
.on('end', (data) => {
54-
this.manager.emit('trackEnd', this.player, this.current, data);
63+
this.manager.emit('trackEnd', this.player, this.current, this.channel, this, data);
5564
if (!this.queue.length) this.manager.emit('queueEnd', this.player, data);
5665
})
5766
.on('stuck', (data) =>
@@ -99,7 +108,7 @@ class Dispatcher extends EventEmitter {
99108
return;
100109
}
101110
this.current = this.queue.shift();
102-
this.player.playTrack({ track: this.current.track });
111+
this.player.playTrack({ track: this.current?.track });
103112
}
104113

105114
destroy() {

src/structures/Manager.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class Manager extends EventEmitter {
8989
deaf: true,
9090
});
9191

92-
const dispatcher = new Dispatcher(this.client, guild, channel, player);
92+
const dispatcher = new Dispatcher(this.client, guild, channel, player, member.user);
9393

9494
this.emit('playerCreate', dispatcher.player);
9595

@@ -103,7 +103,7 @@ class Manager extends EventEmitter {
103103
* @param {string} query
104104
* @returns {Promise<import('shoukaku').LavalinkResponse>}
105105
*/
106-
async search(query) {
106+
async search(query, guildId) {
107107
const node = await this.shoukaku.getNode();
108108

109109
/**
@@ -112,6 +112,11 @@ class Manager extends EventEmitter {
112112
let result;
113113
try {
114114
result = await node.rest.resolve(query);
115+
const player = this.getPlayer(guildId);
116+
if (!player) return;
117+
for (const track of result.tracks.slice(0, 11)) {
118+
player.matchedTracks.push(track);
119+
}
115120
} catch (err) {
116121
return null;
117122
}

src/utils/function.js

+13
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,17 @@ module.exports = {
8181
}
8282
return time;
8383
},
84+
shuffle: function(array) {
85+
let currentIndex = array.length, randomIndex;
86+
87+
while (currentIndex != 0) {
88+
randomIndex = Math.floor(Math.random() * currentIndex);
89+
currentIndex--;
90+
91+
[array[currentIndex], array[randomIndex]] = [
92+
array[randomIndex], array[currentIndex]];
93+
}
94+
95+
return array;
96+
},
8497
};

0 commit comments

Comments
 (0)