From d055e0dbb86ef7fd4ee96a1531b51181e825fb4b Mon Sep 17 00:00:00 2001 From: IRONM00N <64110067+IRONM00N@users.noreply.github.com> Date: Mon, 14 Jun 2021 22:51:48 -0400 Subject: made a few changes --- src/lib/extensions/BushClient.ts | 37 ++-- src/lib/extensions/BushCommand.ts | 1 + src/lib/extensions/BushCommandHandler.ts | 15 ++ src/lib/extensions/BushInteractionMessage.ts | 15 ++ src/lib/extensions/BushListenerHandler.ts | 6 + src/lib/extensions/BushMessage.ts | 11 ++ src/lib/extensions/BushTaskHandler.ts | 12 ++ src/lib/extensions/Util.ts | 256 +++++++++++++++++++++++---- 8 files changed, 299 insertions(+), 54 deletions(-) create mode 100644 src/lib/extensions/BushCommandHandler.ts create mode 100644 src/lib/extensions/BushInteractionMessage.ts create mode 100644 src/lib/extensions/BushListenerHandler.ts create mode 100644 src/lib/extensions/BushMessage.ts create mode 100644 src/lib/extensions/BushTaskHandler.ts (limited to 'src/lib/extensions') diff --git a/src/lib/extensions/BushClient.ts b/src/lib/extensions/BushClient.ts index 8a0fc8c..e2e889b 100644 --- a/src/lib/extensions/BushClient.ts +++ b/src/lib/extensions/BushClient.ts @@ -1,14 +1,15 @@ -import { AkairoClient, CommandHandler, InhibitorHandler, ListenerHandler, TaskHandler } from 'discord-akairo'; -import { Guild } from 'discord.js'; +import chalk from 'chalk'; +import { AkairoClient, InhibitorHandler, ListenerHandler, TaskHandler } from 'discord-akairo'; +import { Guild, Intents, Snowflake } from 'discord.js'; import * as path from 'path'; -import { Sequelize } from 'sequelize'; -import * as Models from '../models'; -import { Util } from './Util'; import { exit } from 'process'; -import { Intents } from 'discord.js'; +import { Sequelize } from 'sequelize'; import * as config from '../../config/options'; -import { Logger } from '../utils/Logger'; -import chalk from 'chalk'; +import * as Models from '../models'; +import AllowedMentions from '../utils/AllowedMentions'; +import { BushLogger } from '../utils/Logger'; +import { BushCommandHandler } from './BushCommandHandler'; +import { BushUtil } from './Util'; export type BotConfig = typeof config; @@ -16,21 +17,21 @@ export class BushClient extends AkairoClient { public config: BotConfig; public listenerHandler: ListenerHandler; public inhibitorHandler: InhibitorHandler; - public commandHandler: CommandHandler; + public commandHandler: BushCommandHandler; public taskHandler: TaskHandler; - public util: Util; - public ownerID: string[]; + public util: BushUtil; + public ownerID: Snowflake[]; public db: Sequelize; - public logger: Logger; + public logger: BushLogger; constructor(config: BotConfig) { super( { ownerID: config.owners, - intents: Intents.NON_PRIVILEGED + intents: Intents.ALL }, { - allowedMentions: { parse: ['users'] }, // No everyone or role mentions by default - intents: Intents.NON_PRIVILEGED + allowedMentions: AllowedMentions.users(), // No everyone or role mentions by default + intents: Intents.ALL } ); @@ -58,7 +59,7 @@ export class BushClient extends AkairoClient { }); // Create command handler - this.commandHandler = new CommandHandler(this, { + this.commandHandler = new BushCommandHandler(this, { directory: path.join(__dirname, '..', '..', 'commands'), prefix: async ({ guild }: { guild: Guild }) => { const row = await Models.Guild.findByPk(guild.id); @@ -82,14 +83,14 @@ export class BushClient extends AkairoClient { automateCategories: true }); - this.util = new Util(this); + this.util = new BushUtil(this); this.db = new Sequelize(this.config.dev ? 'bushbot-dev' : 'bushbot', this.config.db.username, this.config.db.password, { dialect: 'postgres', host: this.config.db.host, port: this.config.db.port, logging: false }); - this.logger = new Logger(this); + this.logger = new BushLogger(this); } // Initialize everything diff --git a/src/lib/extensions/BushCommand.ts b/src/lib/extensions/BushCommand.ts index 4f9dc6e..2b34c69 100644 --- a/src/lib/extensions/BushCommand.ts +++ b/src/lib/extensions/BushCommand.ts @@ -13,6 +13,7 @@ export interface BushCommandOptions extends CommandOptions { export class BushCommand extends Command { public client: BushClient; + options: BushCommandOptions; constructor(id: string, options?: BushCommandOptions) { super(id, options); this.options = options; diff --git a/src/lib/extensions/BushCommandHandler.ts b/src/lib/extensions/BushCommandHandler.ts new file mode 100644 index 0000000..6ef44d7 --- /dev/null +++ b/src/lib/extensions/BushCommandHandler.ts @@ -0,0 +1,15 @@ +import { CommandHandler, CommandHandlerOptions } from 'discord-akairo'; +import { Collection } from 'discord.js'; +import { BushClient } from './BushClient'; +import { BushCommand } from './BushCommand'; + +export interface BushCommandHandlerOptions extends CommandHandlerOptions {} + +export class BushCommandHandler extends CommandHandler { + public constructor(client: BushClient, options: BushCommandHandlerOptions) { + super(client, options); + this.client = client; + } + + declare modules: Collection; +} diff --git a/src/lib/extensions/BushInteractionMessage.ts b/src/lib/extensions/BushInteractionMessage.ts new file mode 100644 index 0000000..9bdc291 --- /dev/null +++ b/src/lib/extensions/BushInteractionMessage.ts @@ -0,0 +1,15 @@ +import { AkairoMessage } from 'discord-akairo'; +import { CommandInteraction } from 'discord.js'; +import { BushClient } from './BushClient'; + +export class BushInteractionMessage extends AkairoMessage { + public constructor( + client: BushClient, + interaction: CommandInteraction, + { slash, replied }: { slash?: boolean; replied?: boolean } + ) { + super(client, interaction, { slash, replied }); + this.client = client; + this.interaction = interaction; + } +} diff --git a/src/lib/extensions/BushListenerHandler.ts b/src/lib/extensions/BushListenerHandler.ts new file mode 100644 index 0000000..28615fc --- /dev/null +++ b/src/lib/extensions/BushListenerHandler.ts @@ -0,0 +1,6 @@ +import { ListenerHandler } from 'discord-akairo'; +import { BushClient } from './BushClient'; + +export class BushListenerHandler extends ListenerHandler { + declare client: BushClient; +} diff --git a/src/lib/extensions/BushMessage.ts b/src/lib/extensions/BushMessage.ts new file mode 100644 index 0000000..e7146f6 --- /dev/null +++ b/src/lib/extensions/BushMessage.ts @@ -0,0 +1,11 @@ +import { DMChannel, Message, NewsChannel, TextChannel } from 'discord.js'; +import { BushClient } from './BushClient'; + +export class BushMessage extends Message { + declare client: BushClient; + constructor(client: BushClient, data: unknown, channel: TextChannel | DMChannel | NewsChannel) { + super(client, data, channel); + this.client = client; + this.channel = channel; + } +} diff --git a/src/lib/extensions/BushTaskHandler.ts b/src/lib/extensions/BushTaskHandler.ts new file mode 100644 index 0000000..f783eb3 --- /dev/null +++ b/src/lib/extensions/BushTaskHandler.ts @@ -0,0 +1,12 @@ +import { AkairoHandlerOptions, TaskHandler } from 'discord-akairo'; +import { BushClient } from './BushClient'; + +export interface BushTaskHandlerOptions extends AkairoHandlerOptions {} + +export class BushTaskHandler extends TaskHandler { + public constructor(client: BushClient, options: BushTaskHandlerOptions) { + super(client, options); + this.client; + } + declare client: BushClient; +} diff --git a/src/lib/extensions/Util.ts b/src/lib/extensions/Util.ts index 78fba12..3913437 100644 --- a/src/lib/extensions/Util.ts +++ b/src/lib/extensions/Util.ts @@ -1,20 +1,33 @@ -import { ClientUtil } from 'discord-akairo'; -import { BushClient } from './BushClient'; -import { promisify } from 'util'; +import chalk from 'chalk'; import { exec } from 'child_process'; -import got from 'got'; -import { MessageEmbed, GuildMember, User } from 'discord.js'; -import { CommandInteractionOption } from 'discord.js'; +import { ClientUtil, Command } from 'discord-akairo'; import { - ApplicationCommandOptionType, - APIInteractionDataResolvedGuildMember, APIInteractionDataResolvedChannel, - APIRole + APIInteractionDataResolvedGuildMember, + APIRole, + ApplicationCommandOptionType } from 'discord-api-types'; -import { GuildChannel } from 'discord.js'; -import { Role } from 'discord.js'; -import chalk from 'chalk'; -import { Guild } from 'discord.js'; +import { + ButtonInteraction, + CommandInteractionOption, + Constants, + Guild, + GuildChannel, + GuildMember, + MessageActionRow, + MessageButton, + MessageComponentInteraction, + MessageEmbed, + MessageOptions, + Role, + Snowflake, + User, + Util +} from 'discord.js'; +import got from 'got'; +import { promisify } from 'util'; +import { BushClient } from './BushClient'; +import { BushMessage } from './BushMessage'; interface hastebinRes { key: string; @@ -23,11 +36,7 @@ interface hastebinRes { export interface uuidRes { uuid: string; username: string; - username_history?: - | { - username: string; - }[] - | null; + username_history?: { username: string }[] | null; textures: { custom: boolean; slim: boolean; @@ -54,7 +63,7 @@ export interface SlashCommandOption { role?: Role | APIRole; } -export class Util extends ClientUtil { +export class BushUtil extends ClientUtil { /** * The client of this ClientUtil * @type {BushClient} @@ -92,7 +101,7 @@ export class Util extends ClientUtil { * @param ids The list of IDs to map * @returns The list of users mapped */ - public async mapIDs(ids: string[]): Promise { + public async mapIDs(ids: Snowflake[]): Promise { return await Promise.all(ids.map((id) => this.client.users.fetch(id))); } @@ -144,7 +153,7 @@ export class Util extends ClientUtil { const idMatch = text.match(idReg); if (idMatch) { try { - const user = await this.client.users.fetch(text); + const user = await this.client.users.fetch(text as Snowflake); return user; } catch { // pass @@ -154,7 +163,7 @@ export class Util extends ClientUtil { const mentionMatch = text.match(mentionReg); if (mentionMatch) { try { - const user = await this.client.users.fetch(mentionMatch.groups.id); + const user = await this.client.users.fetch(mentionMatch.groups.id as Snowflake); return user; } catch { // pass @@ -195,24 +204,37 @@ export class Util extends ClientUtil { */ public colors = { default: '#1FD8F1', - error: '#ff0000', - success: '#00ff02', + error: '#EF4947', + warn: '#FEBA12', + success: '#3BB681', red: '#ff0000', - blue: '#0055ff', + orange: '#E86100', + gold: '#b59400', + yellow: '#ffff00', + green: '#00ff1e', + darkGreen: '#008f11', aqua: '#00bbff', - purple: '#8400ff', + blue: '#0055ff', blurple: '#5440cd', + purple: '#8400ff', pink: '#ff00e6', - green: '#00ff1e', - darkgreen: '#008f11', - gold: '#b59400', - yellow: '#ffff00', white: '#ffffff', gray: '#a6a6a6', - lightgray: '#cfcfcf', - darkgray: '#7a7a7a', - black: '#000000', - orange: '#E86100' + lightGray: '#cfcfcf', + darkGray: '#7a7a7a', + black: '#000000' + }; + + public emojis = { + success: '<:checkmark:837109864101707807>', + warn: '<:warn:848726900876247050> ', + error: '<:error:837123021016924261>', + successFull: '<:checkmark_full:850118767576088646>', + warnFull: '<:warn_full:850118767391539312>', + errorFull: '<:error_full:850118767295201350>', + mad: '<:mad:783046135392239626>', + join: '<:join:850198029809614858>', + leave: '<:leave:850198048205307919>' }; /** @@ -238,7 +260,7 @@ export class Util extends ClientUtil { return apiRes.uuid.replace(/-/g, ''); } - public async syncSlashCommands(force = false, guild?: string): Promise { + public async syncSlashCommands(force = false, guild?: Snowflake): Promise { let fetchedGuild: Guild; if (guild) fetchedGuild = this.client.guilds.cache.get(guild); try { @@ -260,7 +282,7 @@ export class Util extends ClientUtil { for (const [, botCommand] of this.client.commandHandler.modules) { if (botCommand.execSlash) { const found = registered.find((i) => i.name == botCommand.id); - + Command; const slashdata = { name: botCommand.id, description: botCommand.description.content, @@ -332,6 +354,168 @@ export class Util extends ClientUtil { { name: 'No Giveaways', id: '808265422334984203' }, { name: 'No Support', id: '790247359824396319' } ]; + + private paginateEmojis = { + begging: '853667381335162910', + back: '853667410203770881', + stop: '853667471110570034', + forward: '853667492680564747', + end: '853667514915225640' + }; + + public async buttonPaginate( + message: BushMessage, + embeds: MessageEmbed[], + text: string | null = null, + deleteOnExit?: boolean + ): Promise { + if (deleteOnExit === undefined) deleteOnExit = true; + embeds.forEach((_e, i) => { + embeds[i] = embeds[i].setFooter(`Page ${i + 1}/${embeds.length}`); + }); + + const style = Constants.MessageButtonStyles.PRIMARY; + let curPage = 0; + if (typeof embeds !== 'object') throw 'embeds must be an object'; + const msg = await message.util.reply({ content: text, embeds: [embeds[curPage]], components: [getPaginationRow()] }); + const filter = (interaction: ButtonInteraction) => + interaction.customID.startsWith('paginate_') && interaction.message == msg; + const collector = msg.createMessageComponentInteractionCollector(filter, { time: 300000 }); + collector.on('collect', async (interaction: MessageComponentInteraction) => { + if (interaction.user.id == message.author.id || message.client.config.owners.includes(interaction.user.id)) { + switch (interaction.customID) { + case 'paginate_beginning': { + curPage = 0; + await edit(interaction); + break; + } + case 'paginate_back': { + curPage--; + await edit(interaction); + break; + } + case 'paginate_stop': { + if (deleteOnExit) { + await interaction.deferUpdate(); + await msg.delete(); + } else { + await interaction?.update({ + content: `${text ? text + '\n' : ''}Command closed by user.`, + embeds: [], + components: [] + }); + } + return; + } + case 'paginate_next': { + curPage++; + await edit(interaction); + break; + } + case 'paginate_end': { + curPage = embeds.length - 1; + await edit(interaction); + break; + } + } + } else { + return await interaction?.deferUpdate(); + } + }); + + collector.on('end', async () => { + await msg.edit({ content: text, embeds: [embeds[curPage]], components: [getPaginationRow(true)] }).catch(() => {}); + }); + + async function edit(interaction: MessageComponentInteraction): Promise { + return await interaction?.update({ content: text, embeds: [embeds[curPage]], components: [getPaginationRow()] }); + } + function getPaginationRow(disableAll = false): MessageActionRow { + return new MessageActionRow().addComponents( + new MessageButton({ + style, + customID: 'paginate_beginning', + emoji: this.paginateEmojis.begging, + disabled: disableAll || curPage == 0 + }), + new MessageButton({ + style, + customID: 'paginate_back', + emoji: this.paginateEmojis.back, + disabled: disableAll || curPage == 0 + }), + new MessageButton({ style, customID: 'paginate_stop', emoji: this.paginateEmojis.stop, disabled: disableAll }), + new MessageButton({ + style, + customID: 'paginate_next', + emoji: this.paginateEmojis.forward, + disabled: disableAll || curPage == embeds.length - 1 + }), + new MessageButton({ + style, + customID: 'paginate_end', + emoji: this.paginateEmojis.end, + disabled: disableAll || curPage == embeds.length - 1 + }) + ); + } + } + + public async sendWithDeleteButton(message: BushMessage, options: MessageOptions): Promise { + updateOptions(); + const msg = await message.util.reply(options as MessageOptions & { split?: false }); + const filter = (interaction: ButtonInteraction) => interaction.customID == 'paginate__stop' && interaction.message == msg; + const collector = msg.createMessageComponentInteractionCollector(filter, { time: 300000 }); + collector.on('collect', async (interaction: MessageComponentInteraction) => { + if (interaction.user.id == message.author.id || message.client.config.owners.includes(interaction.user.id)) { + await interaction.deferUpdate(); + await msg.delete(); + return; + } else { + return await interaction?.deferUpdate(); + } + }); + + collector.on('end', async () => { + updateOptions(true, true); + await msg.edit(options); + }); + + function updateOptions(edit?: boolean, disable?: boolean) { + if (edit == undefined) edit = false; + if (disable == undefined) disable = false; + options.components = [ + new MessageActionRow().addComponents( + new MessageButton({ + style: Constants.MessageButtonStyles.PRIMARY, + customID: 'paginate__stop', + emoji: this.paginateEmojis.stop, + disabled: disable + }) + ) + ]; + if (edit) { + options.reply = undefined; + } + } + } + /** + * Surrounds text in a code block with the specified language and puts it in a haste bin if it too long. + * + * * Embed Description Limit = 2048 characters + * * Embed Field Limit = 1024 characters + */ + public async codeblock(code: string, length: number, language: 'ts' | 'js' | 'sh' | 'json' | '' = ''): Promise { + let hasteOut = ''; + const tildes = '```'; + const formattingLength = 2 * tildes.length + language.length + 2 * '\n'.length; + if (code.length + formattingLength > length) hasteOut = 'Too large to display. Hastebin: ' + (await this.haste(code)); + + const code2 = code.length > length ? code.substring(0, length - (hasteOut.length + '\n'.length + formattingLength)) : code; + return ( + tildes + language + '\n' + Util.cleanCodeBlockContent(code2) + '\n' + tildes + (hasteOut.length ? '\n' + hasteOut : '') + ); + } } // I just copy pasted this code from stackoverflow don't yell at me if there is issues for it -- cgit