// Require the necessary discord.js classes const Discord = require('discord.js'); const Voice = require('@discordjs/voice'); const { token } = require('../config.json'); const { exec } = require('child_process'); const { promisify } = require('util'); const got = require('got'); const youtubedl = require('youtube-dl'); const cheerio = require('cheerio'); const bot = new Discord.Client({ intents: [ Discord.Intents.FLAGS.GUILDS, Discord.Intents.FLAGS.GUILD_MESSAGES, Discord.Intents.FLAGS.GUILD_VOICE_STATES, Discord.Intents.FLAGS.GUILD_MESSAGE_REACTIONS ], }); bot.once('ready', () => { console.log('Ready!'); }); const prefix = ';'; const embedColor = 0xa0ffa5; const foggyImg = 'https://cdn.discordapp.com/attachments/789023763396165633/898908687299657728/552bb191-93e3-4eaa-b935-d5031f3845e7.gif'; const randomTrendStrings = [ 'GUYS GUYS GUYS GUYS GUYS DID YOU know That {}', 'trending in russia today: {}', 'I Am Going To Tear Off Your {}', 'tghhe {} is feeling sussy today', 'i am going to devour a {}', '{},,,,,,,........ mmmmmhhmhmhh :yummy:', '{}..... no whay', 'Hot Singles Straight From [{}]', 'l', '{} =)', 'Guys guys checkl this out: {}', 'hehe ..... ... {}.... :)', 'SyntaxError: {}', '{} is veyr instantly', 'BITCOIN AT {} GO GO TO HTTPS://BITCOIN GO NOW for free ;;!!!!!', 'lol {}', 'the {}', 'GUYS today i accidentaly {}', 'jesus fuckign christ im going to actually fucking {}', '`{}`. :)' ]; async function postInRandomChannel(text) { const guild = await bot.guilds.fetch('587108210121506816'); if (!guild.available) return; const channels = await guild.channels.fetch(); const textChannels = channels.filter(c => c.parentId === '821064982439264308' && c.isText() && c.permissionsFor(bot.user.id).has(Discord.Permissions.FLAGS.SEND_MESSAGES)); if (textChannels.size === 0) return; textChannels.random().send(text); } async function getTrend() { try { let t = await got('https://trends24.in/russia/'); const $ = cheerio.load(t.body); const elems = $('.trend-card > .trend-card__list')[0]; const trends = elems.children.map(n => n.children[0].children[0].data); return trends[Math.floor(Math.random() * trends.length)]; } catch(err) { console.log(err); } } async function postRandomTrend() { const trend = await getTrend(); if (!trend) return; postInRandomChannel(randomTrendStrings[Math.floor(Math.random() * randomTrendStrings.length)].replace('{}', trend)); } function randomTimeout() { if (Math.random() < 0.2) { return Math.random() * 1000 * 60; // up to a minute } else { return 1000 * 60 * 60 * 12 + Math.random() * 1000 * 60 * 60 * 12; // from 12 to 24 hours } } function doTrendTimeout() { postRandomTrend(); setTimeout(doTrendTimeout, randomTimeout()); } setTimeout(doTrendTimeout, randomTimeout()); async function checkVoiceChannel(msg) { if (!msg.guild) return; const clientVoiceState = msg.guild.me.voice; const memberVoiceState = msg.member.voice; const voiceConnection = await Voice.getVoiceConnection(msg.guild.id); if (clientVoiceState.channelId === memberVoiceState.channelId && voiceConnection) { console.log('reusing connection'); return voiceConnection; } if (!memberVoiceState.channelId) { msg.channel.send('you aren\'t in a voice channel!'); return; } if (clientVoiceState.channelId && clientVoiceState.channelId !== memberVoiceState.channelId) { msg.channel.send('you are in a different voice channel!'); return; } console.log('creating new connection'); return Voice.joinVoiceChannel({ channelId: msg.member.voice.channelId, guildId: msg.guild.id, adapterCreator: msg.guild.voiceAdapterCreator, }); } /* { url: string, } */ let queue = {}; let players = {}; let playerStartTime = {}; function advanceQueue(id, channel, subscription, connection) { if (queue[id][1]) { const song = queue[id][1]; const embed = new Discord.MessageEmbed() .setDescription(`now playing: **${song.titleFormat}**`) .setThumbnail(song.thumbnail) .setColor(embedColor) .setAuthor('foggy ♫', foggyImg); channel.send({embeds: [embed]}); play(queue[id][1], id, connection); queue[id] = queue[id].slice(1); } else { channel.send('no songs left, leaving'); console.log('destroyig'); if (subscription) subscription.unsubscribe(); if (players[id]) delete players[id]; if (connection) connection.destroy(); queue[id] = []; } } async function play(song, id, connection, channel) { // const stream = ytdl(url, { filter: 'audioonly' }); let player; let subscription; let url = song.directUrl; if (song.redownload) { let err, out try { err, out = await promisify(exec)('youtube-dl -f bestaudio --youtube-skip-dash-manifest --force-ipv4 -g "' + song.originalUrl + '"'); } catch(err_) { try { err, out = await promisify(exec)('youtube-dl -f best --youtube-skip-dash-manifest --force-ipv4 -g "' + song.originalUrl + '"'); } catch(err_) { err = err_ } } if (err) { console.log(err); channel.send(`failed to retrieve youtube-dl info!: \`\`\`${err}\`\`\``); advanceQueue(id, channel, subscription, connection); return; } url = out.stdout; } try { const stream = got(url, {isStream: true}); const resource = Voice.createAudioResource(stream, { inputType: Voice.StreamType.Arbitrary }); stream.on('error', err => { console.log(err); channel.send(`failed to play track: \`\`\`${err}\`\`\``); advanceQueue(id, channel, subscription, connection); return; }); if (players[id]) { console.log('reusing player'); player = players[id]; } else { console.log('creating new player'); player = Voice.createAudioPlayer(); players[id] = player; subscription = connection.subscribe(player); player.on(Voice.AudioPlayerStatus.Idle, () => { advanceQueue(id, channel, subscription, connection); }); player.on(Voice.AudioPlayerStatus.Playing, () => { playerStartTime[id] = Date.now(); }) } player.play(resource); } catch(err) { console.log(err); channel.send(`failed to play track: \`\`\`${err}\`\`\``); advanceQueue(id, channel, subscription, connection); return; } return player; } function rawYTDLQueue(info) { let song = { originalUrl: info.webpage_url || url, directUrl: info.url, redownload: true, title: info.title, thumbnail: info.thumbnail, titleFormat: info.title, duration: info._duration_raw, fileType: info.acodec, bitrate: info.abr, }; if (song.track && song.artist && song.album) { song.titleFormat = `${song.artist} - [${song.track}](${info.webpage_url}) from ${song.album}`; } else { if (song.originalUrl.startsWith('http')) song.titleFormat = `[${song.title}](${song.originalUrl})`; } return song; } function formatTime(t) { return `${Math.floor(t / 60).toString().padStart(2, '0')}:${Math.floor(t % 60).toString().padStart(2, '0')}` } async function queueUp(url, id) { if (!queue[id]) queue[id] = []; let song = {}; let err, info; try { err, info = await promisify(youtubedl.getInfo)(url, ['--force-ipv4']); } catch(err_) { err = err_; } if (err) { return [null, `failed to retrieve youtube-dl info!: \`\`\`${err.toString().slice(0, 1000)}\`\`\``]; } else { if (info.length) { info.forEach(i => { let song = rawYTDLQueue(i); queue[id].push(song); }); return [info.length, null]; } else { song = rawYTDLQueue(info); } } queue[id].push(song) return [1, null]; } async function playOrQueue(qsong, channel, msg) { const connection = await checkVoiceChannel(msg); if (!connection) return; console.log(`queueing ${qsong}`); channel.send(`queueing <${qsong}>...`); let [q, e] = await queueUp(qsong, msg.guild.id); if (!q && e) [q, e] = await queueUp('ytsearch:' + qsong, msg.guild.id); if (!q && e) return msg.channel.send(e); const song = queue[msg.guild.id][queue[msg.guild.id].length - 1]; if (queue[msg.guild.id].length === 1) { const embed = new Discord.MessageEmbed() .setDescription(`now playing: **${song.titleFormat}**`) .setThumbnail(song.thumbnail) .setColor(embedColor) .setAuthor('foggy ♫', foggyImg); msg.channel.send({embeds: [embed]}); await play(queue[msg.guild.id][0], msg.guild.id, connection, msg.channel); } else { let queueString = `queued: **${song.titleFormat}** _at position ${queue[msg.guild.id].length - 1}_`; if (q > 1) queueString = `queued **${q} songs** _at positions ${queue[msg.guild.id].length - q} - ${queue[msg.guild.id].length - 1}_`; const embed = new Discord.MessageEmbed() .setDescription(queueString) .setColor(embedColor) .setAuthor('foggy ♫', foggyImg); msg.channel.send({embeds: [embed]}); if (!players[msg.guild.id] || players[msg.guild.id].state === Voice.AudioPlayerStatus.Idle || players[msg.guild.id].state === Voice.AudioPlayerStatus.Paused) { await play(queue[msg.guild.id][0], msg.guild.id, connection, msg.channel); } } } bot.on('messageCreate', async (msg) => { const content = msg.content; const params = content.replace(prefix, '').split(' '); const cmd = params[0]; if (!msg.guild || !content.startsWith(prefix) || !msg.channel) return; if ((cmd === 'playm' || cmd === 'playmany' || cmd === 'pm') && params[1]) { for (const p of params.slice(1)) { await playOrQueue(p, msg.channel, msg); } } else if (cmd === 'play' || cmd === 'p') { playOrQueue(params.slice(1).join(' '), msg.channel, msg); } else if (cmd === 'skip' || cmd === 's') { const player = players[msg.guild.id]; if (!player) return msg.channel.send('the bot isn\'t playing any music!'); const song = queue[msg.guild.id][0]; const embed = new Discord.MessageEmbed() .setDescription(`skipped **${song.titleFormat}**`) .setColor(embedColor) .setAuthor('foggy ♫', foggyImg); msg.channel.send({embeds: [embed]}); player.stop(); } else if (cmd === 'queue' || cmd === 'q') { const q = queue[msg.guild.id] || []; if (q.length === 0) { msg.channel.send('no songs queued!'); } else { msg.channel.send(`${q.length} track${q.length === 1 ? '' : 's'}\ntotal queue length: \`${formatTime(q.map(s => s.duration).reduce((p, c) => p || 0 + c || 0))}\`\n` + '```' + q.slice(0, 10).map((m, i) => `${i === 0 ? 'now playing:' : i + '.'} ${m.title} ${(m.duration !== 0) ? formatTime(m.duration) : ''}`).join('\n') + '```'); } } else if (cmd === 'np' || cmd === 'nowplaying') { const song = (queue[msg.guild.id] || [])[0]; if (!song) return msg.channel.send('no song playing!'); let progress = (Date.now() - (playerStartTime[msg.guild.id] || 0)) / 1000 / song.duration; let progressLength = 20; let progressStr = ''; if (song.duration === -1) { progressStr = '🔘 `where?..`'; } else if (progress < 0 || progress > 1) { progressStr = '🔘 `buffering,,`'; } else { progressStr = `${'▬'.repeat(Math.floor(Math.abs(progress) * progressLength))}🔘${'▬'.repeat(Math.floor((1 - Math.abs(progress)) * progressLength))}`; progressStr += `\n\`${formatTime((Date.now() - (playerStartTime[msg.guild.id] || 0)) / 1000)}\`/\`${formatTime(song.duration)}\`` } let embed = new Discord.MessageEmbed() .setDescription(`now playing: **${song.titleFormat}**\n${progressStr}`) .setFooter(`${song.fileType} • ${song.bitrate}KB/s`) .setThumbnail(song.thumbnail) .setColor(embedColor) .setAuthor('foggy ♫', foggyImg); if (queue[msg.guild.id].length !== 1) { embed = embed.addField('up next', '```' + queue[msg.guild.id].slice(1, 5).map((m, i) => { if (i === 3) { return '...'; } else { return `${i + 1}. ${m.title}`; } }).join('\n') + '```') } msg.channel.send({embeds: [embed]}); } }); // Login to Discord with your bot's token bot.login(token);