import fs from "fs"; import path from "path"; import { Client, GatewayIntentBits, Partials } from "discord.js"; const { TOKEN, GUILD_ID, COMMAND_CHANNEL_ID, ADDUP_ID, PICKING_ID, BLU_ID, RED_ID, FK_ID, CAPTAIN_ID, } = require("./config.json"); let whitelistStr = require("./config.json").RANKING_WHITELIST; const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildPresences, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.DirectMessages, GatewayIntentBits.MessageContent, ], partials: [Partials.Channel, Partials.Message], }); const backticks = "```"; const rankingsPath = path.join(__dirname, "rankings.json"); const matchString = (str, search) => { if (!str || !search) return false; return str.toLowerCase().includes(search.toLowerCase()); }; const findPlayer = (guild, searchName) => { // search display name let player = guild.members.cache.find((member) => matchString(member.displayName, searchName) ); if (!player) { // search global name player = guild.members.cache.find((member) => matchString(member.user.globalName, searchName) ); } if (!player) { // search username player = guild.members.cache.find((member) => matchString(member.user.username, searchName) ); } if (!player) { // match id player = guild.members.cache.find((member) => member.id === searchName); } return player; }; const getApplicableName = (player) => { return ( player.displayName || player.user.globalName || player.user.username || player.id ); }; client.on("ready", () => { console.log(`Logged in as ${client.user.tag}!`); console.log( `Deleting old messages from this bot in command channel: ${COMMAND_CHANNEL_ID}...` ); let channel = client.channels.cache.get(COMMAND_CHANNEL_ID); if (!channel) return console.error("(ready) Can't find command channel"); channel.messages .fetch({ limit: 100 }) .then((messages) => { messages.forEach((message) => { if (message.author.id === client.user.id) { message.delete(); } }); }) .catch(console.error); }); // send message on error client.on("error", (error) => { console.error(error); let channel = client.channels.cache.get(COMMAND_CHANNEL_ID); if (!channel) return console.error("Can't find command channel"); client.channels.cache .get(COMMAND_CHANNEL_ID) .send(`error: ${error.message}`) .catch(console.error); }); client.on("interactionCreate", async (interaction) => { const command = interaction.commandName; if (!interaction.isChatInputCommand()) return; if ( command === "scout" || command === "soldier" || command === "pyro" || command === "demoman" || command === "demo" || command === "heavy" || command === "engineer" || command === "engi" || command === "medic" || command === "sniper" || command === "spy" ) { // get voice channels const picking = interaction.guild.channels.cache.find( (channel) => channel.name === "picking" || channel.id === PICKING_ID ); if (!picking) return console.error("Can't find channel 'picking'!"); const captain = interaction.guild.channels.cache.find( (channel) => channel.name === "captains" || channel.id === CAPTAIN_ID ); if (!captain) return console.error("Can't find channel 'captains'!"); // make sure user is in picking or captain channel if ( !picking.members.has(interaction.user.id) && !captain.members.has(interaction.user.id) ) { await interaction.reply({ content: "must be in picking or captains channel to use this command", ephemeral: true, }); return; } else { await interaction.reply({ content: "checking picking channel...", ephemeral: true, }); // set role name let roleName = command; if (command === "demoman") roleName = "demo"; if (command === "engi") roleName = "engineer"; // check each member in picking channel for role let str = `In picking (${roleName}):`; for (const member of picking.members.values()) { if (member.roles.cache.find((role) => role.name === roleName)) { if (str !== `In picking (${roleName}):`) str += ","; str += " " + member.user.globalName; } } if (str === `In picking (${roleName}):`) str = `None found ¯\\_(ツ)_/¯ (${roleName})`; // respond return await interaction.followUp(str); } } if (interaction.channelId !== COMMAND_CHANNEL_ID) { await interaction.reply({ content: "wrong channel, or you lack the required permissions", ephemeral: true, }); return; } if (command === "hello") { await interaction.reply("world"); } if ( command === "topicking" || command === "end" || command === "resetteams" ) { await interaction.reply("moving members..."); // get voice channels const blu = interaction.guild.channels.cache.find( (channel) => channel.name === "blu" || channel.id === BLU_ID ); if (!blu) return console.error("Can't find channel 'blu'!"); const red = interaction.guild.channels.cache.find( (channel) => channel.name === "red" || channel.id === RED_ID ); if (!red) return console.error("Can't find channel 'red'!"); const addup = interaction.guild.channels.cache.find( (channel) => channel.name === "add-up" || channel.id === ADDUP_ID ); if (!addup) return console.error("Can't find channel 'add-up'!"); const picking = interaction.guild.channels.cache.find( (channel) => channel.name === "picking" || channel.id === PICKING_ID ); if (!picking) return console.error("Can't find channel 'picking'!"); // get members in voice channel let members = blu.members.concat(red.members); if (command !== "resetteams") members = members.concat(addup.members); if (members.size === 0) { return await interaction.followUp( `found no members in ${ command === "resetteams" ? "blu" : "addup, blu," } or red` ); } // move members to addup let idx = 0, eCount = 0; const moveToAddup = async (member) => { try { await member.voice.setChannel(picking); } catch (error) { console.error(error); eCount++; } }; const moveAllToAddup = async () => { return Promise.all( Array.from(members, async ([memberId, member]) => { await moveToAddup(member); }) ); }; moveAllToAddup().then(() => interaction.followUp( `moved members in ${ command === "resetteams" ? "blu" : "addup, blu," } and red${eCount > 0 ? ` (error moving ${eCount} members)` : ""}` ) ); } if (command === "fk" || command === "fatkid") { await interaction.reply("moving fatkids..."); // get voice channels const picking = interaction.guild.channels.cache.find( (channel) => channel.name === "picking" || channel.id === PICKING_ID ); if (!picking) return console.error("Can't find channel 'add-up'!"); const fk = interaction.guild.channels.cache.find( (channel) => channel.name === "fatkid" || channel.id === FK_ID ); if (!fk) return console.error("Can't find channel 'fatkid'!"); // get members in voice channel const members = picking.members; if (members.size === 0) { return await interaction.followUp("found no members in picking"); } let str = "", eCount = 0; const logFk = async (member) => { try { await member.voice.setChannel(fk); if (str.length > 0) str += ", "; str += member.user.globalName; } catch (error) { console.error(error); eCount++; } }; const logAllFks = async () => { return Promise.all( Array.from(members, async ([memberId, member]) => { await logFk(member); }) ); }; logAllFks().then(() => interaction.followUp( `fatkids: ${str}${ eCount > 0 ? ` (error moving ${eCount} members)` : "" }` ) ); } if (command === "clear" || command === "bclear") { await interaction.reply("clearing messages..."); let channel = client.channels.cache.get(COMMAND_CHANNEL_ID); if (!channel) return console.error("Can't find command channel"); channel.messages .fetch({ limit: 100 }) .then((messages) => { messages.forEach((message) => { if (message.author.id === client.user.id) { message.delete(); } }); }) .catch(console.error); } }); client.on("messageCreate", async (message) => { if (message.author.bot || message.guild) return; // check if user is whitelisted if (!whitelistStr.includes(message.author.id)) { return; } if (message.content.toLowerCase().includes("hello penguin")) { await message.reply(`hello ${message.author.username}`); return; } const pickupGuild = client.guilds.cache.get(GUILD_ID); if (!pickupGuild) { await message.reply("could not find guild"); return; } const args = message.content.toLowerCase().split(" "); if (args[0] === "setrank") { if (args.length < 3) { await message.reply( "invalid number of arguments. usage: `setrank `" ); return; } const player = findPlayer(pickupGuild, args[1]); if (!player) { await message.reply( `could not find player ${args[1]}. if this issue persists, try copy/pasting the player's discord handle or user id` ); return; } const playerId = player.id; const applicableName = getApplicableName(player); const rank = parseInt(args[2]); if (isNaN(rank) || rank < 0 || rank > 5) { await message.reply( `invalid rank ${args[2]}. must be an integer between 0 and 5` ); return; } // create rankings.json if it doesn't exist const rankingsPath = path.join(__dirname, "rankings.json"); if (!fs.existsSync(rankingsPath)) { fs.writeFileSync(rankingsPath, "{}"); } // rankings.json is a map of player's id to rank const rankings = JSON.parse(fs.readFileSync(rankingsPath)); rankings[playerId] = rank; console.log( `setting rank of ${applicableName}, ${playerId} to ${rank} at ${rankingsPath}...` ); new Promise((resolve, reject) => { fs.writeFile(rankingsPath, JSON.stringify(rankings), (err) => { if (err) reject(err); else resolve(); }); }).then((res, err) => { if (err) { console.error(err); message.reply(`error setting rank: ${err.message}`); } else { message.reply(`set rank of ${applicableName} to ${rank}`); } }); } if (args[0] === "getrank") { if (args.length < 2) { await message.reply( "invalid number of arguments. usage: `getrank `" ); return; } const player = findPlayer(pickupGuild, args[1]); if (!player) { await message.reply( `could not find player ${args[1]}. if this issue persists, try using the player's id` ); return; } const playerId = player.id; const applicableName = getApplicableName(player); try { console.log( `getting rank of ${applicableName}, ${playerId} at ${rankingsPath}...` ); const rankings = JSON.parse(fs.readFileSync(rankingsPath)); await message.reply( `${backticks}${applicableName} - ${"*".repeat( rankings[playerId] )}${backticks}` ); } catch (error) { console.error(error); await message.reply(`error getting rank: ${error.message}`); } } if (args[0] === "whitelist") { // add user to config.json whitelist if (args.length < 2) { await message.reply( "invalid number of arguments. usage: `whitelist `" ); return; } const name = args[1]; const player = findPlayer(pickupGuild, name); if (!player) { await message.reply( `could not find player ${name}. if this issue persists, try copy/pasting the player's discord handle or user id` ); return; } const playerId = player.id; const applicableName = getApplicableName(player); if (whitelistStr.includes(playerId)) { await message.reply( `user ${applicableName} (${playerId}) is already whitelisted` ); return; } const configPath = path.join(__dirname, "config.json"); const config = JSON.parse(fs.readFileSync(configPath)); whitelistStr += `,${playerId}`; config.RANKING_WHITELIST = whitelistStr; new Promise((resolve, reject) => { fs.writeFile(configPath, JSON.stringify(config), (err) => { if (err) reject(err); else resolve(); }); }).then((res, err) => { if (err) { console.error(err); message.reply(`error whitelisting user: ${err.message}`); } else { message.reply( `whitelisted user ${applicableName} (${playerId}). if this was done in error, please contact an admin` ); } }); } if (args[0] === "getwhitelist") { let str = `${backticks}${whitelistStr}${backticks}${backticks}` const whitelistIds = whitelistStr.split(","); for (const id of whitelistIds) { const player = findPlayer(pickupGuild, id); if (player) { str += `\n${getApplicableName(player)}`; } } str += `${backticks}`; await message.reply(str); } if (args[0] === "clearwhitelist") { if (args[1] !== "confirm") { await message.reply( "this command will clear the whitelist and prevent further dm commands. this will require manual intervention to undo. to confirm, use `clearwhitelist confirm`" ); return; } else { const configPath = path.join(__dirname, "config.json"); const config = JSON.parse(fs.readFileSync(configPath)); whitelistStr = ""; config.RANKING_WHITELIST = whitelistStr; new Promise((resolve, reject) => { fs.writeFile(configPath, JSON.stringify(config), (err) => { if (err) reject(err); else resolve(); }); }).then((res, err) => { if (err) { console.error(err); message.reply(`error clearing whitelist: ${err.message}`); } else { message.reply("whitelist cleared, please alert an admin"); } }); } } }); client.login(TOKEN);