all repos — simple-discord-music-bot @ f5ac8b7e1de04b34091806153194cabdf9c24c6e

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

better indentation, bug fix, code cleanup
Marco Andronaco andronacomarco@gmail.com
Tue, 14 Feb 2023 00:17:08 +0100
commit

f5ac8b7e1de04b34091806153194cabdf9c24c6e

parent

394cdf4f8bb25b2fdc6eb4a21d67aa1b4bff9db8

M .eslintrc.json.eslintrc.json

@@ -1,49 +1,48 @@

{ - "extends": "eslint:recommended", - "env": { - "node": true, - "es6": true - }, - "parserOptions": { - "ecmaVersion": 2021 - }, - "rules": { + "extends": "eslint:recommended", + "env": { + "node": true, + "es6": true + }, + "parserOptions": { + "ecmaVersion": 2021 + }, + "rules": { "arrow-spacing": ["warn", { "before": true, "after": true }], - "brace-style": ["warn", "stroustrup", { "allowSingleLine": true }], - "comma-dangle": ["error", "always-multiline"], - "comma-spacing": "error", - "comma-style": "error", - "curly": [0, "multi-line", "consistent"], - "dot-location": ["error", "property"], - "handle-callback-err": "off", - "indent": ["error", "tab"], - "keyword-spacing": "error", - "max-nested-callbacks": ["error", { "max": 4 }], - "max-statements-per-line": ["error", { "max": 2 }], - "no-console": "off", - "no-empty-function": "error", - "no-floating-decimal": "error", - "no-inline-comments": 0, - "no-lonely-if": "error", - "no-multi-spaces": "error", - "no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }], - "no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }], - "no-trailing-spaces": ["error"], - "no-var": "error", - "object-curly-spacing": ["error", "always"], - "prefer-const": "error", - "quotes": ["error", "single"], - "semi": ["error", "always"], - "space-before-blocks": "error", - "space-before-function-paren": ["error", { - "anonymous": "never", - "named": "never", - "asyncArrow": "always" - }], - "space-in-parens": "error", - "space-infix-ops": "error", - "space-unary-ops": "error", - "spaced-comment": "error", - "yoda": "error" - } + "brace-style": ["warn", "stroustrup", { "allowSingleLine": true }], + "comma-dangle": ["error", "always-multiline"], + "comma-spacing": "error", + "comma-style": "error", + "curly": [0, "multi-line", "consistent"], + "dot-location": ["error", "property"], + "handle-callback-err": "off", + "keyword-spacing": "error", + "max-nested-callbacks": ["error", { "max": 4 }], + "max-statements-per-line": ["error", { "max": 2 }], + "no-console": "off", + "no-empty-function": "error", + "no-floating-decimal": "error", + "no-inline-comments": 0, + "no-lonely-if": "error", + "no-multi-spaces": "error", + "no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }], + "no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }], + "no-trailing-spaces": ["error"], + "no-var": "error", + "object-curly-spacing": ["error", "always"], + "prefer-const": "error", + "quotes": ["error", "single"], + "semi": ["error", "always"], + "space-before-blocks": "error", + "space-before-function-paren": ["error", { + "anonymous": "never", + "named": "never", + "asyncArrow": "always" + }], + "space-in-parens": "error", + "space-infix-ops": "error", + "space-unary-ops": "error", + "spaced-comment": "error", + "yoda": "error" + } }
M commands/outro.jscommands/outro.js

@@ -1,49 +1,45 @@

