// This is the old version of the bot, written by DarkOK, and is kept here for archival purposes. // It no longer works due to discord api changes, hence the rewrite. const Discord = require('discord.js-v11'); // update this you lazy prick and rewrite the bot const client = new Discord.Client(); const {execSync, exec, spawnSync, spawn} = require('child_process'); var token = ""; // token used for authentication to discord var ownerIds = []; // user ids of people that can eval, change servers to non-whitelisted ones, etc var pf = "cvm!"; // command prefix var vncServers = [ "localhost:5900" ]; // servers that appears in the list, and ones that people don't need admin to switch to (first item in array is default server) // (if this is public, keep this as the one (Hm i should really make all VMs use one broadcast for all servers to fix this)) // dont modify var vncServer = vncServers[0]; var vncName = ""; // especially dont modify these var arecord; var broadcast; var bp={txt:"",type:"",status:""}; function changeStatus(txt, type, status) { bp.txt = txt; bp.type = type ? type : "LISTENING"; bp.status = status ? status : "online"; if (bp.txt != txt || bp.type != type || bp.status != status) { client.user.setPresence({ activity: { name: pf + "help" + " | " + bp.txt, type: bp.type }, status: bp.status }); } }; function startStream() { console.log("Connecting to " + vncServer); changeStatus("Connecting"); exec("./name " + vncServer, (error, stdout, stderr) => { if (error) { //vncName = "Failed to get VM name"; console.log("Error getting VM name: " + error.message); } else { vncName = stdout; console.log("VNC name: " + vncName); changeStatus(vncName); }; }); arecord = spawn('./audio', [vncServer]); arecord.stderr.setEncoding("utf8"); arecord.stderr.on("data", data => { console.log(data); }); arecord.on("close", code => { console.log("Audio process exited with " + code); setTimeout(()=>{ startStream(); for (var a of client.voice.connections.values()) { a.play(broadcast); } }, 2000); }); broadcast = client.voice.createBroadcast(); broadcast.play( arecord.stdout.on("data", data => { return data; }), { type: "converted", volume: false, highWaterMark: 1 } ); }; client.login(token); client.on("ready", ()=>{ console.log(`Logged in as: ${client.user.tag} (ID: ${client.user.id})`); console.log(`Prefix is: ${pf}`); console.log(`Currently in ${Array.from(client.guilds.cache.values()).length} servers\n`); startStream(); }); client.on("error", async err => { console.log("An error occurred: " + err.message); }); // HURR NO GOOD COMMAND HANDLER HURR DURR client.on("message", async message => { if (!message.guild) return; if (!message.member) return; if (message.member.id == client.user.id) return; // i could put it in the same one if i want to BUT I CANT BE BOTHERED var args = message.content.split(' '); var cmd = args.shift(); //console.log(`[Message] <${message.author.tag} in ${message.guild.name}> ${message.content}`); if (cmd.startsWith(pf)) { cmd = cmd.slice(pf.length).toLowerCase(); console.log(`[Command] <${message.author.tag} in #${message.channel.name}@${message.guild.name}> ${message.content}`); switch (cmd) { case "play": case "join": case "connect": if (message.member.voice.channel) { message.member.voice.channel.join().then(connection => { connection.play(broadcast); message.reply("joined"); }).catch(err =>{ message.reply("an error occurred while trying to join the voice channel: `" + err.message + '`'); console.log(err.message); }); } else { message.reply("join a voice channel"); }; break; case "stop": case "leave": case "disconnect": if (message.member.voice.channel) { message.member.voice.channel.leave(); message.reply("left"); } else { message.reply("join a voice channel so I know where to leave"); // i can probably make it not do it this way but Cannot Be Bothered }; break; case "eval": if (ownerIds.includes(message.member.id)) { try { //console.log("Evaluating " + args.join(' ')); var evalOutput = eval(args.join(' ')); //var evalOutputChunks = evalOutput.match(/.{1,1950}/g); message.reply("```\n" + evalOutput + "\n```"); //for (var i = 0; i < evalOutputChunks.length; i++) { // message.channel.send(`Part ${i + 1} of ${evalOutputChunks.length}` + "```\n" + evalOutputChunks[i] + "\n```"); //} console.log(evalOutput); } catch (err) { message.reply("fuck!\n```\n" + err.stack + "\n```"); } } else { message.reply("you need to be in the owner list"); }; break; case "rs": case "restartstream": if (ownerIds.includes(message.member.id)) { arecord.kill("SIGTERM"); console.log("Terminated the audio process"); message.reply("terminated the audio process"); } else { message.reply("you need to be in the owner list"); }; break; case "ss": case "setserver": // to do: maybe I could make it so each server can choose which VM in the list it plays audio from, instead of using the same broadcast // (although maybe still use broadcasts for multiple connections to the same server, for optimisation, and less connections to the same server) if (vncServers.includes(args[0]) || ownerIds.includes(message.member.id)) { if (args[0] != undefined) { vncServer = args[0]; arecord.kill("SIGTERM"); console.log("Set the VNC server to " + args[0] + " and terminated the audio process"); message.reply("set the VNC server to `" + args[0] + "` and terminated the audio process"); } else { message.reply("specify a server you dumb fuck"); }; } else { message.reply("you need to be in the owner list to connect to VNC servers not in the VNC server list (check `" + pf + "list`)"); // having the ability for anybody to connect to literally any host + tcp port maybe isn't a good idea }; break; case "cs": case "currentserver": if (ownerIds.includes(message.member.id)) { message.reply(`\nIP: \`${vncServer}\`\nName: \`${vncName}\``); } else { message.reply("you need to be in the owner list"); }; break; case "list": var vmlist = "VM List:\n"; vncServers.forEach(server => { var vmname = spawnSync("./name", [server], {timeout: 2000}); if (!vmname.status && vmname.stdout) { vmlist += `\`${server}\` - **${vmname.stdout}**\n`; } else { vmlist += `\`${server}\` - Failed to get name\n`; }; }); if (vmlist.length < 2000) message.reply(vmlist); else message.reply("VM list is too big to fit in a message (oops!)"); break; case "guilds": if (ownerIds.includes(message.member.id)) { let guildsOnEachPage = 10; let guildsTmp = Array.from(client.guilds.cache.values()); let pageCount = Math.ceil(guildsTmp.length / guildsOnEachPage); if (args[0] == "all") { for (var i = 1; i <= pageCount; ++i) { let guildList = `Guild list - __**Page ${i}/${pageCount}**__:\n`; guildsTmp.slice((i * guildsOnEachPage) - guildsOnEachPage, (i * guildsOnEachPage)).forEach(guild => { guildList += `**\`${guild.name}\`** - owned by ${guild.ownerID}, with ${guild.memberCount} users ${client.voice.connections.has(guild.id) ? `__**[Voice connected, ${Array.from(client.voice.connections.get(guild.id).channel.members).length} users]**__` : ""}\n`; // obese as shit holy FUCK }); if (guildList.length < 2000) message.channel.send(guildList); else message.reply("Guild list is too big to fit in a message (oops!)"); }; } else { let page = isNaN(parseInt(args[0])) ? 1 : parseInt(args[0]); let guildList = `Guild list - __**Page ${page}/${pageCount}**__:\n`; guildsTmp.slice((page * guildsOnEachPage) - guildsOnEachPage, (page * guildsOnEachPage)).forEach(guild => { guildList += `**\`${guild.name}\`** - owned by ${guild.ownerID}, with ${guild.memberCount} users ${client.voice.connections.has(guild.id) ? `__**[Voice connected, ${Array.from(client.voice.connections.get(guild.id).channel.members).length} users]**__` : ""}\n`; // obese as shit holy FUCK }); if (guildList.length < 2000) message.channel.send(guildList); else message.reply("Guild list is too big to fit in a message (oops!)"); }; } else { message.reply("you need to be in the owner list"); }; break; case "stats": let uptimeSeconds = (client.uptime / 1000); let botStats = "Bot stats:\n"; botStats += "**Server count**: " + Array.from(client.guilds.cache).length + '\n'; botStats += "**Voice connection count**: " + Array.from(client.voice.connections).length + '\n'; botStats += "**Bot uptime**: "; botStats += Math.floor(uptimeSeconds / 86400) + " days, "; uptimeSeconds %= 86400; botStats += Math.floor(uptimeSeconds / 3600) + " hrs, "; uptimeSeconds %= 3600; botStats += Math.floor(uptimeSeconds / 60) + " mins, "; botStats += Math.floor(uptimeSeconds % 60) + " secs\n"; botStats += "**Current VM name**: `" + vncName + "`\n"; if (botStats.length < 2000) message.reply(botStats); else message.reply("stats are too big to fit in a message (oops!)"); // i should probably calculate how big the mention part is too, but this somewhat works for now break; case "about": message.reply("this uses DarkOK's own QEMU_VNC_Audio->Discord bot"); break; case "invite": message.reply(`my invite URL is: https://discord.com/oauth2/authorize?client_id=${client.user.id}&permissions=36703232&scope=bot`); break; case "help": message.reply( "Command list:\n" + `**${pf}join** - Join a voice channel\n` + `**${pf}leave** - Leave a voice channel\n` + `**${pf}invite** - Sends the invite URL for the bot\n` + `**${pf}stats** - Display some statistics about the bot\n` + '\n' + "and that's about it\n" + "sometimes audio just stops working, try making the bot leave and rejoin the voice channel to see if it solves the issue\n" + "this bot is relatively unstable as of now, expect crashes/things not working\n\n" + "contact? dark@darkok.xyz" ); // really shitty but it's the best I can do since there's no proper command handler break; }; }; }); client.on("guildCreate", async guild => { console.log(`[Server Joined] ${guild.name} - ${guild.memberCount} members, ID: ${guild.id}`); }); client.on("guildDelete", async guild => { console.log(`[Server Left] ${guild.name} - ${guild.memberCount} members, ID: ${guild.id}`); }); process.on("unhandledRejection", async err => { console.error("Unhandled promise rejection:", err); });