diff --git a/package-lock.json b/package-lock.json index 86853ab..80b5169 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,6 @@ "@types/inquirer": "^6.5.0", "@types/jest": "^26.0.20", "@types/mathjs": "^6.0.11", - "@types/mocha": "^8.2.0", "@types/ms": "^0.7.31", "@types/node": "^14.14.20", "@types/ws": "^7.4.0", @@ -1083,12 +1082,6 @@ "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==", "dev": true }, - "node_modules/@types/mocha": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", - "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==", - "dev": true - }, "node_modules/@types/ms": { "version": "0.7.31", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", @@ -8966,12 +8959,6 @@ "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==", "dev": true }, - "@types/mocha": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", - "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==", - "dev": true - }, "@types/ms": { "version": "0.7.31", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", diff --git a/package.json b/package.json index e38dcab..a48dcd5 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "@types/inquirer": "^6.5.0", "@types/jest": "^26.0.20", "@types/mathjs": "^6.0.11", - "@types/mocha": "^8.2.0", "@types/ms": "^0.7.31", "@types/node": "^14.14.20", "@types/ws": "^7.4.0", diff --git a/src/commands/admin.ts b/src/commands/admin.ts deleted file mode 100644 index 2622087..0000000 --- a/src/commands/admin.ts +++ /dev/null @@ -1,329 +0,0 @@ -import Command from "../core/command"; -import {CommonLibrary, logs, botHasPermission, clean} from "../core/lib"; -import {Config, Storage} from "../core/structures"; -import {PermissionNames, getPermissionLevel} from "../core/permissions"; -import {Permissions} from "discord.js"; -import * as discord from "discord.js"; - -function getLogBuffer(type: string) { - return { - files: [ - { - attachment: Buffer.alloc(logs[type].length, logs[type]), - name: `${Date.now()}.${type}.log` - } - ] - }; -} - -const activities = ["playing", "listening", "streaming", "watching"]; -const statuses = ["online", "idle", "dnd", "invisible"]; - -export default new Command({ - description: - "An all-in-one command to do admin stuff. You need to be either an admin of the server or one of the bot's mechanics to use this command.", - async run($: CommonLibrary): Promise { - if (!$.member) - return $.channel.send( - "Couldn't find a member object for you! Did you make sure you used this in a server?" - ); - const permLevel = getPermissionLevel($.member); - $.channel.send( - `${$.author.toString()}, your permission level is \`${PermissionNames[permLevel]}\` (${permLevel}).` - ); - }, - subcommands: { - set: new Command({ - description: "Set different per-guild settings for the bot.", - run: "You have to specify the option you want to set.", - permission: Command.PERMISSIONS.ADMIN, - subcommands: { - prefix: new Command({ - description: "Set a custom prefix for your guild. Removes your custom prefix if none is provided.", - usage: "()", - async run($: CommonLibrary): Promise { - Storage.getGuild($.guild?.id || "N/A").prefix = null; - Storage.save(); - $.channel.send( - `The custom prefix for this guild has been removed. My prefix is now back to \`${Config.prefix}\`.` - ); - }, - any: new Command({ - async run($: CommonLibrary): Promise { - Storage.getGuild($.guild?.id || "N/A").prefix = $.args[0]; - Storage.save(); - $.channel.send(`The custom prefix for this guild is now \`${$.args[0]}\`.`); - } - }) - }), - welcome: new Command({ - description: "Configure your server's welcome settings for the bot.", - usage: "type/channel <...>", - run: "You need to specify which part to modify, `type`/`channel`.", - subcommands: { - type: new Command({ - description: - "Sets how welcome messages are displayed for your server. Removes welcome messages if unspecified.", - usage: "`none`/`text`/`graphical`", - async run($) { - if ($.guild) { - Storage.getGuild($.guild.id).welcomeType = "none"; - Storage.save(); - $.channel.send("Set this server's welcome type to `none`."); - } else { - $.channel.send("You must use this command in a server."); - } - }, - // I should probably make this a bit more dynamic... Oh well. - subcommands: { - text: new Command({ - async run($) { - if ($.guild) { - Storage.getGuild($.guild.id).welcomeType = "text"; - Storage.save(); - $.channel.send("Set this server's welcome type to `text`."); - } else { - $.channel.send("You must use this command in a server."); - } - } - }), - graphical: new Command({ - async run($) { - if ($.guild) { - Storage.getGuild($.guild.id).welcomeType = "graphical"; - Storage.save(); - $.channel.send("Set this server's welcome type to `graphical`."); - } else { - $.channel.send("You must use this command in a server."); - } - } - }) - } - }), - channel: new Command({ - description: "Sets the welcome channel for your server. Type `#` to reference the channel.", - usage: "()", - async run($) { - if ($.guild) { - Storage.getGuild($.guild.id).welcomeChannel = $.channel.id; - Storage.save(); - $.channel.send( - `Successfully set ${$.channel} as the welcome channel for this server.` - ); - } else { - $.channel.send("You must use this command in a server."); - } - }, - // If/when channel types come out, this will be the perfect candidate to test it. - any: new Command({ - async run($) { - if ($.guild) { - const match = $.args[0].match(/^<#(\d{17,19})>$/); - - if (match) { - Storage.getGuild($.guild.id).welcomeChannel = match[1]; - Storage.save(); - $.channel.send( - `Successfully set this server's welcome channel to ${match[0]}.` - ); - } else { - $.channel.send( - "You must provide a reference channel. You can do this by typing `#` then searching for the proper channel." - ); - } - } else { - $.channel.send("You must use this command in a server."); - } - } - }) - }), - message: new Command({ - description: - "Sets a custom welcome message for your server. Use `%user%` as the placeholder for the user.", - usage: "()", - async run($) { - if ($.guild) { - Storage.getGuild($.guild.id).welcomeMessage = null; - Storage.save(); - $.channel.send("Reset your server's welcome message to the default."); - } else { - $.channel.send("You must use this command in a server."); - } - }, - any: new Command({ - async run($) { - if ($.guild) { - const message = $.args.join(" "); - Storage.getGuild($.guild.id).welcomeMessage = message; - Storage.save(); - $.channel.send(`Set your server's welcome message to \`${message}\`.`); - } else { - $.channel.send("You must use this command in a server."); - } - } - }) - }) - } - }) - } - }), - diag: new Command({ - description: 'Requests a debug log with the "info" verbosity level.', - permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { - $.channel.send(getLogBuffer("info")); - }, - any: new Command({ - description: `Select a verbosity to listen to. Available levels: \`[${Object.keys(logs).join(", ")}]\``, - async run($: CommonLibrary): Promise { - const type = $.args[0]; - - if (type in logs) $.channel.send(getLogBuffer(type)); - else - $.channel.send( - `Couldn't find a verbosity level named \`${type}\`! The available types are \`[${Object.keys( - logs - ).join(", ")}]\`.` - ); - } - }) - }), - status: new Command({ - description: "Changes the bot's status.", - permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { - $.channel.send("Setting status to `online`..."); - }, - any: new Command({ - description: `Select a status to set to. Available statuses: \`[${statuses.join(", ")}]\`.`, - async run($: CommonLibrary): Promise { - if (!statuses.includes($.args[0])) return $.channel.send("That status doesn't exist!"); - else { - $.client.user?.setStatus($.args[0]); - $.channel.send(`Setting status to \`${$.args[0]}\`...`); - } - } - }) - }), - purge: new Command({ - description: "Purges bot messages.", - permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { - if ($.message.channel instanceof discord.DMChannel) { - return; - } - $.message.delete(); - const msgs = await $.channel.messages.fetch({ - limit: 100 - }); - const travMessages = msgs.filter((m) => m.author.id === $.client.user?.id); - - await $.message.channel.send(`Found ${travMessages.size} messages to delete.`).then((m) => - m.delete({ - timeout: 5000 - }) - ); - await $.message.channel.bulkDelete(travMessages); - } - }), - clear: new Command({ - description: "Clears a given amount of messages.", - usage: "", - run: "A number was not provided.", - number: new Command({ - description: "Amount of messages to delete.", - async run($: CommonLibrary): Promise { - if ($.channel.type === "dm") { - await $.channel.send("Can't clear messages in the DMs!"); - return; - } - $.message.delete(); - const fetched = await $.channel.messages.fetch({ - limit: $.args[0] - }); - await $.channel.bulkDelete(fetched); - } - }) - }), - eval: new Command({ - description: "Evaluate code.", - usage: "", - permission: Command.PERMISSIONS.BOT_OWNER, - // You have to bring everything into scope to use them. AFAIK, there isn't a more maintainable way to do this, but at least TS will let you know if anything gets removed. - async run({args, author, channel, client, guild, member, message}): Promise { - try { - const code = args.join(" "); - let evaled = eval(code); - - if (typeof evaled !== "string") evaled = require("util").inspect(evaled); - channel.send(clean(evaled), {code: "js", split: true}); - } catch (err) { - channel.send(`\`ERROR\` \`\`\`js\n${clean(err)}\n\`\`\``); - } - } - }), - nick: new Command({ - description: "Change the bot's nickname.", - permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { - const nickName = $.args.join(" "); - await $.guild?.me?.setNickname(nickName); - if (botHasPermission($.guild, Permissions.FLAGS.MANAGE_MESSAGES)) - $.message.delete({timeout: 5000}).catch($.handler.bind($)); - $.channel.send(`Nickname set to \`${nickName}\``).then((m) => m.delete({timeout: 5000})); - } - }), - guilds: new Command({ - description: "Shows a list of all guilds the bot is a member of.", - permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { - const guildList = $.client.guilds.cache.array().map((e) => e.name); - $.channel.send(guildList); - } - }), - activity: new Command({ - description: "Set the activity of the bot.", - permission: Command.PERMISSIONS.BOT_SUPPORT, - usage: " ", - async run($: CommonLibrary): Promise { - $.client.user?.setActivity(".help", { - type: "LISTENING" - }); - $.channel.send("Activity set to default."); - }, - any: new Command({ - description: `Select an activity type to set. Available levels: \`[${activities.join(", ")}]\``, - async run($: CommonLibrary): Promise { - const type = $.args[0]; - - if (activities.includes(type)) { - $.client.user?.setActivity($.args.slice(1).join(" "), { - type: $.args[0].toUpperCase() - }); - $.channel.send( - `Set activity to \`${$.args[0].toUpperCase()}\` \`${$.args.slice(1).join(" ")}\`.` - ); - } else - $.channel.send( - `Couldn't find an activity type named \`${type}\`! The available types are \`[${activities.join( - ", " - )}]\`.` - ); - } - }) - }), - syslog: new Command({ - description: "Sets up the current channel to receive system logs.", - permission: Command.PERMISSIONS.BOT_ADMIN, - async run($) { - if ($.guild) { - Config.systemLogsChannel = $.channel.id; - Config.save(); - $.channel.send(`Successfully set ${$.channel} as the system logs channel.`); - } else { - $.channel.send("DM system log channels aren't supported."); - } - } - }) - } -}); diff --git a/src/commands/fun/figlet.ts b/src/commands/fun/figlet.ts index ded3d94..b78ba89 100644 --- a/src/commands/fun/figlet.ts +++ b/src/commands/fun/figlet.ts @@ -1,15 +1,15 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; import figlet from "figlet"; -export default new Command({ +export default new NamedCommand({ description: "Generates a figlet of your input.", - async run($) { - const input = $.args.join(" "); - if (!$.args[0]) { - $.channel.send("You have to provide input for me to create a figlet!"); + async run({message, channel, guild, author, member, client, args}) { + const input = args.join(" "); + if (!args[0]) { + channel.send("You have to provide input for me to create a figlet!"); return; } - $.channel.send( + channel.send( "```" + figlet.textSync(`${input}`, { horizontalLayout: "full" diff --git a/src/commands/fun/insult.ts b/src/commands/fun/insult.ts index c2e0260..63847fd 100644 --- a/src/commands/fun/insult.ts +++ b/src/commands/fun/insult.ts @@ -1,14 +1,14 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; -export default new Command({ +export default new NamedCommand({ description: "Insult TravBot! >:D", - async run($) { - $.channel.startTyping(); + async run({message, channel, guild, author, member, client, args}) { + channel.startTyping(); setTimeout(() => { - $.channel.send( - `${$.author.toString()} What the fuck did you just fucking say about me, you little bitch? I'll have you know I graduated top of my class in the Navy Seals, and I've been involved in numerous secret raids on Al-Quaeda, and I have over 300 confirmed kills. I am trained in gorilla warfare and I'm the top sniper in the entire US armed forces. You are nothing to me but just another target. I will wipe you the fuck out with precision the likes of which has never been seen before on this Earth, mark my fucking words. You think you can get away with saying that shit to me over the Internet? Think again, fucker. As we speak I am contacting my secret network of spies across the USA and your IP is being traced right now so you better prepare for the storm, maggot. The storm that wipes out the pathetic little thing you call your life. You're fucking dead, kid. I can be anywhere, anytime, and I can kill you in over seven hundred ways, and that's just with my bare hands. Not only am I extensively trained in unarmed combat, but I have access to the entire arsenal of the United States Marine Corps and I will use it to its full extent to wipe your miserable ass off the face of the continent, you little shit. If only you could have known what unholy retribution your little "clever" comment was about to bring down upon you, maybe you would have held your fucking tongue. But you couldn't, you didn't, and now you're paying the price, you goddamn idiot. I will shit fury all over you and you will drown in it. You're fucking dead, kiddo.` + channel.send( + `${author} What the fuck did you just fucking say about me, you little bitch? I'll have you know I graduated top of my class in the Navy Seals, and I've been involved in numerous secret raids on Al-Quaeda, and I have over 300 confirmed kills. I am trained in gorilla warfare and I'm the top sniper in the entire US armed forces. You are nothing to me but just another target. I will wipe you the fuck out with precision the likes of which has never been seen before on this Earth, mark my fucking words. You think you can get away with saying that shit to me over the Internet? Think again, fucker. As we speak I am contacting my secret network of spies across the USA and your IP is being traced right now so you better prepare for the storm, maggot. The storm that wipes out the pathetic little thing you call your life. You're fucking dead, kid. I can be anywhere, anytime, and I can kill you in over seven hundred ways, and that's just with my bare hands. Not only am I extensively trained in unarmed combat, but I have access to the entire arsenal of the United States Marine Corps and I will use it to its full extent to wipe your miserable ass off the face of the continent, you little shit. If only you could have known what unholy retribution your little "clever" comment was about to bring down upon you, maybe you would have held your fucking tongue. But you couldn't, you didn't, and now you're paying the price, you goddamn idiot. I will shit fury all over you and you will drown in it. You're fucking dead, kiddo.` ); - $.channel.stopTyping(); + channel.stopTyping(); }, 60000); } }); diff --git a/src/commands/fun/love.ts b/src/commands/fun/love.ts index d0f9a5a..94467c5 100644 --- a/src/commands/fun/love.ts +++ b/src/commands/fun/love.ts @@ -1,13 +1,13 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; -export default new Command({ +export default new NamedCommand({ description: "Chooses someone to love.", - async run($) { - if ($.guild) { - const member = $.guild.members.cache.random(); - $.channel.send(`I love ${member.user.username}!`); + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + const member = guild.members.cache.random(); + channel.send(`I love ${member.user.username}!`); } else { - $.channel.send("You must use this command in a guild!"); + channel.send("You must use this command in a guild!"); } } }); diff --git a/src/commands/fun/ravi.ts b/src/commands/fun/ravi.ts index fbcf14e..7b408f6 100644 --- a/src/commands/fun/ravi.ts +++ b/src/commands/fun/ravi.ts @@ -1,11 +1,11 @@ -import Command from "../../core/command"; -import {Random} from "../../core/lib"; +import {Command, NamedCommand} from "../../core"; +import {Random} from "../../lib"; -export default new Command({ +export default new NamedCommand({ description: "Ravioli ravioli...", usage: "[number from 1 to 9]", - async run($) { - $.channel.send({ + async run({message, channel, guild, author, member, client, args}) { + channel.send({ embed: { title: "Ravioli ravioli...", image: { @@ -18,11 +18,11 @@ export default new Command({ }); }, number: new Command({ - async run($) { - const arg: number = $.args[0]; + async run({message, channel, guild, author, member, client, args}) { + const arg: number = args[0]; if (arg >= 1 && arg <= 9) { - $.channel.send({ + channel.send({ embed: { title: "Ravioli ravioli...", image: { @@ -31,7 +31,7 @@ export default new Command({ } }); } else { - $.channel.send("Please provide a number between 1 and 9."); + channel.send("Please provide a number between 1 and 9."); } } }) diff --git a/src/commands/fun/thonk.ts b/src/commands/fun/thonk.ts index a09c6e4..762549e 100644 --- a/src/commands/fun/thonk.ts +++ b/src/commands/fun/thonk.ts @@ -1,4 +1,4 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; const letters: {[letter: string]: string[]} = { a: "aáàảãạâấầẩẫậăắằẳẵặ".split(""), @@ -31,11 +31,11 @@ function transform(str: string) { let phrase = "I have no currently set phrase!"; -export default new Command({ +export default new NamedCommand({ description: "Transforms your text into vietnamese.", usage: "thonk ([text])", - async run($) { - if ($.args.length > 0) phrase = $.args.join(" "); - $.channel.send(transform(phrase)); + async run({message, channel, guild, author, member, client, args}) { + if (args.length > 0) phrase = args.join(" "); + channel.send(transform(phrase)); } }); diff --git a/src/commands/fun/urban.ts b/src/commands/fun/urban.ts index 1f21c2e..2901433 100644 --- a/src/commands/fun/urban.ts +++ b/src/commands/fun/urban.ts @@ -1,16 +1,16 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; import {MessageEmbed} from "discord.js"; // Anycasting Alert const urban = require("relevant-urban"); -export default new Command({ +export default new NamedCommand({ description: "Gives you a definition of the inputted word.", - async run($) { - if (!$.args[0]) { - $.channel.send("Please input a word."); + async run({message, channel, guild, author, member, client, args}) { + if (!args[0]) { + channel.send("Please input a word."); } - const res = await urban($.args.join(" ")).catch((e: Error) => { - return $.channel.send("Sorry, that word was not found."); + const res = await urban(args.join(" ")).catch((e: Error) => { + return channel.send("Sorry, that word was not found."); }); const embed = new MessageEmbed() .setColor(0x1d2439) @@ -22,6 +22,6 @@ export default new Command({ if (res.tags.length > 0 && res.tags.join(" ").length < 1024) { embed.addField("Tags", res.tags.join(", "), true); } - $.channel.send(embed); + channel.send(embed); } }); diff --git a/src/commands/fun/weather.ts b/src/commands/fun/weather.ts index aaac509..d232ea3 100644 --- a/src/commands/fun/weather.ts +++ b/src/commands/fun/weather.ts @@ -1,22 +1,22 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; import {MessageEmbed} from "discord.js"; // Anycasting Alert const weather = require("weather-js"); -export default new Command({ +export default new NamedCommand({ description: "Shows weather info of specified location.", - async run($) { - if ($.args.length == 0) { - $.channel.send("You need to provide a city."); + async run({message, channel, guild, author, member, client, args}) { + if (args.length == 0) { + channel.send("You need to provide a city."); return; } weather.find( { - search: $.args.join(" "), + search: args.join(" "), degreeType: "C" }, function (err: any, result: any) { - if (err) $.channel.send(err); + if (err) channel.send(err); var current = result[0].current; var location = result[0].location; const embed = new MessageEmbed() @@ -30,7 +30,7 @@ export default new Command({ .addField("Feels like", `${current.feelslike} Degrees`, true) .addField("Winds", current.winddisplay, true) .addField("Humidity", `${current.humidity}%`, true); - $.channel.send({ + channel.send({ embed }); } diff --git a/src/commands/fun/whois.ts b/src/commands/fun/whois.ts index ed7e07e..c8daff2 100644 --- a/src/commands/fun/whois.ts +++ b/src/commands/fun/whois.ts @@ -1,5 +1,5 @@ import {User} from "discord.js"; -import Command from "../../core/command"; +import {Command, NamedCommand, getMemberByUsername} from "../../core"; // Quotes must be used here or the numbers will change const registry: {[id: string]: string} = { @@ -34,49 +34,49 @@ const registry: {[id: string]: string} = { "Some random conlanger, worldbuilder and programmer doofus. ~~May also secretly be a nyan. :3~~" }; -export default new Command({ +export default new NamedCommand({ description: "Tells you who you or the specified user is.", aliases: ["whoami"], - async run($) { - const id = $.author.id; + async run({message, channel, guild, author, member, client, args}) { + const id = author.id; if (id in registry) { - $.channel.send(registry[id]); + channel.send(registry[id]); } else { - $.channel.send("You haven't been added to the registry yet!"); + channel.send("You haven't been added to the registry yet!"); } }, user: new Command({ - async run($) { - const user: User = $.args[0]; + async run({message, channel, guild, author, member, client, args}) { + const user: User = args[0]; const id = user.id; if (id in registry) { - $.channel.send(`\`${user.username}\` - ${registry[id]}`); + channel.send(`\`${user.username}\` - ${registry[id]}`); } else { - $.channel.send(`\`${user.username}#${user.discriminator}\` hasn't been added to the registry yet!`); + channel.send(`\`${user.username}#${user.discriminator}\` hasn't been added to the registry yet!`); } } }), any: new Command({ - async run($) { - if ($.guild) { - const query: string = $.args.join(" "); - const member = await $.getMemberByUsername($.guild, query); + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + const query: string = args.join(" "); + const member = await getMemberByUsername(guild, query); if (member && member.id in registry) { const id = member.id; if (id in registry) { - $.channel.send(`\`${member.user.username}\` - ${registry[member.id]}`); + channel.send(`\`${member.user.username}\` - ${registry[member.id]}`); } else { - $.channel.send(`\`${member.user.username}\` hasn't been added to the registry yet!`); + channel.send(`\`${member.user.username}\` hasn't been added to the registry yet!`); } } else { - $.channel.send(`Couldn't find a user by the name of \`${query}\`!`); + channel.send(`Couldn't find a user by the name of \`${query}\`!`); } } else { - $.channel.send( + channel.send( "You must run this in a guild! (*If you have the user's ID, you don't have to be in a guild.*)" ); } diff --git a/src/commands/system/admin.ts b/src/commands/system/admin.ts index b2da0bb..7aeebc2 100644 --- a/src/commands/system/admin.ts +++ b/src/commands/system/admin.ts @@ -50,6 +50,113 @@ export default new NamedCommand({ channel.send(`The custom prefix for this guild is now \`${args[0]}\`.`); } }) + }), + welcome: new NamedCommand({ + description: "Configure your server's welcome settings for the bot.", + usage: "type/channel <...>", + run: "You need to specify which part to modify, `type`/`channel`.", + subcommands: { + type: new NamedCommand({ + description: + "Sets how welcome messages are displayed for your server. Removes welcome messages if unspecified.", + usage: "`none`/`text`/`graphical`", + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + Storage.getGuild(guild.id).welcomeType = "none"; + Storage.save(); + channel.send("Set this server's welcome type to `none`."); + } else { + channel.send("You must use this command in a server."); + } + }, + // I should probably make this a bit more dynamic... Oh well. + subcommands: { + text: new NamedCommand({ + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + Storage.getGuild(guild.id).welcomeType = "text"; + Storage.save(); + channel.send("Set this server's welcome type to `text`."); + } else { + channel.send("You must use this command in a server."); + } + } + }), + graphical: new NamedCommand({ + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + Storage.getGuild(guild.id).welcomeType = "graphical"; + Storage.save(); + channel.send("Set this server's welcome type to `graphical`."); + } else { + channel.send("You must use this command in a server."); + } + } + }) + } + }), + channel: new NamedCommand({ + description: "Sets the welcome channel for your server. Type `#` to reference the channel.", + usage: "()", + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + Storage.getGuild(guild.id).welcomeChannel = channel.id; + Storage.save(); + channel.send(`Successfully set ${channel} as the welcome channel for this server.`); + } else { + channel.send("You must use this command in a server."); + } + }, + // If/when channel types come out, this will be the perfect candidate to test it. + any: new Command({ + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + const match = args[0].match(/^<#(\d{17,19})>$/); + + if (match) { + Storage.getGuild(guild.id).welcomeChannel = match[1]; + Storage.save(); + channel.send( + `Successfully set this server's welcome channel to ${match[0]}.` + ); + } else { + channel.send( + "You must provide a reference channel. You can do this by typing `#` then searching for the proper channel." + ); + } + } else { + channel.send("You must use this command in a server."); + } + } + }) + }), + message: new NamedCommand({ + description: + "Sets a custom welcome message for your server. Use `%user%` as the placeholder for the user.", + usage: "()", + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + Storage.getGuild(guild.id).welcomeMessage = null; + Storage.save(); + channel.send("Reset your server's welcome message to the default."); + } else { + channel.send("You must use this command in a server."); + } + }, + any: new Command({ + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + const message = args.join(" "); + Storage.getGuild(guild.id).welcomeMessage = message; + Storage.save(); + channel.send(`Set your server's welcome message to \`${message}\`.`); + } else { + channel.send("You must use this command in a server."); + } + } + }) + }) + } }) } }), @@ -197,6 +304,19 @@ export default new NamedCommand({ ); } }) + }), + syslog: new NamedCommand({ + description: "Sets up the current channel to receive system logs.", + permission: PERMISSIONS.BOT_ADMIN, + async run({message, channel, guild, author, member, client, args}) { + if (guild) { + Config.systemLogsChannel = channel.id; + Config.save(); + channel.send(`Successfully set ${channel} as the system logs channel.`); + } else { + channel.send("DM system log channels aren't supported."); + } + } }) } }); diff --git a/src/commands/utility/calc.ts b/src/commands/utility/calc.ts index a3fe45e..2303351 100644 --- a/src/commands/utility/calc.ts +++ b/src/commands/utility/calc.ts @@ -1,26 +1,26 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; import * as math from "mathjs"; import {MessageEmbed} from "discord.js"; -export default new Command({ +export default new NamedCommand({ description: "Calculates a specified math expression.", - async run($) { - if (!$.args[0]) { - $.channel.send("Please provide a calculation."); + async run({message, channel, guild, author, member, client, args}) { + if (!args[0]) { + channel.send("Please provide a calculation."); return; } let resp; try { - resp = math.evaluate($.args.join(" ")); + resp = math.evaluate(args.join(" ")); } catch (e) { - $.channel.send("Please provide a *valid* calculation."); + channel.send("Please provide a *valid* calculation."); return; } const embed = new MessageEmbed() .setColor(0xffffff) .setTitle("Math Calculation") - .addField("Input", `\`\`\`js\n${$.args.join("")}\`\`\``) + .addField("Input", `\`\`\`js\n${args.join("")}\`\`\``) .addField("Output", `\`\`\`js\n${resp}\`\`\``); - $.channel.send(embed); + channel.send(embed); } }); diff --git a/src/commands/utility/code.ts b/src/commands/utility/code.ts index b473b57..39e41b7 100644 --- a/src/commands/utility/code.ts +++ b/src/commands/utility/code.ts @@ -1,6 +1,6 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; -export default new Command({ +export default new NamedCommand({ description: "Gives you the Github link.", run: "https://github.com/keanuplayz/TravBot-v3" }); diff --git a/src/commands/utility/invite.ts b/src/commands/utility/invite.ts index 7f1fc94..5d5657c 100644 --- a/src/commands/utility/invite.ts +++ b/src/commands/utility/invite.ts @@ -1,11 +1,11 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; -export default new Command({ +export default new NamedCommand({ description: "Gives you the invite link.", - async run($) { - $.channel.send( - `https://discordapp.com/api/oauth2/authorize?client_id=${$.client.user!.id}&permissions=${ - $.args[0] || 8 + async run({message, channel, guild, author, member, client, args}) { + channel.send( + `https://discordapp.com/api/oauth2/authorize?client_id=${client.user!.id}&permissions=${ + args[0] || 8 }&scope=bot` ); } diff --git a/src/commands/utility/todo.ts b/src/commands/utility/todo.ts index 1f6c639..594610b 100644 --- a/src/commands/utility/todo.ts +++ b/src/commands/utility/todo.ts @@ -1,13 +1,13 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; import moment from "moment"; -import {Storage} from "../../core/structures"; +import {Storage} from "../../structures"; import {MessageEmbed} from "discord.js"; -export default new Command({ +export default new NamedCommand({ description: "Keep and edit your personal todo list.", - async run($) { - const user = Storage.getUser($.author.id); - const embed = new MessageEmbed().setTitle(`Todo list for ${$.author.tag}`).setColor("BLUE"); + async run({message, channel, guild, author, member, client, args}) { + const user = Storage.getUser(author.id); + const embed = new MessageEmbed().setTitle(`Todo list for ${author.tag}`).setColor("BLUE"); for (const timestamp in user.todoList) { const date = new Date(Number(timestamp)); @@ -17,23 +17,23 @@ export default new Command({ ); } - $.channel.send(embed); + channel.send(embed); }, subcommands: { - add: new Command({ - async run($) { - const user = Storage.getUser($.author.id); - const note = $.args.join(" "); + add: new NamedCommand({ + async run({message, channel, guild, author, member, client, args}) { + const user = Storage.getUser(author.id); + const note = args.join(" "); user.todoList[Date.now().toString()] = note; console.debug(user.todoList); Storage.save(); - $.channel.send(`Successfully added \`${note}\` to your todo list.`); + channel.send(`Successfully added \`${note}\` to your todo list.`); } }), - remove: new Command({ - async run($) { - const user = Storage.getUser($.author.id); - const note = $.args.join(" "); + remove: new NamedCommand({ + async run({message, channel, guild, author, member, client, args}) { + const user = Storage.getUser(author.id); + const note = args.join(" "); let isFound = false; for (const timestamp in user.todoList) { @@ -43,19 +43,19 @@ export default new Command({ delete user.todoList[timestamp]; Storage.save(); isFound = true; - $.channel.send(`Removed \`${note}\` from your todo list.`); + channel.send(`Removed \`${note}\` from your todo list.`); } } - if (!isFound) $.channel.send("That item couldn't be found."); + if (!isFound) channel.send("That item couldn't be found."); } }), - clear: new Command({ - async run($) { - const user = Storage.getUser($.author.id); + clear: new NamedCommand({ + async run({message, channel, guild, author, member, client, args}) { + const user = Storage.getUser(author.id); user.todoList = {}; Storage.save(); - $.channel.send("Cleared todo list."); + channel.send("Cleared todo list."); } }) } diff --git a/src/commands/utility/translate.ts b/src/commands/utility/translate.ts index 2e56087..b697855 100644 --- a/src/commands/utility/translate.ts +++ b/src/commands/utility/translate.ts @@ -1,18 +1,18 @@ -import Command from "../../core/command"; +import {Command, NamedCommand} from "../../core"; // Anycasting Alert const translate = require("translate-google"); -export default new Command({ +export default new NamedCommand({ description: "Translates your input.", usage: " ", - async run($) { - const lang = $.args[0]; - const input = $.args.slice(1).join(" "); + async run({message, channel, guild, author, member, client, args}) { + const lang = args[0]; + const input = args.slice(1).join(" "); translate(input, { to: lang }) .then((res: any) => { - $.channel.send({ + channel.send({ embed: { title: "Translation", fields: [ @@ -30,7 +30,7 @@ export default new Command({ }) .catch((err: any) => { console.error(err); - $.channel.send( + channel.send( `${err}\nPlease use the following list: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes` ); }); diff --git a/src/events/guildCreate.ts b/src/events/guildCreate.ts deleted file mode 100644 index 02189b3..0000000 --- a/src/events/guildCreate.ts +++ /dev/null @@ -1,32 +0,0 @@ -import Event from "../core/event"; -import $ from "../core/lib"; -import {updateGlobalEmoteRegistry} from "../core/lib"; -import {client} from "../index"; -import {Config} from "../core/structures"; -import {TextChannel} from "discord.js"; - -export default new Event<"guildCreate">({ - on(guild) { - $.log( - `[GUILD JOIN] ${guild.name} (${guild.id}) added the bot. Owner: ${guild.owner!.user.tag} (${ - guild.owner!.user.id - }). Updated emote registry.` - ); - - if (Config.systemLogsChannel) { - const channel = client.channels.cache.get(Config.systemLogsChannel); - - if (channel && channel.type === "text") { - (channel as TextChannel).send( - `TravBot joined: \`${guild.name}\`. The owner of this guild is: \`${guild.owner!.user.tag}\` (\`${ - guild.owner!.user.id - }\`)` - ); - } else { - console.warn(`${Config.systemLogsChannel} is not a valid text channel for system logs!`); - } - } - - updateGlobalEmoteRegistry(); - } -}); diff --git a/src/events/guildDelete.ts b/src/events/guildDelete.ts deleted file mode 100644 index 41c656f..0000000 --- a/src/events/guildDelete.ts +++ /dev/null @@ -1,24 +0,0 @@ -import Event from "../core/event"; -import $ from "../core/lib"; -import {updateGlobalEmoteRegistry} from "../core/lib"; -import {client} from "../index"; -import {Config} from "../core/structures"; -import {TextChannel} from "discord.js"; - -export default new Event<"guildDelete">({ - on(guild) { - $.log(`[GUILD LEAVE] ${guild.name} (${guild.id}) removed the bot. Updated emote registry.`); - - if (Config.systemLogsChannel) { - const channel = client.channels.cache.get(Config.systemLogsChannel); - - if (channel && channel.type === "text") { - (channel as TextChannel).send(`\`${guild.name}\` (\`${guild.id}\`) removed the bot.`); - } else { - console.warn(`${Config.systemLogsChannel} is not a valid text channel for system logs!`); - } - } - - updateGlobalEmoteRegistry(); - } -}); diff --git a/src/events/guildMemberAdd.ts b/src/events/guildMemberAdd.ts deleted file mode 100644 index 1855094..0000000 --- a/src/events/guildMemberAdd.ts +++ /dev/null @@ -1,77 +0,0 @@ -import Event from "../core/event"; -import $, {parseVars} from "../core/lib"; -import {createCanvas, loadImage, Canvas} from "canvas"; -import {Storage} from "../core/structures"; -import {TextChannel, MessageAttachment} from "discord.js"; - -function applyText(canvas: Canvas, text: string) { - const ctx = canvas.getContext("2d"); - let fontSize = 70; - - do { - ctx.font = `${(fontSize -= 10)}px sans-serif`; - } while (ctx.measureText(text).width > canvas.width - 300); - - return ctx.font; -} - -export default new Event<"guildMemberAdd">({ - async on(member) { - const {welcomeType, welcomeChannel, welcomeMessage} = Storage.getGuild(member.guild.id); - - if (welcomeChannel) { - const channel = member.guild.channels.cache.get(welcomeChannel); - - if (channel && channel.type === "text") { - if (welcomeType === "graphical") { - const canvas = createCanvas(700, 250); - const ctx = canvas.getContext("2d"); - const background = await loadImage( - "https://raw.githubusercontent.com/keanuplayz/TravBot/dev/assets/wallpaper.png" - ); - ctx.drawImage(background, 0, 0, canvas.width, canvas.height); - - ctx.strokeStyle = "#74037b"; - ctx.strokeRect(0, 0, canvas.width, canvas.height); - - ctx.font = "28px sans-serif"; - ctx.fillStyle = "#ffffff"; - ctx.fillText("Welcome to the server,", canvas.width / 2.5, canvas.height / 3.5); - - ctx.font = applyText(canvas, member.displayName); - ctx.fillStyle = "#ffffff"; - ctx.fillText(`${member.displayName}!`, canvas.width / 2.5, canvas.height / 1.5); - - ctx.beginPath(); - ctx.arc(125, 125, 100, 0, Math.PI * 2, true); - ctx.closePath(); - ctx.clip(); - - const avatarURL = - member.user.avatarURL({ - dynamic: true, - size: 2048, - format: "png" - }) ?? member.user.defaultAvatarURL; - const avatar = await loadImage(avatarURL); - ctx.drawImage(avatar, 25, 25, 200, 200); - - const attachment = new MessageAttachment(canvas.toBuffer("image/png"), "welcome-image.png"); - (channel as TextChannel).send(`Welcome \`${member.user.tag}\`!`, attachment); - } else if (welcomeType === "text") { - (channel as TextChannel).send( - parseVars( - welcomeMessage || - "Say hello to `%user%`, everyone! We all need a warm welcome sometimes :D", - { - user: member.user.tag - } - ) - ); - } - } else { - $.error(`"${welcomeChannel}" is not a valid text channel ID!`); - } - } - } -}); diff --git a/src/events/ready.ts b/src/events/ready.ts deleted file mode 100644 index b8467aa..0000000 --- a/src/events/ready.ts +++ /dev/null @@ -1,20 +0,0 @@ -import Event from "../core/event"; -import {client} from "../index"; -import $ from "../core/lib"; -import {Config} from "../core/structures"; -import {updateGlobalEmoteRegistry} from "../core/lib"; - -export default new Event<"ready">({ - once() { - if (client.user) { - $.ready( - `Logged in as ${client.user.tag}, ready to serve ${client.users.cache.size} users in ${client.guilds.cache.size} servers..` - ); - client.user.setActivity({ - type: "LISTENING", - name: `${Config.prefix}help` - }); - } - updateGlobalEmoteRegistry(); - } -}); diff --git a/src/index.ts b/src/index.ts index 21633ce..67cacb5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -69,3 +69,4 @@ import "./modules/emoteRegistry"; import "./modules/channelListener"; import "./modules/intercept"; import "./modules/messageEmbed"; +import "./modules/guildMemberAdd"; diff --git a/src/modules/emoteRegistry.ts b/src/modules/emoteRegistry.ts index a3de8bc..88ab099 100644 --- a/src/modules/emoteRegistry.ts +++ b/src/modules/emoteRegistry.ts @@ -1,6 +1,7 @@ import {client} from "../index"; import FileManager from "./storage"; -import {EmoteRegistryDump} from "../structures"; +import {EmoteRegistryDump, Config} from "../structures"; +import {TextChannel} from "discord.js"; function updateGlobalEmoteRegistry(): void { const data: EmoteRegistryDump = {version: 1, list: []}; @@ -38,13 +39,43 @@ client.on("emojiUpdate", () => { updateGlobalEmoteRegistry(); }); -client.on("guildCreate", () => { - console.log("Updated emote registry."); +client.on("guildCreate", (guild) => { + console.log( + `[GUILD JOIN] ${guild.name} (${guild.id}) added the bot. Owner: ${guild.owner!.user.tag} (${ + guild.owner!.user.id + }). Updated emote registry.` + ); + + if (Config.systemLogsChannel) { + const channel = client.channels.cache.get(Config.systemLogsChannel); + + if (channel && channel.type === "text") { + (channel as TextChannel).send( + `TravBot joined: \`${guild.name}\`. The owner of this guild is: \`${guild.owner!.user.tag}\` (\`${ + guild.owner!.user.id + }\`)` + ); + } else { + console.warn(`${Config.systemLogsChannel} is not a valid text channel for system logs!`); + } + } + updateGlobalEmoteRegistry(); }); -client.on("guildDelete", () => { - console.log("Updated emote registry."); +client.on("guildDelete", (guild) => { + console.log(`[GUILD LEAVE] ${guild.name} (${guild.id}) removed the bot. Updated emote registry.`); + + if (Config.systemLogsChannel) { + const channel = client.channels.cache.get(Config.systemLogsChannel); + + if (channel && channel.type === "text") { + (channel as TextChannel).send(`\`${guild.name}\` (\`${guild.id}\`) removed the bot.`); + } else { + console.warn(`${Config.systemLogsChannel} is not a valid text channel for system logs!`); + } + } + updateGlobalEmoteRegistry(); }); diff --git a/src/modules/guildMemberAdd.ts b/src/modules/guildMemberAdd.ts new file mode 100644 index 0000000..9a26ffd --- /dev/null +++ b/src/modules/guildMemberAdd.ts @@ -0,0 +1,74 @@ +import {createCanvas, loadImage, Canvas} from "canvas"; +import {TextChannel, MessageAttachment} from "discord.js"; +import {parseVars} from "../lib"; +import {Storage} from "../structures"; +import {client} from "../index"; + +function applyText(canvas: Canvas, text: string) { + const ctx = canvas.getContext("2d"); + let fontSize = 70; + + do { + ctx.font = `${(fontSize -= 10)}px sans-serif`; + } while (ctx.measureText(text).width > canvas.width - 300); + + return ctx.font; +} + +client.on("guildMemberAdd", async (member) => { + const {welcomeType, welcomeChannel, welcomeMessage} = Storage.getGuild(member.guild.id); + + if (welcomeChannel) { + const channel = member.guild.channels.cache.get(welcomeChannel); + + if (channel && channel.type === "text") { + if (welcomeType === "graphical") { + const canvas = createCanvas(700, 250); + const ctx = canvas.getContext("2d"); + const background = await loadImage( + "https://raw.githubusercontent.com/keanuplayz/TravBot/dev/assets/wallpaper.png" + ); + ctx.drawImage(background, 0, 0, canvas.width, canvas.height); + + ctx.strokeStyle = "#74037b"; + ctx.strokeRect(0, 0, canvas.width, canvas.height); + + ctx.font = "28px sans-serif"; + ctx.fillStyle = "#ffffff"; + ctx.fillText("Welcome to the server,", canvas.width / 2.5, canvas.height / 3.5); + + ctx.font = applyText(canvas, member.displayName); + ctx.fillStyle = "#ffffff"; + ctx.fillText(`${member.displayName}!`, canvas.width / 2.5, canvas.height / 1.5); + + ctx.beginPath(); + ctx.arc(125, 125, 100, 0, Math.PI * 2, true); + ctx.closePath(); + ctx.clip(); + + const avatarURL = + member.user.avatarURL({ + dynamic: true, + size: 2048, + format: "png" + }) ?? member.user.defaultAvatarURL; + const avatar = await loadImage(avatarURL); + ctx.drawImage(avatar, 25, 25, 200, 200); + + const attachment = new MessageAttachment(canvas.toBuffer("image/png"), "welcome-image.png"); + (channel as TextChannel).send(`Welcome \`${member.user.tag}\`!`, attachment); + } else if (welcomeType === "text") { + (channel as TextChannel).send( + parseVars( + welcomeMessage || "Say hello to `%user%`, everyone! We all need a warm welcome sometimes :D", + { + user: member.user.tag + } + ) + ); + } + } else { + console.error(`"${welcomeChannel}" is not a valid text channel ID!`); + } + } +}); diff --git a/src/modules/ready.ts b/src/modules/ready.ts index 9c3c86b..eac1fa0 100644 --- a/src/modules/ready.ts +++ b/src/modules/ready.ts @@ -3,7 +3,9 @@ import {Config} from "../structures"; client.once("ready", () => { if (client.user) { - console.ready(`Logged in as ${client.user.tag}.`); + console.ready( + `Logged in as ${client.user.tag}, ready to serve ${client.users.cache.size} users in ${client.guilds.cache.size} servers..` + ); client.user.setActivity({ type: "LISTENING", name: `${Config.prefix}help`