const { SlashCommandBuilder } = require('discord.js'); -const { playUrl, getChannel } = require("../functions/music"); +const { playUrl, getChannel } = require('../functions/music'); const outros = [ - { name: 'Random!', value: 'random' }, - { name: 'TheFatRat - Xenogenesis', value: 'https://www.youtube.com/watch?v=6N8zvi1VNSc' }, - { name: 'OMFG - Hello', value: 'https://www.youtube.com/watch?v=5nYVNTX0Ib8' }, - { name: 'Pegboard Nerds - Disconnected', value: 'https://www.youtube.com/watch?v=YdBtx8qG68w' }, - { name: 'Gym Class Heroes - Stereo Hearts', value: 'https://www.youtube.com/watch?v=ThctmvQ3NGk' }, + { name: 'Random!', value: 'random' }, + { name: 'TheFatRat - Xenogenesis', value: 'https://www.youtube.com/watch?v=6N8zvi1VNSc' }, + { name: 'OMFG - Hello', value: 'https://www.youtube.com/watch?v=5nYVNTX0Ib8' }, + { name: 'Pegboard Nerds - Disconnected', value: 'https://www.youtube.com/watch?v=YdBtx8qG68w' }, + { name: 'Gym Class Heroes - Stereo Hearts', value: 'https://www.youtube.com/watch?v=ThctmvQ3NGk' }, ]; module.exports = { - data: new SlashCommandBuilder() - .setName('outro') - .setDescription('Leave with an outro.') - .addStringOption(option => - option.setName('which') - .setDescription('Select which outro to play') - .setRequired(false) - .addChoices(...outros)) - .addStringOption(option => - option.setName('kick') - .setDescription('Do you actually want to log off?') - .setRequired(false) - .addChoices({ name: 'Yes', value: 'true' }, { name: 'No', value: 'false' })), - - async execute(interaction) { - - channel = await getChannel(interaction); - if (typeof channel == "string") - return await interaction.reply({ content: channel, ephemeral: true }); - - const outro = interaction.options.getString('which'); - const kick = interaction.options.getString('kick'); + data: new SlashCommandBuilder() + .setName('outro') + .setDescription('Leave with an outro.') + .addStringOption(option => + option.setName('which') + .setDescription('Select which outro to play') + .setRequired(false) + .addChoices(...outros)) + .addStringOption(option => + option.setName('kick') + .setDescription('Do you actually want to log off?') + .setRequired(false) + .addChoices({ name: 'Yes', value: 'true' }, { name: 'No', value: 'false' })), - let outro_file = outro ? outro : 'random'; - if (outro_file == 'random') - outro_file = outros[Math.floor(Math.random() * (outros.length - 1)) + 1].value; + async execute(interaction) { + const channel = await getChannel(interaction); + if (typeof channel == 'string') + return await interaction.reply({ content: channel, ephemeral: true }); - await playUrl(outro_file, channel); + const outro = interaction.options.getString('which'); + const kick = interaction.options.getString('kick'); + const randomIndex = Math.floor(Math.random() * (outros.length - 1)) + 1; + const outro_file = outro ? outro : outros[randomIndex].value; + await playUrl(outro_file, channel); - const kick_switch = kick ? kick : 'true'; - if (kick_switch == 'true') { - setTimeout(() => interaction.member.voice.disconnect(), 20_000); - return await interaction.reply({ content: `Prepare for takeoff!`, ephemeral: true }); - } - return await interaction.reply({ content: `Playing outro.`, ephemeral: true }); - }, + const kick_switch = kick ? kick : 'true'; + if (kick_switch == 'true') { + setTimeout(() => interaction.member.voice.disconnect(), 20_000); + return await interaction.reply({ content: 'Prepare for takeoff!', ephemeral: true }); + } + return await interaction.reply({ content: 'Playing outro.', ephemeral: true }); + }, };
M commands/play.jscommands/play.js

@@ -1,50 +1,48 @@

