diff options
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/commands/admin/prefix.ts | 8 | ||||
-rw-r--r-- | src/commands/moderation/ban.ts | 26 | ||||
-rw-r--r-- | src/commands/moderation/kick.ts | 18 | ||||
-rw-r--r-- | src/commands/moderation/modlog.ts | 7 | ||||
-rw-r--r-- | src/commands/moderation/warn.ts | 18 | ||||
-rw-r--r-- | src/config/example-options.ts | 1 | ||||
-rw-r--r-- | src/lib/extensions/BotClient.ts | 40 | ||||
-rw-r--r-- | src/lib/extensions/BotCommand.ts | 5 | ||||
-rw-r--r-- | src/lib/extensions/Util.ts | 49 | ||||
-rw-r--r-- | src/lib/utils/Logger.ts | 44 | ||||
-rw-r--r-- | src/listeners/client/CreateSlashCommands.ts | 17 | ||||
-rw-r--r-- | src/listeners/client/ready.ts | 6 | ||||
-rw-r--r-- | src/listeners/commands/commandstarted.ts | 24 | ||||
-rw-r--r-- | src/listeners/message/levels.ts | 8 | ||||
-rw-r--r-- | src/tasks.ts | 7 | ||||
-rw-r--r-- | yarn.lock | 2 |
17 files changed, 188 insertions, 93 deletions
diff --git a/package.json b/package.json index 67b772f..ad755a0 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "dependencies": { "@top-gg/sdk": "^3.0.9", "body-parser": "^1.19.0", + "chalk": "^4.1.1", "common-tags": "^1.8.0", "discord-akairo": "SkyBlockDev/discord-akairo", "discord-api-types": "^0.18.1", diff --git a/src/commands/admin/prefix.ts b/src/commands/admin/prefix.ts index 8fb50f8..3948a7e 100644 --- a/src/commands/admin/prefix.ts +++ b/src/commands/admin/prefix.ts @@ -10,7 +10,13 @@ export default class PrefixCommand extends BotCommand { id: 'prefix' } ], - userPermissions: ['MANAGE_GUILD'] + userPermissions: ['MANAGE_GUILD'], + description: { + content: + 'Set the prefix of the current server (resets to default if prefix is not given)', + usage: 'prefix [prefix]', + examples: ['prefix', 'prefix +'] + } }); } async exec( diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts index fc861dc..7ce36d3 100644 --- a/src/commands/moderation/ban.ts +++ b/src/commands/moderation/ban.ts @@ -1,4 +1,5 @@ import { User } from 'discord.js'; +import { Guild } from '../../lib/models'; import { BotCommand } from '../../lib/extensions/BotCommand'; import { BotMessage } from '../../lib/extensions/BotMessage'; import { Ban, Modlog, ModlogType } from '../../lib/models'; @@ -27,7 +28,8 @@ export default class PrefixCommand extends BotCommand { } }, { - id: 'reason' + id: 'reason', + match: 'rest' }, { id: 'time', @@ -36,7 +38,16 @@ export default class PrefixCommand extends BotCommand { } ], clientPermissions: ['BAN_MEMBERS'], - userPermissions: ['BAN_MEMBERS'] + userPermissions: ['BAN_MEMBERS'], + description: { + content: + 'Ban a member and log it in modlogs (with optional time to unban)', + usage: 'ban <member> <reason> [--time]', + examples: [ + 'ban @Tyman being cool', + 'ban @Tyman being cool --time 7days' + ] + } }); } async exec( @@ -47,6 +58,15 @@ export default class PrefixCommand extends BotCommand { let modlogEnry: Modlog; let banEntry: Ban; const translatedTime: string[] = []; + // Create guild entry so postgres doesn't get mad when I try and add a modlog entry + await Guild.findOrCreate({ + where: { + id: message.guild.id + }, + defaults: { + id: message.guild.id + } + }); try { try { if (time) { @@ -129,8 +149,8 @@ export default class PrefixCommand extends BotCommand { ); } catch { await message.util.send('Error banning :/'); - await modlogEnry.destroy(); await banEntry.destroy(); + await modlogEnry.destroy(); return; } } diff --git a/src/commands/moderation/kick.ts b/src/commands/moderation/kick.ts index dcd19de..23fc092 100644 --- a/src/commands/moderation/kick.ts +++ b/src/commands/moderation/kick.ts @@ -1,6 +1,6 @@ import { BotCommand } from '../../lib/extensions/BotCommand'; import { BotMessage } from '../../lib/extensions/BotMessage'; -import { Modlog, ModlogType } from '../../lib/models'; +import { Guild, Modlog, ModlogType } from '../../lib/models'; import { GuildMember } from 'discord.js'; export default class PrefixCommand extends BotCommand { @@ -21,7 +21,12 @@ export default class PrefixCommand extends BotCommand { } ], clientPermissions: ['KICK_MEMBERS'], - userPermissions: ['KICK_MEMBERS'] + userPermissions: ['KICK_MEMBERS'], + description: { + content: 'Kick a member and log it in modlogs', + usage: 'kick <member> <reason>', + examples: ['kick @Tyman being cool'] + } }); } async exec( @@ -29,6 +34,15 @@ export default class PrefixCommand extends BotCommand { { user, reason }: { user: GuildMember; reason?: string } ): Promise<void> { let modlogEnry: Modlog; + // Create guild entry so postgres doesn't get mad when I try and add a modlog entry + await Guild.findOrCreate({ + where: { + id: message.guild.id + }, + defaults: { + id: message.guild.id + } + }); try { modlogEnry = Modlog.build({ user: user.id, diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts index dbb101c..320c6b4 100644 --- a/src/commands/moderation/modlog.ts +++ b/src/commands/moderation/modlog.ts @@ -22,7 +22,12 @@ export default class ModlogCommand extends BotCommand { type: 'number' } ], - userPermissions: ['MANAGE_MESSAGES'] + userPermissions: ['MANAGE_MESSAGES'], + description: { + content: "View a user's modlogs, or view a specific modlog entry", + usage: 'warn <search> [page]', + examples: ['modlogs @Tyman', 'modlogs @Tyman 3'] + } }); } *args(): unknown { diff --git a/src/commands/moderation/warn.ts b/src/commands/moderation/warn.ts index 755a036..98ba4bd 100644 --- a/src/commands/moderation/warn.ts +++ b/src/commands/moderation/warn.ts @@ -1,7 +1,7 @@ import { GuildMember } from 'discord.js'; import { BotCommand } from '../../lib/extensions/BotCommand'; import { BotMessage } from '../../lib/extensions/BotMessage'; -import { Modlog, ModlogType } from '../../lib/models'; +import { Guild, Modlog, ModlogType } from '../../lib/models'; export default class WarnCommand extends BotCommand { public constructor() { @@ -17,13 +17,27 @@ export default class WarnCommand extends BotCommand { id: 'reason', match: 'rest' } - ] + ], + description: { + content: 'Warn a member and log it in modlogs', + usage: 'warn <member> <reason>', + examples: ['warn @Tyman being cool'] + } }); } public async exec( message: BotMessage, { member, reason }: { member: GuildMember; reason: string } ): Promise<void> { + // Create guild entry so postgres doesn't get mad when I try and add a modlog entry + await Guild.findOrCreate({ + where: { + id: message.guild.id + }, + defaults: { + id: message.guild.id + } + }); try { const entry = Modlog.build({ user: member.id, diff --git a/src/config/example-options.ts b/src/config/example-options.ts index 2da1374..5bfb92f 100644 --- a/src/config/example-options.ts +++ b/src/config/example-options.ts @@ -17,6 +17,7 @@ export const channels = { dm: 'id here', command: 'id here' }; +export const verbose = false; // Database specific export const db = { diff --git a/src/lib/extensions/BotClient.ts b/src/lib/extensions/BotClient.ts index e2de812..99b40ef 100644 --- a/src/lib/extensions/BotClient.ts +++ b/src/lib/extensions/BotClient.ts @@ -15,27 +15,11 @@ import * as Tasks from '../../tasks'; import { v4 as uuidv4 } from 'uuid'; import { exit } from 'process'; import { Intents } from 'discord.js'; +import * as config from '../../config/options'; +import { Logger } from '../utils/Logger'; +import chalk from 'chalk'; -export interface BotConfig { - credentials: { - botToken: string; - }; - owners: string[]; - prefix: string; - dev: boolean; - db: { - username: string; - password: string; - host: string; - port: number; - }; - channels: { - log: string; - error: string; - dm: string; - command: string; - }; -} +export type BotConfig = typeof config; export class BotClient extends AkairoClient { public config: BotConfig; @@ -45,6 +29,7 @@ export class BotClient extends AkairoClient { public util: Util; public ownerID: string[]; public db: Sequelize; + public logger: Logger; constructor(config: BotConfig) { super( { @@ -114,6 +99,7 @@ export class BotClient extends AkairoClient { ); BotGuild.install(); BotMessage.install(); + this.logger = new Logger(this); } // Initialize everything @@ -134,14 +120,20 @@ export class BotClient extends AkairoClient { for (const loader of Object.keys(loaders)) { try { loaders[loader].loadAll(); - console.log('Successfully loaded ' + loader + '.'); + this.logger.log( + chalk.green('Successfully loaded ' + chalk.cyan(loader) + '.') + ); } catch (e) { - console.error('Unable to load loader ' + loader + ' with error ' + e); + console.error( + chalk.red( + 'Unable to load loader ' + chalk.cyan(loader) + ' with error ' + e + ) + ); } } await this.dbPreInit(); Object.keys(Tasks).forEach((t) => { - setInterval(() => Tasks[t](this), 60000); + setInterval(() => Tasks[t](this), 30000); }); } @@ -270,7 +262,7 @@ export class BotClient extends AkairoClient { await this._init(); await this.login(this.token); } catch (e) { - console.error(e.stack); + console.error(chalk.red(e.stack)); exit(2); } } diff --git a/src/lib/extensions/BotCommand.ts b/src/lib/extensions/BotCommand.ts index 2db93b0..c5d31e9 100644 --- a/src/lib/extensions/BotCommand.ts +++ b/src/lib/extensions/BotCommand.ts @@ -4,6 +4,11 @@ import { BotClient } from './BotClient'; export interface BotCommandOptions extends CommandOptions { slashCommandOptions?: APIApplicationCommandOption[]; + description: { + content: string; + usage: string; + examples: string[]; + }; } export class BotCommand extends Command { diff --git a/src/lib/extensions/Util.ts b/src/lib/extensions/Util.ts index 4243ebf..661392f 100644 --- a/src/lib/extensions/Util.ts +++ b/src/lib/extensions/Util.ts @@ -1,12 +1,9 @@ import { ClientUtil } from 'discord-akairo'; import { BotClient } from './BotClient'; -import { User } from 'discord.js'; import { promisify } from 'util'; import { exec } from 'child_process'; import got from 'got'; -import { TextChannel } from 'discord.js'; -import { MessageEmbed } from 'discord.js'; -import { GuildMember } from 'discord.js'; +import { MessageEmbed, GuildMember, User } from 'discord.js'; interface hastebinRes { key: string; @@ -120,14 +117,6 @@ export class Util extends ClientUtil { } /** - * Logs something but only in dev mode - * @param content The thing to log - */ - public devLog(content: unknown): void { - if (this.client.config.dev) console.log(content); - } - - /** * Resolves a user-provided string into a user object, if possible * @param text The text to try and resolve * @returns The user resolved or null @@ -184,42 +173,6 @@ export class Util extends ClientUtil { } /** - * Logs a message to console and log channel as info - * @param message The message to send - */ - public async info(message: string): Promise<void> { - console.log(`INFO: ${message}`); - const channel = (await this.client.channels.fetch( - this.client.config.channels.log - )) as TextChannel; - await channel.send(`INFO: ${message}`); - } - - /** - * Logs a message to console and log channel as a warning - * @param message The message to send - */ - public async warn(message: string): Promise<void> { - console.warn(`WARN: ${message}`); - const channel = (await this.client.channels.fetch( - this.client.config.channels.log - )) as TextChannel; - await channel.send(`WARN: ${message}`); - } - - /** - * Logs a message to console and log channel as an error - * @param message The message to send - */ - public async error(message: string): Promise<void> { - console.error(`ERROR: ${message}`); - const channel = (await this.client.channels.fetch( - this.client.config.channels.error - )) as TextChannel; - await channel.send(`ERROR: ${message}`); - } - - /** * The colors used throught the bot */ public colors = { diff --git a/src/lib/utils/Logger.ts b/src/lib/utils/Logger.ts new file mode 100644 index 0000000..455ba36 --- /dev/null +++ b/src/lib/utils/Logger.ts @@ -0,0 +1,44 @@ +import { TextChannel } from 'discord.js'; +import { BotClient } from '../extensions/BotClient'; +import chalk from 'chalk'; + +export class Logger { + private client: BotClient; + public constructor(client: BotClient) { + this.client = client; + } + private stripColor(text: string): string { + return text.replace( + // eslint-disable-next-line no-control-regex + /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, + '' + ); + } + public getChannel(channel: 'log' | 'error' | 'dm'): Promise<TextChannel> { + return this.client.channels.fetch( + this.client.config.channels[channel] + ) as Promise<TextChannel>; + } + public async log(message: string, sendChannel = false): Promise<void> { + console.log(chalk`{bgCyan LOG} ` + message); + if (sendChannel) { + const channel = await this.getChannel('log'); + await channel.send('[LOG] ' + this.stripColor(message)); + } + } + public async verbose(message: string, sendChannel = false): Promise<void> { + if (!this.client.config.verbose) return; + console.log(chalk`{bgMagenta VERBOSE} ` + message); + if (sendChannel) { + const channel = await this.getChannel('log'); + await channel.send('[VERBOSE] ' + this.stripColor(message)); + } + } + public async error(message: string, sendChannel = false): Promise<void> { + console.log(chalk`{bgRed ERROR} ` + message); + if (sendChannel) { + const channel = await this.getChannel('error'); + await channel.send('[ERROR] ' + this.stripColor(message)); + } + } +} diff --git a/src/listeners/client/CreateSlashCommands.ts b/src/listeners/client/CreateSlashCommands.ts index b860bec..c395b3a 100644 --- a/src/listeners/client/CreateSlashCommands.ts +++ b/src/listeners/client/CreateSlashCommands.ts @@ -1,3 +1,4 @@ +import chalk from 'chalk'; import { BotListener } from '../../lib/extensions/BotListener'; export default class CreateSlashCommands extends BotListener { @@ -17,7 +18,9 @@ export default class CreateSlashCommands extends BotListener { ) ) { await this.client.application.commands.delete(command[1].id); - console.log('deleted', command[1].name); + this.client.logger.verbose( + `{red Deleted slash command ${command[1].name}}` + ); } } @@ -36,16 +39,20 @@ export default class CreateSlashCommands extends BotListener { await this.client.application.commands.edit(found.id, slashdata); } } else { - console.log('enabled', cmd[1].id); + this.client.logger.verbose( + `{red Deleted slash command ${cmd[1].id}}` + ); await this.client.application.commands.create(slashdata); } } } - return console.log('Slash commands registered'); + return this.client.logger.log(chalk.green('Slash commands registered')); } catch (e) { - console.log(e); - return console.log('Slash commands not registered, see above error.'); + console.log(chalk.red(e)); + return this.client.logger.error( + '{red Slash commands not registered, see above error.}' + ); } } } diff --git a/src/listeners/client/ready.ts b/src/listeners/client/ready.ts index ae510f6..fc43f3c 100644 --- a/src/listeners/client/ready.ts +++ b/src/listeners/client/ready.ts @@ -1,3 +1,4 @@ +import chalk from 'chalk'; import { BotListener } from '../../lib/extensions/BotListener'; export default class CommandBlockedListener extends BotListener { @@ -9,8 +10,9 @@ export default class CommandBlockedListener extends BotListener { } public async exec(): Promise<void> { - await this.client.util.info( - `Sucessfully logged in as ${this.client.user.tag}` + await this.client.logger.log( + chalk`{green Sucessfully logged in as {cyan ${this.client.user.tag}}.}`, + true ); } } diff --git a/src/listeners/commands/commandstarted.ts b/src/listeners/commands/commandstarted.ts new file mode 100644 index 0000000..15eea9d --- /dev/null +++ b/src/listeners/commands/commandstarted.ts @@ -0,0 +1,24 @@ +import chalk from 'chalk'; +import { Message, DMChannel } from 'discord.js'; +import { BotCommand } from '../../lib/extensions/BotCommand'; +import { BotListener } from '../../lib/extensions/BotListener'; + +export default class CommandStartedListener extends BotListener { + constructor() { + super('logCommands', { + emitter: 'commandHandler', + event: 'commandStarted' + }); + } + exec(message: Message, command: BotCommand): void { + this.client.logger.verbose( + chalk`{cyan {green ${message.author.tag}} is running {green ${ + command.aliases[0] + }} in {green ${ + message.channel instanceof DMChannel + ? 'DMs' + : `#${message.channel.name} (Server: ${message.guild.name})` + }}.}` + ); + } +} diff --git a/src/listeners/message/levels.ts b/src/listeners/message/levels.ts index 0767286..9a5fbe8 100644 --- a/src/listeners/message/levels.ts +++ b/src/listeners/message/levels.ts @@ -1,3 +1,4 @@ +import chalk from 'chalk'; import { Message } from 'discord.js'; import { BotListener } from '../../lib/extensions/BotListener'; import { Level } from '../../lib/models'; @@ -15,7 +16,8 @@ export default class LevelListener extends BotListener { if (message.author.bot) return; if (message.util?.parsed?.command) return; if (this.levelCooldowns.has(message.author.id)) return; - if (message.guild.id != '516977525906341928') return; + if (!this.client.config.dev && message.guild.id != '516977525906341928') + return; if (this.blacklistedChannels.includes(message.channel.id)) return; const [user] = await Level.findOrBuild({ where: { @@ -28,7 +30,9 @@ export default class LevelListener extends BotListener { const xpToGive = Level.genRandomizedXp(); user.xp += xpToGive; await user.save(); - console.log(`Gave XP to ${message.author.tag}: ${xpToGive}xp`); + await this.client.logger.verbose( + chalk`{cyan Gave XP to {green ${message.author.tag}}: {green ${xpToGive}xp}.}` + ); this.levelCooldowns.add(message.author.id); setTimeout(() => this.levelCooldowns.delete(message.author.id), 60_000); } diff --git a/src/tasks.ts b/src/tasks.ts index 69fe97a..d728636 100644 --- a/src/tasks.ts +++ b/src/tasks.ts @@ -1,3 +1,4 @@ +import chalk from 'chalk'; import { DiscordAPIError } from 'discord.js'; import { Op } from 'sequelize'; import { BotClient } from './lib/extensions/BotClient'; @@ -15,7 +16,9 @@ export const BanTask = async (client: BotClient): Promise<void> => { ] } }); - client.util.devLog(`Queried bans, found ${rows.length} expired bans.`); + client.logger.verbose( + chalk.cyan(`Queried bans, found ${rows.length} expired bans.`) + ); for (const row of rows) { const guild = client.guilds.cache.get(row.guild); if (!guild) { @@ -33,6 +36,6 @@ export const BanTask = async (client: BotClient): Promise<void> => { } else throw e; } await row.destroy(); - client.util.devLog('Unbanned user'); + client.logger.verbose(chalk.cyan('Unbanned user')); } }; @@ -458,7 +458,7 @@ chalk@^2.0.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0: +chalk@^4.0.0, chalk@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== |