all repos — simple-discord-music-bot @ 5fded05bd1b192407fad78e1406f688388b5f62e

A Discord bot making use of discord.js and play-yt.

major code cleanup
Marco Andronaco andronacomarco@gmail.com
Wed, 10 Jan 2024 12:47:07 +0100
commit

5fded05bd1b192407fad78e1406f688388b5f62e

parent

f1b6c2135d3740fb92fc2a8f095e159d93480a4a

M src/commands/clear.tssrc/commands/clear.ts

@@ -1,5 +1,5 @@

import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js'; -import * as music from '../functions/music'; +import { getChannel, queue } from '../functions/music'; module.exports = { data: new SlashCommandBuilder()

@@ -7,11 +7,11 @@ .setName('clear')

.setDescription('Clear the queue.'), async execute(interaction: ChatInputCommandInteraction) { - const channel = await music.getChannel(interaction); + const channel = await getChannel(interaction); if (typeof channel == 'string') return await interaction.reply({ content: channel, ephemeral: true }); - music.clearQueue() + queue.clear() return await interaction.reply({ content: 'Queue cleared.' }); }, };
M src/commands/outro.tssrc/commands/outro.ts

@@ -1,5 +1,5 @@

import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js'; -import { playOutro, getChannel } from '../functions/music'; +import { getChannel, queue } from '../functions/music'; import path from 'node:path'; const { outros } = require(path.join(process.cwd(), 'config.json'));

@@ -32,7 +32,7 @@