-const { SlashCommandBuilder } = require("discord.js"); +const { SlashCommandBuilder } = require('discord.js'); const play = require('play-dl'); -const { playUrl, getChannel } = require("../functions/music"); +const { playUrl, getChannel } = require('../functions/music'); -//const reg = /^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube(-nocookie)?\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$/; +// const reg = /^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube(-nocookie)?\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$/; module.exports = { - data: new SlashCommandBuilder() - .setName("play") - .setDescription("Play something off YouTube.") - .addStringOption((option) => option - .setName("query") - .setDescription("YouTube URL or search query") - .setRequired(true) - ), + data: new SlashCommandBuilder() + .setName('play') + .setDescription('Play something off YouTube.') + .addStringOption((option) => option + .setName('query') + .setDescription('YouTube URL or search query') + .setRequired(true), + ), - async execute(interaction) { - - channel = await getChannel(interaction); - if (typeof channel == "string") - return await interaction.reply({ content: channel, ephemeral: true }); + async execute(interaction) { + const channel = await getChannel(interaction); + if (typeof channel == 'string') + return await interaction.reply({ content: channel, ephemeral: true }); - await interaction.deferReply(); + await interaction.deferReply(); - // Get the YouTube URL or search query - let url = interaction.options.getString("query"); + // Get the YouTube URL or search query + const url = interaction.options.getString('query'); - let video; - switch (play.yt_validate(url)) { - case "video": - video = {url: url}; - break; - - case "search": - yt_info = await play.search(url, { source : { youtube : "video" }, limit: 1 }); + let video, yt_info; + switch (play.yt_validate(url)) { + case 'video': + video = { url: url }; + break; + + case 'search': + yt_info = await play.search(url, { source: { youtube: 'video' }, limit: 1 }); - if(yt_info.length === 0) - return await interaction.editReply(`No results found.`); + if (yt_info.length === 0) + return await interaction.editReply('No results found.'); - video = yt_info[0]; - break; - - default: - return await interaction.editReply(`Not supported.`); - } + video = yt_info[0]; + break; - playUrl(video.url, channel); - return await interaction.editReply(`Playing ${video.url}`); - }, + default: + return await interaction.editReply('Not supported.'); + } + playUrl(video.url, channel); + return await interaction.editReply(`Playing ${video.url}`); + }, };
M commands/stop.jscommands/stop.js

@@ -1,18 +1,17 @@

-const { SlashCommandBuilder } = require("discord.js"); -const { getChannel } = require("../functions/music"); +const { SlashCommandBuilder } = require('discord.js'); +const { getChannel } = require('../functions/music'); module.exports = { - data: new SlashCommandBuilder() - .setName("stop") - .setDescription("Stop the music."), + data: new SlashCommandBuilder() + .setName('stop') + .setDescription('Stop the music.'), - async execute(interaction) { - - channel = await getChannel(interaction); - if (typeof channel == "string") - return await interaction.reply({ content: channel, ephemeral: true }); + async execute(interaction) { + const channel = await getChannel(interaction); + if (typeof channel == 'string') + return await interaction.reply({ content: channel, ephemeral: true }); - player.stop(); - return await interaction.reply({ content: "Stopped.", ephemeral: true }); - }, + player.stop(); + return await interaction.reply({ content: 'Stopped.', ephemeral: true }); + }, };
M config.json.exampleconfig.json.example

@@ -1,4 +1,4 @@

{ - "applicationId": "your-client-id-here", - "token": "your-token-here" + "applicationId": "your-client-id-here", + "token": "your-token-here" }
M functions/music.jsfunctions/music.js

@@ -1,56 +1,42 @@

