diff --git a/.gitignore b/.gitignore index 46c831cca60460d382e3b0c989c39be7e7d1cc61..248948afcbd01cfabb553f55f725a400bc5d0a75 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ token .env node_modules +index.js diff --git a/index.js b/index.js deleted file mode 100644 index ad0d3bacc7d0b87febc99192ee2c769b7d148aae..0000000000000000000000000000000000000000 --- a/index.js +++ /dev/null @@ -1,214 +0,0 @@ -require('dotenv').config(); - -const Discord = require('discord.js'); -const fs = require('fs'); -const Color = require('color'); -const Long = require('long'); -const Cache = require('node-cache'); -const client = new Discord.Client({ - intents: [ - 'GUILDS', - 'GUILD_MEMBERS', - 'GUILD_MESSAGES', - ], -}); - -function updateClientStatus() { - client.user.setActivity(`/paint | ${client.guilds.cache.size} servers.`) -} - -client.on('ready', async() => { - console.log(`Logged in as ${client.user.tag}!`); - updateClientStatus(); - - // client.application.commands.create({ - // name: 'paint', - // description: 'Customize your username color', - // options: [ - // { name: 'color', description: 'Color to change your name to (HEX or CSS)', type: 'STRING', required: true }, - // ] - // }); - - // client.application.commands.create({ - // name: 'clean-roles', - // description: 'Run the role cleanup utility manually' - // }); -}); - -const schedules = {} - -const rateLimitCache = new Cache({ - stdTTL: 2 -}) -const rateLimitCache2 = new Cache({ - stdTTL: 230 -}) - -async function cleanup(guild) { - clearTimeout(schedules[guild.id]); - delete schedules[guild.id]; - let n = 0; - const rolesToCheck = guild.roles.cache.filter(role => role.name.startsWith('#')); - const allMembers = await guild.members.fetch(); - const colorUses = {}; - allMembers.forEach(x => { - x.roles.cache.forEach(role => { - if (role.name.startsWith('#')) { - colorUses[role.id] = true; - } - }); - }); - await Promise.all(rolesToCheck.map(async (role) => { - if (!colorUses[role.id]) { - n++; - await role.delete(); - } - })); - return n; -} - -async function scheduleClean(guild) { - if (schedules[guild.id]) { - return; - } - schedules[guild.id] = setTimeout(() => { - cleanup(guild) - }, 5 * 60 * 1000); -} - -client.on('messageCreate', async (msg) => { - if (msg.author.bot) return; - - if (msg.content.startsWith('!paint') || msg.content.startsWith('!color')) { - msg.channel.send('Name Paint has been updated to use slash commands. If they do not show up, have an admin reinvite the bot:\n') - } -}); - -client.on('interactionCreate', async (i) => { - if (i.isCommand() && i.commandName === 'paint') { - const input = i.options.get('color').value; - - let color; - try { - color = Color(input); - } catch (error) { - try { - color = Color('#' + input); - } catch (error) { - i.reply({ - ephemeral: true, - content: `Could not get a color from \`\`${input.substring(0, 500).replace(/\n/g, ' ').replace(/`/g, '\u2063`\u2063')}\`\``, - }) - return; - } - } - - const hex = color.hex(); - - if (rateLimitCache.has(i.user.id)) { - i.reply({ - ephemeral: true, - content: 'You have reached the rate limit. Please wait a few seconds before running the command again.', - }); - return; - } - - rateLimitCache.set(i.user.id , 'true'); - - let role = i.guild.roles.cache.find(role => role.name === hex); - - if (!role) { - if (i.guild.roles.cache.length === 250) { - i.reply({ - ephemeral: true, - content: `The role limit of **250 Roles** has been hit, I cannot assign you this name color.` - }); - return; - } else { - try { - role = await i.guild.roles.create({ - name: hex, - color: color.rgbNumber(), - permissions: [], - }); - } catch (error) { - console.log(error) - i.reply({ - ephemeral: true, - content: `Error creating a role for you, contact an admin to check my permissions.` - }); - return; - } - } - } - - try { - const rolesToRemove = i.member.roles.cache.filter(role => role.name.startsWith('#') && role.name !== hex); - rolesToRemove.map(async (role) => { - i.member.roles.remove(role); - }); - await i.member.roles.add(role); - i.reply({ - ephemeral: true, - content: `You\'ve been painted to **${hex}**` - }); - - scheduleClean(i.guild) - } catch (error) { - i.reply({ - ephemeral: true, - content: `Error assigning your role, contact an admin to check my permissions.` - }); - } - } -}); - -client.on('guildMemberRemove', (member) => { - scheduleClean(member.guild); -}); - -client.on('guildDelete', guild => { - updateClientStatus(); -}); - -function getDefaultChannel(guild) { - // Check for a "general" channel, which is often default chat - const generalChannel = guild.channels.cache.find(channel => channel.name === "general" && channel.permissionsFor(guild.client.user).has("SEND_MESSAGES")); - if (generalChannel) - return generalChannel; - // Now we get into the heavy stuff: first channel in order where the bot can speak - // hold on to your hats! - return guild.channels.cache - .filter(c => c.type === "text" && - c.permissionsFor(guild.client.user).has("SEND_MESSAGES")) - .sort((a, b) => a.position - b.position || - Long.fromString(a.id).sub(Long.fromString(b.id)).toNumber()) - .first(); -} - -client.on('guildCreate', guild => { - let defaultChannel = getDefaultChannel(guild); - defaultChannel && defaultChannel.send(`**I'm the Name Painter. I let users customize their name color.** -Some things to note - -- The paint command is available to **all users** in the server. -- You should not have existing roles that start with # -- You *generally* should not assign the roles manually - -To use me, simply run -> /paint -> Where is any valid HEX or CSS color value. - -Created by dave caruso, , support \`dave@davecode.me\``); - - updateClientStatus(); -}); - -client.login(process.env.TOKEN); - -if (process.env.HEALTHCHECKS_URL) { - var https = require('https'); - setInterval(() => { - https.get(process.env.HEALTHCHECKS_URL).on('error', () => {}); - }, 5 * 60 * 1000); -} \ No newline at end of file diff --git a/index.ts b/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..726a5dcc9ebb17664b67b682d6f463aa10491fca --- /dev/null +++ b/index.ts @@ -0,0 +1,197 @@ +import Color from "color"; +import * as Discord from "discord.js"; +import Long from "long"; +import NodeCache from "node-cache"; + +import 'dotenv/config'; + +const helpMessage = `**I'm the Name Painter. I let users customize their name color.** +Some things to note + +- The paint command is available to **all users** in the server by default. +- You should not have existing roles that start with \`#\` as this is how the bot identifies color roles. +- Roles always are added at the bottom of the role list, meaning none of your other roles should have colors. + +To use me, simply run +> /paint +> Where is any valid HEX or CSS color value. + +Created by dave caruso, , support \`me@paperdave.net\``; + +const client = new Discord.Client({ + intents: [ + Discord.IntentsBitField.Flags.Guilds, + Discord.IntentsBitField.Flags.GuildMembers, + ], +}); + +function updateClientStatus() { + client.user?.setActivity(`/paint | ${client.guilds.cache.size} servers.`) +} + +client.on('ready', async() => { + console.log(`Logged in as ${client.user?.tag}!`); + updateClientStatus(); + + // client.application.commands.create({ + // name: 'paint', + // description: 'Customize your username color', + // options: [ + // { name: 'color', description: 'Color to change your name to (HEX or CSS)', type: 'STRING', required: true }, + // ] + // }); +}); + +const rateLimitCache = new NodeCache({ + stdTTL: 2, +}) + +async function cleanup(guild: Discord.Guild) { + const now = Date.now(); + let n = 0; + const rolesToCheck = guild.roles.cache.filter(role => role.name.startsWith('#')); + const allMembers = await guild.members.fetch(); + const colorUses: Record = {}; + allMembers.forEach(x => { + x.roles.cache.forEach(role => { + if (role.name.startsWith('#')) { + colorUses[role.id] = true; + } + }); + }); + await Promise.all(rolesToCheck.map(async (role) => { + if (!colorUses[role.id]) { + n++; + await role.delete(); + } + })); + const time = Date.now() - now; + console.log(`[guild ${guild.id}] cleaned up ${n} roles in ${time}ms [${guild.roles.cache.size} roles left]`); + return n; +} + +client.on('interactionCreate', async (i) => { + if (i.isCommand() && i.commandName === 'paint') { + const input = String(i.options.get('color')!.value); + + let color; + try { + color = Color(input); + } catch (error) { + try { + color = Color('#' + input); + } catch (error) { + i.reply({ + ephemeral: true, + content: `Could not get a color from \`\`${input.substring(0, 500).replace(/\n/g, ' ').replace(/`/g, '\u2063`\u2063')}\`\``, + }) + return; + } + } + + const hex = color.hex(); + + if (rateLimitCache.has(i.user.id)) { + i.reply({ + ephemeral: true, + content: 'You have reached the rate limit. Please wait a few seconds before running the command again.', + }); + return; + } + + rateLimitCache.set(i.user.id , 'true'); + + let guild = i.guild ?? await client.guilds.fetch(i.guildId!); + + let role = guild.roles.cache.find(role => role.name === hex); + + if (!role) { + if (guild.roles.cache.size === 250) { + i.reply({ + ephemeral: true, + content: `The role limit of **250 Roles** has been hit, I cannot assign you this name color.` + }); + return; + } else { + try { + role = await guild.roles.create({ + name: hex, + color: color.rgbNumber(), + permissions: [], + }); + } catch (error) { + console.log(error) + i.reply({ + ephemeral: true, + content: `Error creating a role for you, contact an admin to check my permissions.` + }); + return; + } + } + } + + try { + const member = i.member!; + const memberRoles = member.roles as Discord.GuildMemberRoleManager; + const rolesToRemove = memberRoles.cache.filter(role => role.name.startsWith('#') && role.name !== hex); + rolesToRemove.map(async (role) => { + memberRoles.remove(role); + }); + await memberRoles.add(role); + i.reply({ + ephemeral: true, + content: `You\'ve been painted to **${hex}**` + }); + await cleanup(guild); + } catch (error) { + i.reply({ + ephemeral: true, + content: `Error assigning your role, contact an admin to check my permissions.` + }); + } + } +}); + +client.on('guildMemberRemove', (member) => { + cleanup(member.guild); +}); + +client.on('guildDelete', guild => { + updateClientStatus(); +}); + +async function getDefaultChannel(guild: Discord.Guild) { + const channels = await guild.channels.fetch(); + const array = [...channels.values()].filter(Boolean).sort((a, b) => (a as any).rawPosition - (b as any).rawPosition) as Discord.TextChannel[]; + + console.log(array.map(x => x.name)) + + // Check for a "general" channel, which is often default chat + const generalChannel = array.find(channel => channel.name === "general" && channel.permissionsFor(guild.client.user)?.has("SendMessages")); + if (generalChannel) + return generalChannel; + + // Now we get into the heavy stuff: first channel in order where everyone (and the bot) can speak + // hold on to your hats! + const sendableChannels = array + .filter(c => c.type === Discord.ChannelType.GuildText && c.permissionsFor(guild.client.user)?.has("SendMessages")) + .filter(c => c.permissionsFor(guild.id)?.has("ViewChannel") && c.permissionsFor(guild.id)?.has("SendMessages")) + + return sendableChannels[0]; +} + +client.on('guildCreate', async(guild) => { + let defaultChannel = await getDefaultChannel(guild) as Discord.TextChannel; + defaultChannel && defaultChannel.send(helpMessage); + + updateClientStatus(); +}); + +client.login(process.env.TOKEN); + +// if (process.env.HEALTHCHECKS_URL) { +// var https = require('https'); +// setInterval(() => { +// https.get(process.env.HEALTHCHECKS_URL).on('error', () => {}); +// }, 5 * 60 * 1000); +// } diff --git a/package.json b/package.json index 5b31bc45f9eedc3666699b35c0c28ef9a9d23a8e..ef23fd1a96037167396626e49bb76cc46633ec59 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,21 @@ { "name": "name-paint", - "version": "3.0.0", + "version": "3.1.0", + "private": true, "description": "Discord Bot to let people paint their name anything.", "main": "index.js", "scripts": { - "start": "node index.js" + "start": "node index.js", + "build": "tsc" }, - "author": "", - "license": "ISC", + "type": "module", + "author": "Dave Caruso", "dependencies": { "color": "^3.1.2", - "discord.js": "^13.0.0-dev.4d53d0f.1626566655", + "discord.js": "^14.7.1", "dotenv": "^10.0.0", "long": "^4.0.0", - "node-cache": "^5.1.2" + "node-cache": "^5.1.2", + "typescript": "^4.9.5" } } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..eb28dc8ab99520c2f962ee6ae3ebcc57cba8614c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,103 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "ESNext", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/yarn.lock b/yarn.lock index 0409d126df3085f47bab9bdc2a01b70e8ed7cee7..a5d0e508341f6bc5ab231625d6894d7ce74029a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,56 +2,83 @@ # yarn lockfile v1 -"@discordjs/builders@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-0.2.0.tgz#832c8d894aad13362db7a99f11a7826b21e4cd94" - integrity sha512-TVq7NZBCJrrTRc3CfxOr3IdgY5nrtqVxZ7qDUF1mN6LgxIiOldmFxsSwMrQBzLFVmOwqFyNLKCeblley8UpEuw== +"@discordjs/builders@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.4.0.tgz#b951b5e6ce4e459cd06174ce50dbd51c254c1d47" + integrity sha512-nEeTCheTTDw5kO93faM1j8ZJPonAX86qpq/QVoznnSa8WWcCgJpjlu6GylfINTDW6o7zZY0my2SYdxx2mfNwGA== dependencies: - discord-api-types "^0.18.1" - tslib "^2.3.0" + "@discordjs/util" "^0.1.0" + "@sapphire/shapeshift" "^3.7.1" + discord-api-types "^0.37.20" + fast-deep-equal "^3.1.3" + ts-mixer "^6.0.2" + tslib "^2.4.1" -"@discordjs/collection@^0.1.6": - version "0.1.6" - resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.1.6.tgz#9e9a7637f4e4e0688fd8b2b5c63133c91607682c" - integrity sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ== +"@discordjs/collection@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.3.0.tgz#65bf9674db72f38c25212be562bb28fa0dba6aa3" + integrity sha512-ylt2NyZ77bJbRij4h9u/wVy7qYw/aDqQLWnadjvDqW/WoWCxrsX6M3CIw9GVP5xcGCDxsrKj5e0r5evuFYwrKg== -"@discordjs/form-data@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@discordjs/form-data/-/form-data-3.0.1.tgz#5c9e6be992e2e57d0dfa0e39979a850225fb4697" - integrity sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg== +"@discordjs/rest@^1.4.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-1.5.0.tgz#dc15474ab98cf6f31291bf61bbc72bcf4f30cea2" + integrity sha512-lXgNFqHnbmzp5u81W0+frdXN6Etf4EUi8FAPcWpSykKd8hmlWh1xy6BmE0bsJypU1pxohaA8lQCgp70NUI3uzA== dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" + "@discordjs/collection" "^1.3.0" + "@discordjs/util" "^0.1.0" + "@sapphire/async-queue" "^1.5.0" + "@sapphire/snowflake" "^3.2.2" + discord-api-types "^0.37.23" + file-type "^18.0.0" + tslib "^2.4.1" + undici "^5.13.0" -"@sapphire/async-queue@^1.1.4": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.1.4.tgz#ae431310917a8880961cebe8e59df6ffa40f2957" - integrity sha512-fFrlF/uWpGOX5djw5Mu2Hnnrunao75WGey0sP0J3jnhmrJ5TAPzHYOmytD5iN/+pMxS+f+u/gezqHa9tPhRHEA== +"@discordjs/util@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@discordjs/util/-/util-0.1.0.tgz#e42ca1bf407bc6d9adf252877d1b206e32ba369a" + integrity sha512-e7d+PaTLVQav6rOc2tojh2y6FE8S7REkqLldq1XF4soCx74XB/DIjbVbVLtBemf0nLW77ntz0v+o5DytKwFNLQ== + +"@sapphire/async-queue@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.5.0.tgz#2f255a3f186635c4fb5a2381e375d3dfbc5312d8" + integrity sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA== + +"@sapphire/shapeshift@^3.7.1": + version "3.8.1" + resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.8.1.tgz#b98dc6a7180f9b38219267917b2e6fa33f9ec656" + integrity sha512-xG1oXXBhCjPKbxrRTlox9ddaZTvVpOhYLmKmApD/vIWOV1xEYXnpoFs68zHIZBGbqztq6FrUPNPerIrO1Hqeaw== + dependencies: + fast-deep-equal "^3.1.3" + lodash "^4.17.21" + +"@sapphire/snowflake@^3.2.2": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.4.0.tgz#25c012158a9feea2256c718985dbd6c1859a5022" + integrity sha512-zZxymtVO6zeXVMPds+6d7gv/OfnCc25M1Z+7ZLB0oPmeMTPeRWVPQSS16oDJy5ZsyCOLj7M6mbZml5gWXcVRNw== + +"@tokenizer/token@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" + integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== "@types/node@*": version "16.3.3" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.3.tgz#0c30adff37bbbc7a50eb9b58fae2a504d0d88038" integrity sha512-8h7k1YgQKxKXWckzFCMfsIwn0Y61UK6tlD6y2lOb3hTOIMlK3t9/QwHOhc81TwU+RMf0As5fj7NPjroERCnejQ== -"@types/ws@^7.4.5": - version "7.4.6" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.6.tgz#c4320845e43d45a7129bb32905e28781c71c1fff" - integrity sha512-ijZ1vzRawI7QoWnTNL8KpHixd2b2XVb9I9HAqI3triPsh1EC0xH0Eg6w2O3TKbDCgiNNlJqfrof6j4T2I+l9vw== +"@types/ws@^8.5.3": + version "8.5.4" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" + integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== dependencies: "@types/node" "*" -abort-controller@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" - integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== +busboy@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== dependencies: - event-target-shim "^5.0.0" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + streamsearch "^1.1.0" clone@2.x: version "2.1.2" @@ -86,75 +113,78 @@ color@^3.1.2: color-convert "^2.0.1" color-string "^1.6.0" -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -discord-api-types@^0.18.1: - version "0.18.1" - resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.18.1.tgz#5d08ed1263236be9c21a22065d0e6b51f790f492" - integrity sha512-hNC38R9ZF4uaujaZQtQfm5CdQO58uhdkoHQAVvMfIL0LgOSZeW575W8H6upngQOuoxWd8tiRII3LLJm9zuQKYg== - -discord-api-types@^0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e: - version "0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e" - resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e.tgz#d5f36f5712ec8fe2fe928b5c37618c94a3969d6a" - integrity sha512-ttRA/8e/WKHDbGFfED5WlS7gID+kalmNr6iMiWBCvkphQ7kFHiTOVbnj/zX9ksaRaYXp/I38SCQ+qZvLu8DJZg== +discord-api-types@^0.37.20, discord-api-types@^0.37.23: + version "0.37.32" + resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.32.tgz#661053cfab02eb4807d26a3800c81949b5ab7607" + integrity sha512-oUA4dhrzFOkvIWOc9WvKhPwsezUUVd5v5M7am1uupnRZjOmpE9RJMS0fTCUqNiMRlNAaZwPEy09UZOdIR9CyGQ== -discord.js@^13.0.0-dev.4d53d0f.1626566655: - version "13.0.0-dev.4d53d0f.1626566655" - resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-13.0.0-dev.4d53d0f.1626566655.tgz#8680d6c08a931b1550664daeb4ac02eb2c9c9bde" - integrity sha512-TXq6Q16X8QVt4A81q0eaBZd+hfvThIvx76FLd8eRiB4H9Hid3BnVwzbOAucwZ3Xr+koPcaw16YVoV+XL9ZwknA== +discord.js@^14.7.1: + version "14.7.1" + resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.7.1.tgz#26079d0ff4d27daf02480a403c456121f0682bd9" + integrity sha512-1FECvqJJjjeYcjSm0IGMnPxLqja/pmG1B0W2l3lUY2Gi4KXiyTeQmU1IxWcbXHn2k+ytP587mMWqva2IA87EbA== dependencies: - "@discordjs/builders" "^0.2.0" - "@discordjs/collection" "^0.1.6" - "@discordjs/form-data" "^3.0.1" - "@sapphire/async-queue" "^1.1.4" - "@types/ws" "^7.4.5" - abort-controller "^3.0.0" - discord-api-types "^0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e" - node-fetch "^2.6.1" - ws "^7.5.1" + "@discordjs/builders" "^1.4.0" + "@discordjs/collection" "^1.3.0" + "@discordjs/rest" "^1.4.0" + "@discordjs/util" "^0.1.0" + "@sapphire/snowflake" "^3.2.2" + "@types/ws" "^8.5.3" + discord-api-types "^0.37.20" + fast-deep-equal "^3.1.3" + lodash.snakecase "^4.1.1" + tslib "^2.4.1" + undici "^5.13.0" + ws "^8.11.0" dotenv@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== -event-target-shim@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" - integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +file-type@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-18.2.0.tgz#c2abec00d1af0f09151e1549e3588aab0bac5001" + integrity sha512-M3RQMWY3F2ykyWZ+IHwNCjpnUmukYhtdkGGC1ZVEUb0ve5REGF7NNJ4Q9ehCUabtQKtSVFOMbFTXgJlFb0DQIg== + dependencies: + readable-web-to-node-stream "^3.0.2" + strtok3 "^7.0.0" + token-types "^5.0.1" + +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +inherits@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== is-arrayish@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== +lodash.snakecase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== + +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + long@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== -mime-db@1.48.0: - version "1.48.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" - integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== - -mime-types@^2.1.12: - version "2.1.31" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" - integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== - dependencies: - mime-db "1.48.0" - node-cache@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" @@ -162,10 +192,31 @@ node-cache@^5.1.2: dependencies: clone "2.x" -node-fetch@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== +peek-readable@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-5.0.0.tgz#7ead2aff25dc40458c60347ea76cfdfd63efdfec" + integrity sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A== + +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-web-to-node-stream@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb" + integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw== + dependencies: + readable-stream "^3.6.0" + +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== simple-swizzle@^0.2.2: version "0.2.2" @@ -174,12 +225,62 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" -tslib@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" - integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== - -ws@^7.5.1: - version "7.5.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74" - integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg== +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strtok3@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-7.0.0.tgz#868c428b4ade64a8fd8fee7364256001c1a4cbe5" + integrity sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ== + dependencies: + "@tokenizer/token" "^0.3.0" + peek-readable "^5.0.0" + +token-types@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/token-types/-/token-types-5.0.1.tgz#aa9d9e6b23c420a675e55413b180635b86a093b4" + integrity sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg== + dependencies: + "@tokenizer/token" "^0.3.0" + ieee754 "^1.2.1" + +ts-mixer@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.3.tgz#69bd50f406ff39daa369885b16c77a6194c7cae6" + integrity sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ== + +tslib@^2.4.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== + +typescript@^4.9.5: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +undici@^5.13.0: + version "5.18.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.18.0.tgz#e88a77a74d991a30701e9a6751e4193a26fabda9" + integrity sha512-1iVwbhonhFytNdg0P4PqyIAXbdlVZVebtPDvuM36m66mRw4OGrCm2MYynJv/UENFLdP13J1nPVQzVE2zTs1OeA== + dependencies: + busboy "^1.6.0" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +ws@^8.11.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8" + integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==