const outro = interaction.options.getString('which'); const kick = interaction.options.getString('kick'); const outroUrl = getOutroUrl(outro); - await playOutro(outroUrl, channel); + await queue.outro(outroUrl, channel); if (kick !== 'false') { const member = interaction.member;
M src/commands/play.tssrc/commands/play.ts

@@ -1,6 +1,6 @@

import { ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js'; import play, { YouTubeVideo } from 'play-dl'; -import { playUrls, getChannel, formatTitle } from '../functions/music'; +import { getChannel, formatTitle, queue } from '../functions/music'; async function handleUserInput(input: string): Promise<YouTubeVideo[]> { try {

@@ -42,7 +42,7 @@ await interaction.deferReply();

const opt = interaction.options; const input = opt.getString('query'); const yt_videos = await handleUserInput(input); - const added = await playUrls(yt_videos, channel); + const added = await queue.addArray(yt_videos, channel); switch (added.length) { case 0:
M src/commands/queue.tssrc/commands/queue.ts

@@ -1,5 +1,5 @@

import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js'; -import { formatTitle, getChannel, getQueue } from '../functions/music'; +import { formatTitle, getChannel, queue } from '../functions/music'; import { YouTubeVideo } from 'play-dl'; const CHARACTER_LIMIT_API = 2000;

@@ -36,7 +36,7 @@ const channel = await getChannel(interaction);

if (typeof channel == 'string') return await interaction.reply({ content: channel, ephemeral: true }); - const result = await getQueue(); + const result = queue.queue; if (result) { const reply = getReply(result); return await interaction.reply({ content: reply });
M src/commands/skip.tssrc/commands/skip.ts

@@ -1,5 +1,5 @@

import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js'; -import { getChannel, skipMusic } from '../functions/music'; +import { getChannel, queue } from '../functions/music'; module.exports = { data: new SlashCommandBuilder()

@@ -11,7 +11,7 @@ const channel = await getChannel(interaction);

if (typeof channel == 'string') return await interaction.reply({ content: channel, ephemeral: true }); - const result = await skipMusic(); + const result = await queue.next(); return await interaction.reply({ content: 'Skipped.' }); //return await interaction.reply({ content: 'Error: couldn\'t skip.', ephemeral: true }); },
M src/commands/stop.tssrc/commands/stop.ts

@@ -1,5 +1,5 @@

import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js'; -import { getChannel, stopMusic } from '../functions/music'; +import { getChannel, queue } from '../functions/music'; module.exports = { data: new SlashCommandBuilder()

@@ -11,7 +11,8 @@ const channel = await getChannel(interaction);

if (typeof channel == 'string') return await interaction.reply({ content: channel, ephemeral: true }); - if (stopMusic()) + const r = queue.stop(); + if (r) return await interaction.reply({ content: 'Stopped.', ephemeral: true }); return await interaction.reply({ content: 'Error.', ephemeral: true });
M src/functions/music.tssrc/functions/music.ts

@@ -1,53 +1,13 @@

-import { joinVoiceChannel } from '@discordjs/voice'; import { ChatInputCommandInteraction, VoiceBasedChannel } from 'discord.js' -import MyQueue from './myqueue'; import { YouTubeVideo } from 'play-dl'; +import MyQueue from './myqueue'; -const q = new MyQueue(); +export const queue = new MyQueue(); export function formatTitle(video: YouTubeVideo): string { return `**${video.title}** (\`${video.durationRaw}\`)` } -export function getChannelConnection(channel: VoiceBasedChannel) { - const guild = channel.guild; - return joinVoiceChannel({ - channelId: channel.id, - guildId: guild.id, - adapterCreator: guild.voiceAdapterCreator - }); -} - -export async function playUrls(videos: YouTubeVideo[], channel: VoiceBasedChannel): Promise<YouTubeVideo[]> { - if (!channel) { - console.log('Channel error:', channel); - return; - } - if (videos.length == 0) return []; - q.connection = getChannelConnection(channel); - return await q.addArray(videos); -} - -/* Only useful for radio -export async function playStream(url: string, channel: VoiceBasedChannel) { - if (!channel) { - console.log('Channel error:', channel); - return; - } - q.connection = getChannelConnection(channel); - q.add(createAudioResource(url, { inputType: StreamType.Opus })); -} -*/ - -export async function playOutro(url: string, channel: VoiceBasedChannel) { - if (!channel) { - console.log('Channel error:', channel); - return; - } - q.connection = getChannelConnection(channel); - q.outro(url); -} - export async function getChannel(interaction: ChatInputCommandInteraction): Promise<string | VoiceBasedChannel>{ const member = interaction.member; if (!member)

@@ -59,19 +19,3 @@ if (!("voice" in member)) return vc_error;

const channel: VoiceBasedChannel = member.voice.channel; return channel ? channel : vc_error; } - -export async function stopMusic() { - return q.stop(); -} - -export async function skipMusic() { - return q.next(); -} - -export async function getQueue() { - return q.queue; -} - -export function clearQueue() { - return q.clear(); -}
M src/functions/myqueue.tssrc/functions/myqueue.ts

@@ -1,6 +1,16 @@

-import { createAudioResource, createAudioPlayer, NoSubscriberBehavior, AudioPlayerStatus, VoiceConnection, AudioPlayer, AudioResource } from '@discordjs/voice'; +import { createAudioResource, createAudioPlayer, NoSubscriberBehavior, AudioPlayerStatus, VoiceConnection, AudioPlayer, AudioResource, joinVoiceChannel } from '@discordjs/voice'; +import { VoiceBasedChannel } from 'discord.js'; import play, { YouTubeVideo } from 'play-dl'; +export function getChannelConnection(channel: VoiceBasedChannel) { + const guild = channel.guild; + return joinVoiceChannel({ + channelId: channel.id, + guildId: guild.id, + adapterCreator: guild.voiceAdapterCreator + }); +} + async function resourceFromYTUrl(url: string): Promise<AudioResource<null>> { try { const stream = await play.stream(url);

@@ -16,7 +26,7 @@ #nowPlaying: YouTubeVideo;

#queue: Array<YouTubeVideo>; connection: VoiceConnection; player: AudioPlayer; - static _instance: MyQueue; + get queue() { if (this.#nowPlaying) return [this.#nowPlaying].concat(this.#queue);

@@ -24,11 +34,6 @@ return null

} constructor() { - if (MyQueue._instance) { - return MyQueue._instance - } - MyQueue._instance = this; - this.#nowPlaying = null; this.connection = null; this.#queue = Array<YouTubeVideo>();

@@ -74,11 +79,18 @@ this.#queue.splice(normalizedPosition, 0, video);

if (l == 0) this.next(); } - async addArray(urls: YouTubeVideo[]) { + async addArray(videos: YouTubeVideo[], channel: VoiceBasedChannel) { + if (!channel) { + console.log('Channel error:', channel); + return; + } + if (videos.length == 0) return []; + this.connection = getChannelConnection(channel); + const l = this.#queue.length; - this.#queue.push(...urls); + this.#queue.push(...videos); if (l == 0 && this.player.state.status == AudioPlayerStatus.Idle) this.next(); - return urls; + return videos; } pause() {

@@ -90,7 +102,13 @@ this.player.unpause();

this.connection.subscribe(this.player); } - async outro(url: string) { + async outro(url: string, channel: VoiceBasedChannel) { + if (!channel) { + console.log('Channel error:', channel); + return; + } + this.connection = getChannelConnection(channel); + this.player.pause(); const resource = await resourceFromYTUrl(url);