const { - createAudioResource, - createAudioPlayer, - joinVoiceChannel, - NoSubscriberBehavior, - AudioPlayerStatus, -} = require("@discordjs/voice"); + createAudioResource, + createAudioPlayer, + joinVoiceChannel, + AudioPlayerStatus, +} = require('@discordjs/voice'); const play = require('play-dl'); module.exports = { - async playUrl(url, channel) { - if(!channel){ - console.log("Channel error:", channel); - return; - } - let stream = await play.stream(url); - - // Connect to the user's voice channel - const guild = channel.guild; - - const connection = joinVoiceChannel({ - channelId: channel.id, - guildId: guild.id, - adapterCreator: guild.voiceAdapterCreator, - }); - - let resource = createAudioResource(stream.stream, { - inputType: stream.type - }) - - player = createAudioPlayer({ - behaviors: { noSubscriber: NoSubscriberBehavior.Play } - }); - - player.on(AudioPlayerStatus.Idle, () => { - player.stop(); - player.subscribers.forEach((element) => element.connection.disconnect()); - }); - - player.play(resource); - connection.subscribe(player); - }, - async getChannel(interaction) { + async playUrl(url, channel) { + if (!channel) { + console.log('Channel error:', channel); + return; + } + const stream = await play.stream(url); + const guild = channel.guild; + const connection = joinVoiceChannel({ + channelId: channel.id, + guildId: guild.id, + adapterCreator: guild.voiceAdapterCreator, + }); - const member = interaction.member; - if (!member) - return "Please use this in your current server."; + player = createAudioPlayer(); + player.on(AudioPlayerStatus.Idle, () => { + player.subscribers.forEach((element) => element.connection.disconnect()); + }); - const channel = member.voice.channel; + player.play(createAudioResource(stream.stream, { inputType: stream.type })); + connection.subscribe(player); + }, + async getChannel(interaction) { + const member = interaction.member; + if (!member) + return 'Please use this in your current server.'; - if (!channel) - return "You're not in a voice channel."; + const channel = member.voice.channel; + if (!channel) + return 'You\'re not in a voice channel.'; - return channel - } + return channel; + }, };
M index.jsindex.js

@@ -10,30 +10,30 @@ const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));

let player; for (const file of commandFiles) { - const filePath = path.join(commandsPath, file); - const command = require(filePath); - client.commands.set(command.data.name, command); + const filePath = path.join(commandsPath, file); + const command = require(filePath); + client.commands.set(command.data.name, command); } client.once('ready', () => { - client.user.setActivity("FOSS", { type: ActivityType.Competing }); - console.log('Bot online!'); + client.user.setActivity('FOSS', { type: ActivityType.Competing }); + console.log('Bot online!'); }); client.on('interactionCreate', async interaction => { - if (!interaction.isChatInputCommand()) return; - const command = client.commands.get(interaction.commandName); - if (!command) return; - try { - await command.execute(interaction); - } - catch (error) { - console.error(error); - await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true }); - } + if (!interaction.isChatInputCommand()) return; + const command = client.commands.get(interaction.commandName); + if (!command) return; + try { + await command.execute(interaction); + } + catch (error) { + console.error(error); + await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true }); + } }); if (!token) - throw 'Check your config.json!'; + throw 'Check your config.json!'; client.login(token);
M tools/delete-commands.jstools/delete-commands.js

@@ -4,9 +4,9 @@ const { REST } = require('@discordjs/rest');

const { applicationId, token } = require(path.join(process.cwd(), 'config.json')); if (!(applicationId && token)) - throw 'Check your config.json!'; + throw 'Check your config.json!'; const rest = new REST({ version: '10' }).setToken(token); rest.put(Routes.applicationCommands(applicationId), { body: [] }) - .then(() => console.log('Successfully deleted all application commands.')) - .catch(console.error); + .then(() => console.log('Successfully deleted all application commands.')) + .catch(console.error);
M tools/deploy-commands.jstools/deploy-commands.js

@@ -3,24 +3,24 @@ const path = require('node:path');

const { Routes } = require('discord.js'); const { REST } = require('@discordjs/rest'); const { applicationId, token } = require(path.join(process.cwd(), 'config.json')); -const permissions = 17825792 +const permissions = 17825792; if (!(applicationId && token)) - throw 'Check your config.json!'; + throw 'Check your config.json!'; const commands = new Array(); const commandsPath = path.join(process.cwd(), 'commands'); const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js')); for (const file of commandFiles) { - const filePath = path.join(commandsPath, file); - const command = require(filePath); - commands.push(command.data.toJSON()); + const filePath = path.join(commandsPath, file); + const command = require(filePath); + commands.push(command.data.toJSON()); } const rest = new REST({ version: '10' }).setToken(token); rest.put(Routes.applicationCommands(applicationId), { body: commands }) - .then((data) => console.log(` + .then((data) => console.log(` Successfully registered ${data.length} application commands. Bot invite link: https://discord.com/api/oauth2/authorize?client_id=${applicationId}&permissions=${permissions}&scope=bot`)) - .catch(console.error); + .catch(console.error);