diff options
Diffstat (limited to 'src')
40 files changed, 1030 insertions, 232 deletions
diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts index abdf2b9..4f6a293 100644 --- a/src/commands/dev/eval.ts +++ b/src/commands/dev/eval.ts @@ -86,10 +86,8 @@ export default class EvalCommand extends BushCommand { config = client.config, members = message.guild?.members, roles = message.guild?.roles, - client = client, emojis = util.emojis, colors = util.colors, - util = util, { ActivePunishment, Global, Guild, Level, ModLog, StickyRole } = await import('@lib'), { ButtonInteraction, diff --git a/src/commands/info/avatar.ts b/src/commands/info/avatar.ts index dc10f7d..4f7449b 100644 --- a/src/commands/info/avatar.ts +++ b/src/commands/info/avatar.ts @@ -43,9 +43,8 @@ export default class AvatarCommand extends BushCommand { const embed = new MessageEmbed() .setTimestamp() .setColor(util.colors.default) - .setTitle(user.tag) + .setTitle(`${user.tag}'s Avatar`) .setImage(user.avatarURL({ size: 2048, format: 'png', dynamic: true })); - await message.util.reply({ embeds: [embed] }); } } diff --git a/src/commands/info/guildInfo.ts b/src/commands/info/guildInfo.ts index e82a5fe..577086b 100644 --- a/src/commands/info/guildInfo.ts +++ b/src/commands/info/guildInfo.ts @@ -1,8 +1,7 @@ import { Argument, Constants } from 'discord-akairo'; -import { Guild, GuildPreview, MessageEmbed, Snowflake, Vanity } from 'discord.js'; +import { BaseGuildVoiceChannel, Guild, GuildPreview, MessageEmbed, Snowflake, Vanity } from 'discord.js'; import { BushCommand, BushMessage, BushSlashMessage } from '../../lib'; -// TODO: Implement regions and security export default class GuildInfoCommand extends BushCommand { public constructor() { super('guildInfo', { @@ -58,9 +57,9 @@ export default class GuildInfoCommand extends BushCommand { const guild: Guild | GuildPreview = (args?.guild as Guild | GuildPreview) || (message.guild as Guild); const emojis: string[] = []; const guildAbout: string[] = []; - // const guildSecurity = []; - if (['516977525906341928', '784597260465995796', '717176538717749358', '767448775450820639'].includes(guild.id)) - emojis.push(client.consts.mappings.otherEmojis.BUSH_VERIFIED); + const guildSecurity = []; + const verifiedGuilds = Object.values(client.consts.mappings.guilds); + if (verifiedGuilds.includes(guild.id)) emojis.push(client.consts.mappings.otherEmojis.BUSH_VERIFIED); if (!isPreview && guild instanceof Guild) { if (guild.premiumTier) emojis.push(client.consts.mappings.otherEmojis['BOOST_' + guild.premiumTier]); @@ -91,21 +90,29 @@ export default class GuildInfoCommand extends BushCommand { .size.toLocaleString()}` ]; - // TODO add guild regions - // const guildRegions = []; + const guildRegions = []; + guild.channels.cache.forEach((channel) => { + if (!channel.type.includes('VOICE')) return; + else if (!guildRegions.includes((channel as BaseGuildVoiceChannel).rtcRegion ?? 'automatic')) { + guildRegions.push((channel as BaseGuildVoiceChannel).rtcRegion ?? 'automatic'); + } + }); guildAbout.push( `**Owner:** ${guild.members.cache.get(guild.ownerId)?.user.tag}`, - `**Created** ${guild.createdAt.toLocaleString()}`, - `**Members:** ${guild.memberCount.toLocaleString()}`, - `**Online:** ${guild.approximatePresenceCount?.toLocaleString()}`, - `**Channels:** ${guild.channels.cache.size} (${channelTypes.join(', ')})`, - `**Emojis:** ${guild.emojis.cache.size.toLocaleString()}` - // `**Region:** ${guildRegions.join()}` + `**Created** ${guild.createdAt.toLocaleString()} (${util.dateDelta(guild.createdAt)})`, + `**Members:** ${guild.memberCount.toLocaleString() ?? 0}`, + `**Online:** ${guild.approximatePresenceCount?.toLocaleString() ?? 0}`, + `**Channels:** ${guild.channels.cache.size?.toLocaleString() ?? 0} (${channelTypes.join(', ')})`, + `**Emojis:** ${guild.emojis.cache.size?.toLocaleString() ?? 0}`, + `**Stickers:** ${guild.stickers.cache.size?.toLocaleString() ?? 0}`, + `**Regions:** ${guildRegions.map((region) => client.consts.mappings.regions[region] || region).join(', ')}` ); if (guild.premiumSubscriptionCount) guildAbout.push( - `**Boosts:** Level ${guild.premiumTier.slice(0, 4)} with ${guild.premiumSubscriptionCount ?? 0} boosts` + `**Boosts:** Level ${guild.premiumTier == 'NONE' ? '0' : guild.premiumTier[5]} with ${ + guild.premiumSubscriptionCount ?? 0 + } boosts` ); if (guild.me?.permissions.has('MANAGE_GUILD') && guild.vanityURLCode) { const vanityInfo: Vanity = await guild.fetchVanityData(); @@ -115,12 +122,22 @@ export default class GuildInfoCommand extends BushCommand { ); } - // guildSecurity.push; + guildSecurity.push( + `**Verification Level**: ${guild.verificationLevel.toLowerCase().replace(/_/g, ' ')}`, + `**Explicit Content Filter:** ${guild.explicitContentFilter.toLowerCase().replace(/_/g, ' ')}`, + `**Default Message Notifications:** ${ + typeof guild.defaultMessageNotifications === 'string' + ? guild.defaultMessageNotifications.toLowerCase().replace(/_/g, ' ') + : guild.defaultMessageNotifications + }`, + `**2FA Required**: ${guild.mfaLevel === 'ELEVATED' ? 'yes' : 'no'}` + ); } else { guildAbout.push( `**Members:** ${guild.approximateMemberCount?.toLocaleString()}`, `**Online:** ${guild.approximatePresenceCount?.toLocaleString()}`, - `**Emojis:** ${(guild as GuildPreview).emojis.size}` + `**Emojis:** ${(guild as GuildPreview).emojis.size?.toLocaleString() ?? 0}` + // `**Stickers:** ${(guild as GuildPreview).stickers.size}` ); } @@ -160,11 +177,11 @@ export default class GuildInfoCommand extends BushCommand { if (guildIcon) { guildInfoEmbed.setThumbnail(guildIcon); } - // if (!isPreview) { - // guildInfoEmbed.addField('» Security', guildSecurity.join('\n')); - // } + if (!isPreview) { + guildInfoEmbed.addField('» Security', guildSecurity.join('\n')); + } if (emojis) { - guildInfoEmbed.setDescription(emojis.join(' ')); + guildInfoEmbed.setDescription('\u200B' /*zero width space*/ + emojis.join(' ')); } return await message.util.reply({ embeds: [guildInfoEmbed] }); } diff --git a/src/commands/info/userInfo.ts b/src/commands/info/userInfo.ts index 87235a9..e36e92b 100644 --- a/src/commands/info/userInfo.ts +++ b/src/commands/info/userInfo.ts @@ -1,6 +1,5 @@ import { BushCommand, BushMessage, BushSlashMessage } from '@lib'; import { GuildMember, MessageEmbed } from 'discord.js'; -import moment from 'moment'; // TODO: Allow looking up a user not in the guild and not cached // TODO: Re-Implement Status Emojis @@ -78,11 +77,11 @@ export default class UserInfoCommand extends BushCommand { if (user.premiumSinceTimestamp) emojis.push(client.consts.mappings.otherEmojis.BOOSTER); const createdAt = user.user.createdAt.toLocaleString(), - createdAtDelta = moment(moment(user.user.createdAt).diff(moment())).toLocaleString(), + createdAtDelta = util.dateDelta(user.user.createdAt), joinedAt = user.joinedAt?.toLocaleString(), - joinedAtDelta = moment(user.joinedAt)?.diff(moment()).toLocaleString(), + joinedAtDelta = util.dateDelta(user.joinedAt, 2), premiumSince = user.premiumSince?.toLocaleString(), - premiumSinceDelta = moment(user.premiumSince)?.diff(moment()).toLocaleString(); + premiumSinceDelta = util.dateDelta(user.premiumSince, 2); // General Info const generalInfo = [ @@ -101,12 +100,12 @@ export default class UserInfoCommand extends BushCommand { if (premiumSince) serverUserInfo.push(`**Boosting Since:** ${premiumSince} (${premiumSinceDelta} ago)`); if (user.displayHexColor) serverUserInfo.push(`**Display Color:** ${user.displayHexColor}`); if (user.id == '322862723090219008' && message.guild.id == client.consts.mappings.guilds.bush) - serverUserInfo.push(`**General Deletions:** 2`); + serverUserInfo.push(`**General Deletions:** 1⅓`); if ( ['384620942577369088', '496409778822709251'].includes(user.id) && message.guild.id == client.consts.mappings.guilds.bush ) - serverUserInfo.push(`**General Deletions:** 1`); + serverUserInfo.push(`**General Deletions:** ⅓`); if (user.nickname) serverUserInfo.push(`**Nickname** ${user.nickname}`); if (serverUserInfo.length) userEmbed.addField('» Server Info', serverUserInfo.join('\n')).setColor(user.displayColor || util.colors.default); @@ -128,10 +127,11 @@ export default class UserInfoCommand extends BushCommand { if (user.presence.clientStatus) devices = Object.keys(user.presence.clientStatus); const presenceInfo = []; if (user.presence.status) presenceInfo.push(`**Status:** ${user.presence.status}`); - if (devices) presenceInfo.push(`**${devices.length - 1 ? 'Devices' : 'Device'}:** ${util.oxford(devices, 'and', '')}`); + if (devices && devices.length) + presenceInfo.push(`**${devices.length - 1 ? 'Devices' : 'Device'}:** ${util.oxford(devices, 'and', '')}`); if (activitiesNames.length) presenceInfo.push(`**Activit${activitiesNames.length - 1 ? 'ies' : 'y'}:** ${util.oxford(activitiesNames, 'and', '')}`); - if (customStatus) presenceInfo.push(`**Custom Status:** ${customStatus}`); + if (customStatus && customStatus.length) presenceInfo.push(`**Custom Status:** ${customStatus}`); userEmbed.addField('» Presence', presenceInfo.join('\n')); } @@ -148,7 +148,7 @@ export default class UserInfoCommand extends BushCommand { } if (perms.length) userEmbed.addField('» Important Perms', perms.join(' ')); - if (emojis) userEmbed.setDescription('' /*zero width space*/ + emojis.join(' ')); + if (emojis) userEmbed.setDescription('\u200B' /*zero width space*/ + emojis.join(' ')); return await message.util.reply({ embeds: [userEmbed] }); } diff --git a/src/commands/moderation/slowmode.ts b/src/commands/moderation/slowmode.ts index f9ffbab..9b0d300 100644 --- a/src/commands/moderation/slowmode.ts +++ b/src/commands/moderation/slowmode.ts @@ -1,6 +1,6 @@ -import { BushCommand, BushMessage, BushSlashMessage } from '@lib'; -import { Argument, Constants } from 'discord-akairo'; -import { TextChannel, ThreadChannel } from 'discord.js'; +import { BushCommand, BushMessage, BushNewsChannel, BushSlashMessage, BushTextChannel, BushThreadChannel } from '@lib'; +import { Argument } from 'discord-akairo'; +import { NewsChannel, TextChannel, ThreadChannel } from 'discord.js'; export default class SlowModeCommand extends BushCommand { public constructor() { @@ -25,8 +25,7 @@ export default class SlowModeCommand extends BushCommand { }, { id: 'channel', - type: Constants.ArgumentTypes.CHANNEL, - match: Constants.ArgumentMatches.PHRASE, + type: 'channel', prompt: { start: 'What channel would you like to change?', retry: '{error} Choose a valid channel.', @@ -51,12 +50,18 @@ export default class SlowModeCommand extends BushCommand { public async exec( message: BushMessage | BushSlashMessage, - { length, channel }: { length: number | 'off' | 'none' | 'disable'; channel: TextChannel | ThreadChannel } + { + length, + channel + }: { + length: number | 'off' | 'none' | 'disable'; + channel: TextChannel | ThreadChannel | BushTextChannel | BushNewsChannel | BushThreadChannel | NewsChannel; + } ): Promise<unknown> { if (message.channel.type === 'DM') return await message.util.reply(`${util.emojis.error} This command cannot be run in dms.`); - if (!channel) channel = message.channel as ThreadChannel | TextChannel; - if (!(channel instanceof TextChannel) || !(channel instanceof ThreadChannel)) + if (!channel) channel = message.channel; + if (!(channel instanceof TextChannel) && !(channel instanceof ThreadChannel)) return await message.util.reply(`${util.emojis.error} <#${channel.id}> is not a text or thread channel.`); if (length) { length = diff --git a/src/commands/skyblock-reborn/chooseColorCommand.ts b/src/commands/skyblock-reborn/chooseColor.ts index 0138e36..2b72301 100644 --- a/src/commands/skyblock-reborn/chooseColorCommand.ts +++ b/src/commands/skyblock-reborn/chooseColor.ts @@ -93,7 +93,7 @@ export default class ChooseColorCommand extends BushCommand { args: [ { id: 'color', - type: Constants.ArgumentTypes.ROLE, + type: 'role', match: Constants.ArgumentMatches.REST, prompt: { start: 'Please choose a valid color.', @@ -102,7 +102,7 @@ export default class ChooseColorCommand extends BushCommand { } } ], - clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'], + clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES', 'MANAGE_ROLES'], channel: 'guild', restrictedGuilds: ['839287012409999391'], slash: true, diff --git a/src/lib/badlinks.json b/src/lib/badlinks.json new file mode 100644 index 0000000..f010bc3 --- /dev/null +++ b/src/lib/badlinks.json @@ -0,0 +1,289 @@ +[ + "streammcomunnity.ru", + "stceamcomminity.com", + "steamcommnuninty.com", + "steamcommnunily.com", + "steamncommuniity.com", + "steamcommuniiy.ru", + "steamcomnumity.ru", + "steamoemmunity.com", + "streancommunuty.ru", + "streamcommuninnity.com", + "streamcomnumity.ru", + "stemcommunnilty.com", + "steamsupportpowered.icu", + "staemcomrnunity.store", + "steam-trades.icu", + "facecup.fun", + "fatown.net", + "ultracup.fun", + "iemcup.com", + "esea-mdl.com", + "uspringcup.com", + "denforapasi.cf", + "streamcommulinty.com", + "csskill.com", + "csgo-gifts.com", + "eplcups.com", + "tf2market.store", + "gamerich.xyz", + "anomalygiveaways.pro", + "casefire.fun", + "fineleague.fun", + "stearmcommunnitty.online", + "stearncomminuty.ru", + "stiemcommunitty.ru", + "strearmcommunity.ru", + "steancomunnity.ru", + "cloud9team.space", + "streancommunuty.ru", + "strearmcomunity.ru", + "stermccommunitty.ru", + "steamcommunytu.ru", + "streamcomunity.com", + "steamncommunity.com", + "steamcommunily.uno", + "acercup.com", + "xgamercup.com", + "lootxmarket.com", + "roll4tune.com", + "fivetown.net", + "giveavvay.com", + "stermcommuniity.com", + "skinxinfo.net", + "bit-skins.ru", + "aladdinhub.fun", + "allskinz.xyz", + "ano-skinspin.xyz", + "anomalyknifes.xyz", + "anomalyskin.xyz", + "anomalyskinz.xyz", + "anoskinzz.xyz", + "berrygamble.com", + "bitknife.xyz", + "bitskines.ru", + "challengeme.vip", + "challengeme.in", + "challengme.ru", + "cmepure.com", + "cmskillcup.com", + "counterpaid.xyz", + "counterspin.top", + "counterstrikegift.xyz", + "cs-beast.xyz", + "cs-lucky.xyz", + "cs-pill.xyz", + "cs-prizeskins.xyz", + "cs-prizeskinz.xyz", + "cs-simpleroll.xyz", + "cs-skinz.xyz", + "cs-smoke.xyz", + "cs-spinz.xyz", + "cs-victory.xyz", + "csallskin.xyz", + "csbuyskins.in", + "cscoat.eu", + "csgo-analyst.com", + "csgo-cash.eu", + "csgo-steamanalyst.net", + "csgo-swapskin.com", + "csgo-trade.net", + "csgo-up.com", + "csgobeats.com", + "csgocase.one", + "csgocashs.com", + "csgocheck.ru", + "csgocompetive.com", + "csgodetails.info", + "csgodreamer.com", + "csgodrs.com", + "csgoelite.xyz", + "csgoencup.com", + "csgoevent.xyz", + "csgoindex.ru", + "csgoitemdetails.com", + "csgoitemsprices.com", + "csgoko.tk", + "csgomarble.xyz", + "csgomarketplace.net", + "csgomarkets.net", + "csgoprocupgo.com", + "csgorcup.com", + "csgorose.com", + "csgoroyalskins1.com", + "csgoskill.ru", + "csgoskinprices.com", + "csgoskinsinfo.com", + "csgoskinsroll.com", + "csgosteamanalysis.com", + "csgosteamanalyst.ru", + "csgoteammate.gq", + "csgothunby.com", + "csgotrades.net", + "csgovip.ru", + "csgoxgiveaway.ru", + "csgozone.net.in", + "csgunskins.xyz", + "csmoneyskinz.xyz", + "csmvcecup.com", + "csprices.in", + "csskillpro.xyz", + "csskinz.xyz", + "cstournament.ru", + "csxrnoney.com", + "cybergamearena.ru", + "d2cups.com", + "d2faceit.com", + "deamonbets.ru", + "demonbets.ru", + "diablobets.com", + "doatgiveaway.top", + "dopeskins.com", + "dota2fight.ru", + "dota2fight.net", + "dota2giveaway.top", + "dota2giveaways.top", + "dotafights.vip", + "dotagiveaway.win", + "earnskinz.xyz", + "emeraldbets.ru", + "esportgaming.ru", + "event-games4roll.com", + "exchangeuritems.gq", + "extraskinscs.xyz", + "ezwin24.ru", + "faceiteasyleague.ru", + "fireopencase.com", + "free-skins.ru", + "game4roll.com", + "gameluck.ru", + "games-roll.ru", + "games-roll.ml", + "games-roll.ga", + "giveawayskin.com", + "global-skins.gq", + "globalcsskins.xyz", + "globalskins.tk", + "goldendota.com", + "goodskins.gq", + "gosteamanalyst.com", + "gtakey.ru", + "hellgiveaway.trade", + "hltvcsgo.com", + "hltvgames.net", + "knifespin.top", + "knifespin.xyz", + "knifespin.top", + "knifespins.xyz", + "knifez-roll.xyz", + "knifez-win.xyz", + "league-csgo.com", + "lehatop-01.ru", + "loungeztrade.com", + "lucky-skins.xyz", + "makson-gta.ru", + "maxskins.xyz", + "mvcsgo.com", + "mvpcup.ru", + "mvptournament.com", + "mygames4roll.com", + "night-skins.com", + "ownerbets.com", + "playerskinz.xyz", + "rangskins.com", + "roll-skins.ru", + "roll4knife.xyz", + "rollknfez.xyz", + "rollskin-simple.xyz", + "csgo-market.ru.com", + "sakuralive.ru.com", + "csgocupp.ru.com", + "csgoeasywin.ru.com", + "csgocybersport.ru.com", + "csgocheck.ru.com", + "csgo-market.ru.com", + "csgoindex.ru.com", + "rushbskins.xyz", + "rushskins.xyz", + "s1mple-spin.xyz", + "simple-knifez.xyz", + "simple-win.xyz", + "simplegamepro.ru", + "simpleroll-cs.xyz", + "simplespinz.xyz", + "simplewinz.xyz", + "skin-index.com", + "skin888trade.com", + "skincs-spin.xyz", + "skincs-spin.top", + "skinmarkets.net", + "skins-hub.top", + "skins-info.net", + "skins-jungle.xyz", + "skinsboost.ru", + "skinsdatabse.com", + "skinsind.com", + "skinsmind.ru", + "skinspace.ru", + "skinsplane.com", + "skinsplanes.com", + "skinsplanets.com", + "skinxmarket.site", + "skinz-spin.top", + "skinz-spin.xyz", + "skinzjar.ru", + "skinzprize.xyz", + "skinzspin-cs.xyz", + "skinzspinz.xyz", + "spin-games.com", + "spin4skinzcs.top", + "spin4skinzcs.xyz", + "spinforskin.ml", + "sponsored-simple.xyz", + "staffstatsgo.com", + "starrygamble.com", + "stat-csgo.ru", + "stats-cs.ru", + "steam-analyst.ru", + "steamanalysts.com", + "steamgamesroll.ru", + "stewie2k-giveaway-150days.pro", + "sunnygamble.com", + "swapskins.live", + "test-domuin2.com", + "test-domuin3.ru", + "test-domuin4.ru", + "test-domuin5.ru", + "tournamentt.com", + "waterbets.ru", + "ultimateskins.xyz", + "win-skin.top", + "win-skin.xyz", + "winknifespin.xyz", + "winskin-simple.xyz", + "winskins.top", + "wintheskin.xyz", + "steemcommnunity.ru", + "steamcomminytu.ru", + "stearncommunity.ru", + "stearncommunytiy.ru", + "steamcommutiny.com", + "steamcomrunity.com", + "steamcommunytiu.ru", + "steamcommnuntiy.com", + "steamcomminytiu.ru", + "steamcomminytiu.com", + "steancomunyiti.ru", + "steamcormmuntiy.com", + "store-stempowered.com", + "dlscord.store", + "streamcommuunnity.com", + "steamcommunityw.com", + "steamconmunlty.com", + "steamcommrutiny.ru", + "dlscord.info", + "steamcomnmuituy.com", + "steamcommunityu.com", + "dicsord.gifts", + "discod.gift" +] diff --git a/src/lib/badwords.json b/src/lib/badwords.json new file mode 100644 index 0000000..94c854f --- /dev/null +++ b/src/lib/badwords.json @@ -0,0 +1,15 @@ +{ + "nigger": 3, + "nigga": 3, + "retard": 2, + "retarted": 2, + "faggot": 2, + "slut": 1, + "whore": 1, + "卍": 3, + "found a cool software that improves the": 3, + "hi, bro h am leaving cs:go and giving away my skin": 3, + "hi friend, today i am leaving this fucking game": 3, + "hi guys, i'm leaving this fucking game, take my": 3, + "you can choose any skin for yourself": 3 +} diff --git a/src/lib/extensions/discord-akairo/BushClient.ts b/src/lib/extensions/discord-akairo/BushClient.ts index 10db18d..54b5250 100644 --- a/src/lib/extensions/discord-akairo/BushClient.ts +++ b/src/lib/extensions/discord-akairo/BushClient.ts @@ -1,6 +1,7 @@ import chalk from 'chalk'; import { AkairoClient } from 'discord-akairo'; import { + Collection, Guild, Intents, InteractionReplyOptions, @@ -31,6 +32,7 @@ import { BushCache } from '../../utils/BushCache'; import { BushConstants } from '../../utils/BushConstants'; import { BushLogger } from '../../utils/BushLogger'; import { Config } from '../../utils/Config'; +import { BushApplicationCommand } from '../discord.js/BushApplicationCommand'; import { BushButtonInteraction } from '../discord.js/BushButtonInteraction'; import { BushCategoryChannel } from '../discord.js/BushCategoryChannel'; import { BushCommandInteraction } from '../discord.js/BushCommandInteraction'; @@ -42,6 +44,7 @@ import { BushMessage } from '../discord.js/BushMessage'; import { BushMessageReaction } from '../discord.js/BushMessageReaction'; import { BushNewsChannel } from '../discord.js/BushNewsChannel'; import { BushPresence } from '../discord.js/BushPresence'; +import { BushReactionEmoji } from '../discord.js/BushReactionEmoji'; import { BushRole } from '../discord.js/BushRole'; import { BushSelectMenuInteraction } from '../discord.js/BushSelectMenuInteraction'; import { BushStoreChannel } from '../discord.js/BushStoreChannel'; @@ -66,6 +69,15 @@ export type BushThreadMemberResolvable = BushThreadMember | BushUserResolvable; export type BushUserResolvable = BushUser | Snowflake | BushMessage | BushGuildMember | BushThreadMember; export type BushGuildMemberResolvable = BushGuildMember | BushUserResolvable; export type BushRoleResolvable = BushRole | Snowflake; +export type BushMessageResolvable = BushMessage | Snowflake; +export type BushEmojiResolvable = Snowflake | BushGuildEmoji | BushReactionEmoji; +export type BushEmojiIdentifierResolvable = string | BushEmojiResolvable; +export type BushThreadChannelResolvable = BushThreadChannel | Snowflake; +export type BushApplicationCommandResolvable = BushApplicationCommand | Snowflake; +export interface BushFetchedThreads { + threads: Collection<Snowflake, BushThreadChannel>; + hasMore?: boolean; +} const rl = readline.createInterface({ input: process.stdin, diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts index ebac9eb..926a529 100644 --- a/src/lib/extensions/discord-akairo/BushClientUtil.ts +++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts @@ -38,7 +38,10 @@ import { } from 'discord.js'; import got from 'got'; import humanizeDuration from 'humanize-duration'; +import moment from 'moment'; import { inspect, InspectOptions, promisify } from 'util'; +import _badLinks from '../../badlinks.json'; // Stolen from https://github.com/nacrt/SkyblockClient-REPO/blob/main/files/scamlinks.json +import badWords from '../../badwords.json'; import { ActivePunishment, ActivePunishmentType } from '../../models/ActivePunishment'; import { BushNewsChannel } from '../discord.js/BushNewsChannel'; import { BushTextChannel } from '../discord.js/BushTextChannel'; @@ -752,8 +755,13 @@ export class BushClientUtil extends ClientUtil { return typeMap[type]; } - public humanizeDuration(duration: number): string { - return humanizeDuration(duration, { language: 'en', maxDecimalPoints: 2 }); + public humanizeDuration(duration: number, largest?: number): string { + if (largest) return humanizeDuration(duration, { language: 'en', maxDecimalPoints: 2, largest }); + else return humanizeDuration(duration, { language: 'en', maxDecimalPoints: 2 }); + } + + public dateDelta(date: Date, largest?: number) { + return this.humanizeDuration(moment(date).diff(moment()), largest ?? 3); } public async findUUID(player: string): Promise<string> { @@ -790,8 +798,78 @@ export class BushClientUtil extends ClientUtil { /* eslint-enable @typescript-eslint/no-unused-vars */ public async automod(message: BushMessage) { - const autoModPhases = await message.guild.getSetting('autoModPhases'); - if (autoModPhases.includes(message.content.toString()) && message.deletable) return await message.delete(); + if (message.guild.id !== client.consts.mappings.guilds.bush) return; // just temporary + /* await message.guild.getSetting('autoModPhases'); */ + const badLinks = _badLinks.map((link) => { + return { [link]: 3 }; + }); + + const wordArray = [...Object.keys(badWords), ...Object.keys(badLinks)]; + const offences: { [key: string]: number } = {}; + wordArray.forEach((word) => { + if (message.content?.toLowerCase().replace(/ /g, '').includes(word.toLowerCase().replace(/ /g, ''))) { + if (offences[word]) offences[word] = wordArray[word]; + } + }); + if (!Object.keys(offences)?.length) return; + + const highestOffence = Object.values(offences).sort((a, b) => b - a)[0]; + + switch (highestOffence) { + case 0: { + if (message.deletable) void message.delete(); + break; + } + case 1: { + if (message.deletable) void message.delete(); + void message.member.warn({ + moderator: message.guild.me, + reason: 'Saying a blacklisted word.' + }); + break; + } + case 2: { + if (message.deletable) void message.delete(); + void message.member.mute({ + moderator: message.guild.me, + reason: 'Saying a blacklisted word.', + duration: 900_000 // 15 minutes + }); + break; + } + case 3: { + if (message.deletable) void message.delete(); + void message.member.mute({ + moderator: message.guild.me, + reason: 'Saying a blacklisted word.', + duration: 0 // perm + }); + break; + } + } + + const color = + highestOffence === 0 + ? util.colors.lightGray + : highestOffence === 1 + ? util.colors.yellow + : highestOffence === 2 + ? util.colors.orange + : util.colors.red; + void (message.guild.channels.cache.get('783088333055066212') as TextChannel).send({ + embeds: [ + new MessageEmbed() + .setTitle(`[Severity ${highestOffence}] Automod Action Performed`) + .setDescription( + `**User:** ${message.author} (${message.author.tag})\n**Blacklisted Words:** ${util + .surroundArray(Object.keys(offences), '`') + .join()}` + ) + .addField('Message Content', `${this.codeblock(message.content, 1024)}`) + .setColor(color) + .setTimestamp() + ] + }); } public capitalizeFirstLetter(string: string): string { diff --git a/src/lib/extensions/discord-akairo/BushCommand.ts b/src/lib/extensions/discord-akairo/BushCommand.ts index 0127a59..3f79aeb 100644 --- a/src/lib/extensions/discord-akairo/BushCommand.ts +++ b/src/lib/extensions/discord-akairo/BushCommand.ts @@ -78,7 +78,7 @@ export interface BushArgumentOptions extends BaseBushArgumentOptions { type?: BushArgumentType; } export interface CustomBushArgumentOptions extends BaseBushArgumentOptions { - type?: ArgumentTypeCaster | (string | string[])[] | RegExp | string; + customType?: ArgumentTypeCaster | (string | string[])[] | RegExp | string; } export interface BushCommandOptions extends CommandOptions { @@ -122,6 +122,11 @@ export class BushCommand extends Command { this.restrictedChannels = options.restrictedChannels; this.restrictedGuilds = options.restrictedGuilds; this.completelyHide = options.completelyHide; + if (options.args && typeof options.args !== 'function') { + options.args.forEach((arg: BushArgumentOptions | CustomBushArgumentOptions) => { + if (arg['customType']) arg.type = arg['customType']; + }); + } } public exec(message: BushMessage, args: any): any; diff --git a/src/lib/extensions/discord.js/BushApplicationCommandManager.d.ts b/src/lib/extensions/discord.js/BushApplicationCommandManager.d.ts new file mode 100644 index 0000000..0e071ff --- /dev/null +++ b/src/lib/extensions/discord.js/BushApplicationCommandManager.d.ts @@ -0,0 +1,43 @@ +import { ApplicationCommandData, CachedManager, Collection, FetchApplicationCommandOptions, Snowflake } from 'discord.js'; +import { BushApplicationCommandResolvable, BushClient } from '../discord-akairo/BushClient'; +import { BushApplicationCommand } from './BushApplicationCommand'; +import { BushApplicationCommandPermissionsManager } from './BushApplicationCommandPermissionsManager'; +import { BushGuildResolvable } from './BushCommandInteraction'; + +export class BushApplicationCommandManager< + ApplicationCommandType = BushApplicationCommand<{ guild: BushGuildResolvable }>, + PermissionsOptionsExtras = { guild: BushGuildResolvable }, + PermissionsGuildType = null +> extends CachedManager<Snowflake, ApplicationCommandType, BushApplicationCommandResolvable> { + public constructor(client: BushClient, iterable?: Iterable<unknown>); + public declare readonly client: BushClient; + public permissions: BushApplicationCommandPermissionsManager< + { command?: BushApplicationCommandResolvable } & PermissionsOptionsExtras, + { command: BushApplicationCommandResolvable } & PermissionsOptionsExtras, + PermissionsOptionsExtras, + PermissionsGuildType, + null + >; + private commandPath({ id, guildId }: { id?: Snowflake; guildId?: Snowflake }): unknown; + public create(command: ApplicationCommandData): Promise<ApplicationCommandType>; + public create(command: ApplicationCommandData, guildId: Snowflake): Promise<BushApplicationCommand>; + public delete(command: BushApplicationCommandResolvable, guildId?: Snowflake): Promise<ApplicationCommandType | null>; + public edit(command: BushApplicationCommandResolvable, data: ApplicationCommandData): Promise<ApplicationCommandType>; + public edit( + command: BushApplicationCommandResolvable, + data: ApplicationCommandData, + guildId: Snowflake + ): Promise<BushApplicationCommand>; + public fetch( + id: Snowflake, + options: FetchApplicationCommandOptions & { guildId: Snowflake } + ): Promise<BushApplicationCommand>; + public fetch(id: Snowflake, options?: FetchApplicationCommandOptions): Promise<ApplicationCommandType>; + public fetch( + id?: Snowflake, + options?: FetchApplicationCommandOptions + ): Promise<Collection<Snowflake, ApplicationCommandType>>; + public set(commands: ApplicationCommandData[]): Promise<Collection<Snowflake, ApplicationCommandType>>; + public set(commands: ApplicationCommandData[], guildId: Snowflake): Promise<Collection<Snowflake, BushApplicationCommand>>; + private static transformCommand(command: ApplicationCommandData): unknown; +} diff --git a/src/lib/extensions/discord.js/BushApplicationCommandManager.ts b/src/lib/extensions/discord.js/BushApplicationCommandManager.ts deleted file mode 100644 index a8abb6f..0000000 --- a/src/lib/extensions/discord.js/BushApplicationCommandManager.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { ApplicationCommandManager, GuildResolvable, Snowflake } from 'discord.js'; -import { BushClient } from '../discord-akairo/BushClient'; -import { BushApplicationCommand } from './BushApplicationCommand'; -import { BushApplicationCommandPermissionsManager } from './BushApplicationCommandPermissionsManager'; -import { BushGuildResolvable } from './BushCommandInteraction'; - -export type BushApplicationCommandResolvable = BushApplicationCommand | Snowflake; - -export class BushApplicationCommandManager< - ApplicationCommandType = BushApplicationCommand<{ guild: BushGuildResolvable }>, - PermissionsOptionsExtras = { guild: GuildResolvable }, - PermissionsGuildType = null -> extends ApplicationCommandManager<ApplicationCommandType, PermissionsOptionsExtras, PermissionsGuildType> { - public declare permissions: BushApplicationCommandPermissionsManager< - { command?: BushApplicationCommandResolvable } & PermissionsOptionsExtras, - { command: BushApplicationCommandResolvable } & PermissionsOptionsExtras, - PermissionsOptionsExtras, - PermissionsGuildType, - null - >; - - public constructor(client: BushClient, iterable?: Iterable<any>) { - super(client, iterable); - } -} diff --git a/src/lib/extensions/discord.js/BushApplicationCommandPermissionsManager.d.ts b/src/lib/extensions/discord.js/BushApplicationCommandPermissionsManager.d.ts new file mode 100644 index 0000000..443fee2 --- /dev/null +++ b/src/lib/extensions/discord.js/BushApplicationCommandPermissionsManager.d.ts @@ -0,0 +1,54 @@ +import { + ApplicationCommand, + ApplicationCommandManager, + ApplicationCommandPermissionData, + ApplicationCommandPermissions, + BaseManager, + Collection, + GuildApplicationCommandManager, + GuildApplicationCommandPermissionData, + Snowflake +} from 'discord.js'; +import { BushClient, BushRoleResolvable, BushUserResolvable } from '../discord-akairo/BushClient'; + +export class BushApplicationCommandPermissionsManager< + BaseOptions, + FetchSingleOptions, + FullPermissionsOptions, + GuildType, + CommandIdType +> extends BaseManager { + public constructor(manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand); + public declare readonly client: BushClient; + public commandId: CommandIdType; + public guild: GuildType; + public guildId: Snowflake | null; + public manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand; + public add( + options: FetchSingleOptions & { permissions: ApplicationCommandPermissionData[] } + ): Promise<ApplicationCommandPermissions[]>; + public has(options: FetchSingleOptions & { permissionId: BushUserResolvable | BushRoleResolvable }): Promise<boolean>; + public fetch(options: FetchSingleOptions): Promise<ApplicationCommandPermissions[]>; + public fetch(options: BaseOptions): Promise<Collection<Snowflake, ApplicationCommandPermissions[]>>; + public remove( + options: + | (FetchSingleOptions & { + users: BushUserResolvable | BushUserResolvable[]; + roles?: BushRoleResolvable | BushRoleResolvable[]; + }) + | (FetchSingleOptions & { + users?: BushUserResolvable | BushUserResolvable[]; + roles: BushRoleResolvable | BushRoleResolvable[]; + }) + ): Promise<ApplicationCommandPermissions[]>; + public set( + options: FetchSingleOptions & { permissions: ApplicationCommandPermissionData[] } + ): Promise<ApplicationCommandPermissions[]>; + public set( + options: FullPermissionsOptions & { + fullPermissions: GuildApplicationCommandPermissionData[]; + } + ): Promise<Collection<Snowflake, ApplicationCommandPermissions[]>>; + private permissionsPath(guildId: Snowflake, commandId?: Snowflake): unknown; + private static transformPermissions(permissions: ApplicationCommandPermissionData, received?: boolean): unknown; +} diff --git a/src/lib/extensions/discord.js/BushApplicationCommandPermissionsManager.ts b/src/lib/extensions/discord.js/BushApplicationCommandPermissionsManager.ts deleted file mode 100644 index 3a98833..0000000 --- a/src/lib/extensions/discord.js/BushApplicationCommandPermissionsManager.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { - ApplicationCommand, - ApplicationCommandManager, - ApplicationCommandPermissionsManager, - GuildApplicationCommandManager -} from 'discord.js'; -import { BushClient } from '../discord-akairo/BushClient'; -import { BushApplicationCommand } from './BushApplicationCommand'; -import { BushApplicationCommandManager } from './BushApplicationCommandManager'; -import { BushGuildApplicationCommandManager } from './BushGuildApplicationCommandManager'; - -export class BushApplicationCommandPermissionsManager< - BaseOptions, - FetchSingleOptions, - FullPermissionsOptions, - GuildType, - CommandIdType -> extends ApplicationCommandPermissionsManager< - BaseOptions, - FetchSingleOptions, - FullPermissionsOptions, - GuildType, - CommandIdType -> { - public declare client: BushClient; - public declare manager: BushApplicationCommandManager | BushGuildApplicationCommandManager | BushApplicationCommand; - - public constructor(manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand) { - super(manager); - } -} diff --git a/src/lib/extensions/discord.js/BushClientEvents.d.ts b/src/lib/extensions/discord.js/BushClientEvents.d.ts new file mode 100644 index 0000000..6c1fec5 --- /dev/null +++ b/src/lib/extensions/discord.js/BushClientEvents.d.ts @@ -0,0 +1,6 @@ +import { ClientEvents } from 'discord.js'; +import { BushMessage } from './BushMessage'; + +export interface BushClientEvents extends ClientEvents { + messageCreate: [message: BushMessage]; +} diff --git a/src/lib/extensions/discord.js/BushGuild.ts b/src/lib/extensions/discord.js/BushGuild.ts index 9d618ec..dd41dad 100644 --- a/src/lib/extensions/discord.js/BushGuild.ts +++ b/src/lib/extensions/discord.js/BushGuild.ts @@ -1,12 +1,13 @@ import { Guild, User } from 'discord.js'; -import { ModLogType } from '../..'; +import { BushGuildMember, ModLogType } from '../..'; import { Guild as GuildDB, GuildModel } from '../../models/Guild'; import { BushClient, BushUserResolvable } from '../discord-akairo/BushClient'; +import { BushGuildMemberManager } from './BushGuildMemberManager'; export class BushGuild extends Guild { public declare readonly client: BushClient; - // I cba to do this - //// public declare members: GuildMemberManager; + public declare readonly me: BushGuildMember | null; + public declare members: BushGuildMemberManager; public constructor(client: BushClient, data: unknown) { super(client, data); } diff --git a/src/lib/extensions/discord.js/BushGuildApplicationCommandManager.d.ts b/src/lib/extensions/discord.js/BushGuildApplicationCommandManager.d.ts new file mode 100644 index 0000000..c0400ce --- /dev/null +++ b/src/lib/extensions/discord.js/BushGuildApplicationCommandManager.d.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/ban-types */ +import { ApplicationCommandData, BaseFetchOptions, Collection, Snowflake } from 'discord.js'; +import { BushApplicationCommandResolvable, BushClient } from '../discord-akairo/BushClient'; +import { BushApplicationCommand } from './BushApplicationCommand'; +import { BushApplicationCommandManager } from './BushApplicationCommandManager'; +import { BushGuild } from './BushGuild'; + +export class BushGuildApplicationCommandManager extends BushApplicationCommandManager<BushApplicationCommand, {}, BushGuild> { + public constructor(guild: BushGuild, iterable?: Iterable<unknown>); + public declare readonly client: BushClient; + public guild: BushGuild; + public create(command: ApplicationCommandData): Promise<BushApplicationCommand>; + public delete(command: BushApplicationCommandResolvable): Promise<BushApplicationCommand | null>; + public edit(command: BushApplicationCommandResolvable, data: ApplicationCommandData): Promise<BushApplicationCommand>; + public fetch(id: Snowflake, options?: BaseFetchOptions): Promise<BushApplicationCommand>; + public fetch(id?: undefined, options?: BaseFetchOptions): Promise<Collection<Snowflake, BushApplicationCommand>>; + public set(commands: ApplicationCommandData[]): Promise<Collection<Snowflake, BushApplicationCommand>>; +} diff --git a/src/lib/extensions/discord.js/BushGuildApplicationCommandManager.ts b/src/lib/extensions/discord.js/BushGuildApplicationCommandManager.ts deleted file mode 100644 index 3dd79a4..0000000 --- a/src/lib/extensions/discord.js/BushGuildApplicationCommandManager.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { GuildApplicationCommandManager } from 'discord.js'; -import { BushGuild } from './BushGuild'; - -export class BushGuildApplicationCommandManager extends GuildApplicationCommandManager { - public declare guild: BushGuild; - public constructor(guild: BushGuild, iterable?: Iterable<any>) { - super(guild, iterable); - } -} diff --git a/src/lib/extensions/discord.js/BushGuildEmojiRoleManager.d.ts b/src/lib/extensions/discord.js/BushGuildEmojiRoleManager.d.ts new file mode 100644 index 0000000..6e36292 --- /dev/null +++ b/src/lib/extensions/discord.js/BushGuildEmojiRoleManager.d.ts @@ -0,0 +1,19 @@ +import { Collection, DataManager, Snowflake } from 'discord.js'; +import { BushClient, BushRoleResolvable } from '../discord-akairo/BushClient'; +import { BushGuild } from './BushGuild'; +import { BushGuildEmoji } from './BushGuildEmoji'; +import { BushRole } from './BushRole'; + +export class BushGuildEmojiRoleManager extends DataManager<Snowflake, BushRole, BushRoleResolvable> { + public constructor(emoji: BushGuildEmoji); + public declare readonly client: BushClient; + public emoji: BushGuildEmoji; + public guild: BushGuild; + public add( + roleOrRoles: BushRoleResolvable | readonly BushRoleResolvable[] | Collection<Snowflake, BushRole> + ): Promise<BushGuildEmoji>; + public set(roles: readonly BushRoleResolvable[] | Collection<Snowflake, BushRole>): Promise<BushGuildEmoji>; + public remove( + roleOrRoles: BushRoleResolvable | readonly BushRoleResolvable[] | Collection<Snowflake, BushRole> + ): Promise<BushGuildEmoji>; +} diff --git a/src/lib/extensions/discord.js/BushGuildEmojiRoleManager.ts b/src/lib/extensions/discord.js/BushGuildEmojiRoleManager.ts deleted file mode 100644 index 00afb25..0000000 --- a/src/lib/extensions/discord.js/BushGuildEmojiRoleManager.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Collection, GuildEmojiRoleManager, Snowflake } from 'discord.js'; -import { BushGuild } from './BushGuild'; -import { BushGuildEmoji } from './BushGuildEmoji'; -import { BushRole } from './BushRole'; - -export class BushGuildEmojiRoleManager extends GuildEmojiRoleManager { - public declare emoji: BushGuildEmoji; - public declare guild: BushGuild; - public declare cache: Collection<Snowflake, BushRole>; - public constructor(emoji: BushGuildEmoji) { - super(emoji); - } -} diff --git a/src/lib/extensions/discord.js/BushGuildMemberManager.d.ts b/src/lib/extensions/discord.js/BushGuildMemberManager.d.ts new file mode 100644 index 0000000..96b99e5 --- /dev/null +++ b/src/lib/extensions/discord.js/BushGuildMemberManager.d.ts @@ -0,0 +1,35 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { + BanOptions, + CachedManager, + Collection, + FetchMemberOptions, + FetchMembersOptions, + GuildMember, + GuildMemberEditData, + GuildPruneMembersOptions, + GuildSearchMembersOptions, + Snowflake, + User, + UserResolvable +} from 'discord.js'; +import { BushClient, BushGuildMemberResolvable, BushUserResolvable } from '../discord-akairo/BushClient'; +import { BushGuild } from './BushGuild'; +import { BushGuildMember } from './BushGuildMember'; + +export class BushGuildMemberManager extends CachedManager<Snowflake, BushGuildMember, BushGuildMemberResolvable> { + public constructor(guild: BushGuild, iterable?: Iterable<unknown>); + public declare readonly client: BushClient; + public guild: BushGuild; + public ban(user: BushUserResolvable, options?: BanOptions): Promise<GuildMember | User | Snowflake>; + public edit(user: BushUserResolvable, data: GuildMemberEditData, reason?: string): Promise<void>; + public fetch( + options: UserResolvable | FetchMemberOptions | (FetchMembersOptions & { user: UserResolvable }) + ): Promise<GuildMember>; + public fetch(options?: FetchMembersOptions): Promise<Collection<Snowflake, GuildMember>>; + public kick(user: UserResolvable, reason?: string): Promise<BushGuildMember | User | Snowflake>; + public prune(options: GuildPruneMembersOptions & { dry?: false; count: false }): Promise<null>; + public prune(options?: GuildPruneMembersOptions): Promise<number>; + public search(options: GuildSearchMembersOptions): Promise<Collection<Snowflake, GuildMember>>; + public unban(user: UserResolvable, reason?: string): Promise<User>; +} diff --git a/src/lib/extensions/discord.js/BushGuildMemberManager.ts b/src/lib/extensions/discord.js/BushGuildMemberManager.ts deleted file mode 100644 index dbc2da5..0000000 --- a/src/lib/extensions/discord.js/BushGuildMemberManager.ts +++ /dev/null @@ -1,11 +0,0 @@ -// /* eslint-disable @typescript-eslint/no-explicit-any */ -// import { GuildMemberManager } from 'discord.js'; -// import { BushGuild } from './BushGuild'; - -// export class BushGuildMemberManager extends GuildMemberManager { -// public guild: BushGuild; - -// public constructor(guild: BushGuild, iterable?: Iterable<any>) { -// super(guild, iterable); -// } -// } diff --git a/src/lib/extensions/discord.js/BushMessageManager.d.ts b/src/lib/extensions/discord.js/BushMessageManager.d.ts new file mode 100644 index 0000000..bf795ad --- /dev/null +++ b/src/lib/extensions/discord.js/BushMessageManager.d.ts @@ -0,0 +1,33 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { + BaseFetchOptions, + CachedManager, + ChannelLogsQueryOptions, + Collection, + EmojiIdentifierResolvable, + MessageEditOptions, + MessagePayload, + Snowflake, + TextBasedChannelFields +} from 'discord.js'; +import { BushClient, BushMessageResolvable } from '../discord-akairo/BushClient'; +import { BushDMChannel } from './BushDMChannel'; +import { BushMessage } from './BushMessage'; +import { BushTextChannel } from './BushTextChannel'; +import { BushThreadChannel } from './BushThreadChannel'; + +export class BushMessageManager extends CachedManager<Snowflake, BushMessage, BushMessageResolvable> { + public constructor(channel: BushTextChannel | BushDMChannel | BushThreadChannel, iterable?: Iterable<unknown>); + public declare readonly client: BushClient; + public channel: TextBasedChannelFields; + public cache: Collection<Snowflake, BushMessage>; + public crosspost(message: BushMessageResolvable): Promise<BushMessage>; + public delete(message: BushMessageResolvable): Promise<void>; + public edit(message: BushMessageResolvable, options: MessagePayload | MessageEditOptions): Promise<BushMessage>; + public fetch(message: Snowflake, options?: BaseFetchOptions): Promise<BushMessage>; + public fetch(options?: ChannelLogsQueryOptions, cacheOptions?: BaseFetchOptions): Promise<Collection<Snowflake, BushMessage>>; + public fetchPinned(cache?: boolean): Promise<Collection<Snowflake, BushMessage>>; + public react(message: BushMessageResolvable, emoji: EmojiIdentifierResolvable): Promise<void>; + public pin(message: BushMessageResolvable): Promise<void>; + public unpin(message: BushMessageResolvable): Promise<void>; +} diff --git a/src/lib/extensions/discord.js/BushMessageManager.ts b/src/lib/extensions/discord.js/BushMessageManager.ts deleted file mode 100644 index 181808a..0000000 --- a/src/lib/extensions/discord.js/BushMessageManager.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { Collection, MessageManager, Snowflake } from 'discord.js'; -import { BushClient } from '../discord-akairo/BushClient'; -import { BushDMChannel } from './BushDMChannel'; -import { BushMessage } from './BushMessage'; -import { BushTextChannel } from './BushTextChannel'; -import { BushThreadChannel } from './BushThreadChannel'; - -export class BushMessageManager extends MessageManager { - public declare readonly client: BushClient; - public declare cache: Collection<Snowflake, BushMessage>; - public constructor(channel: BushTextChannel | BushDMChannel | BushThreadChannel, iterable?: Iterable<any>) { - super(channel, iterable); - } -} diff --git a/src/lib/extensions/discord.js/BushThreadManager.d.ts b/src/lib/extensions/discord.js/BushThreadManager.d.ts new file mode 100644 index 0000000..6b8250b --- /dev/null +++ b/src/lib/extensions/discord.js/BushThreadManager.d.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { + BaseFetchOptions, + CachedManager, + FetchArchivedThreadOptions, + FetchThreadsOptions, + Snowflake, + ThreadChannelResolvable, + ThreadCreateOptions +} from 'discord.js'; +import { BushClient, BushFetchedThreads, BushThreadChannelResolvable } from '../discord-akairo/BushClient'; +import { BushNewsChannel } from './BushNewsChannel'; +import { BushTextChannel } from './BushTextChannel'; +import { BushThreadChannel } from './BushThreadChannel'; + +export class BushThreadManager<AllowedThreadType> extends CachedManager<Snowflake, BushThreadChannel, ThreadChannelResolvable> { + public constructor(channel: BushTextChannel | BushNewsChannel, iterable?: Iterable<unknown>); + public declare readonly client: BushClient; + public channel: BushTextChannel | BushNewsChannel; + public create(options: ThreadCreateOptions<AllowedThreadType>): Promise<BushThreadChannel>; + public fetch(options: BushThreadChannelResolvable, cacheOptions?: BaseFetchOptions): Promise<BushThreadChannel | null>; + public fetch(options?: FetchThreadsOptions, cacheOptions?: { cache?: boolean }): Promise<BushFetchedThreads>; + public fetchArchived(options?: FetchArchivedThreadOptions, cache?: boolean): Promise<BushFetchedThreads>; + public fetchActive(cache?: boolean): Promise<BushFetchedThreads>; +} diff --git a/src/lib/extensions/discord.js/BushThreadManager.ts b/src/lib/extensions/discord.js/BushThreadManager.ts deleted file mode 100644 index 50eaa2d..0000000 --- a/src/lib/extensions/discord.js/BushThreadManager.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { ThreadManager } from 'discord.js'; -import { BushNewsChannel } from './BushNewsChannel'; -import { BushTextChannel } from './BushTextChannel'; - -export class BushThreadManager<AllowedThreadType> extends ThreadManager<AllowedThreadType> { - public declare channel: BushTextChannel | BushNewsChannel; - public constructor(channel: BushTextChannel | BushNewsChannel, iterable?: Iterable<any>) { - super(channel, iterable); - } -} diff --git a/src/lib/extensions/discord.js/BushThreadMemberManager.d.ts b/src/lib/extensions/discord.js/BushThreadMemberManager.d.ts new file mode 100644 index 0000000..ba9b90b --- /dev/null +++ b/src/lib/extensions/discord.js/BushThreadMemberManager.d.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-empty-interface */ +import { CachedManager, Collection, Snowflake, ThreadChannel, ThreadMember, UserResolvable } from 'discord.js'; +import { BushClient, BushThreadMemberResolvable } from '../discord-akairo/BushClient'; +import { BushThreadChannel } from './BushThreadChannel'; +import { BushThreadMember } from './BushThreadMember'; + +export class BushThreadMemberManager extends CachedManager<Snowflake, BushThreadMember, BushThreadMemberResolvable> { + public constructor(thread: BushThreadChannel, iterable?: Iterable<unknown>); + public declare readonly client: BushClient; + public thread: ThreadChannel; + public add(member: UserResolvable | '@me', reason?: string): Promise<Snowflake>; + public fetch(cache?: boolean): Promise<Collection<Snowflake, ThreadMember>>; + public remove(id: Snowflake | '@me', reason?: string): Promise<Snowflake>; +} diff --git a/src/lib/extensions/discord.js/BushThreadMemberManager.ts b/src/lib/extensions/discord.js/BushThreadMemberManager.ts deleted file mode 100644 index e585ee7..0000000 --- a/src/lib/extensions/discord.js/BushThreadMemberManager.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-empty-interface */ -import { ThreadMemberManager } from 'discord.js'; -import { BushClient } from '../discord-akairo/BushClient'; -import { BushThreadChannel } from './BushThreadChannel'; - -export class BushThreadMemberManager extends ThreadMemberManager { - public declare thread: BushThreadChannel; - public declare readonly client: BushClient; - - public constructor(thread: BushThreadChannel, iterable?: Iterable<unknown>) { - super(thread, iterable); - } -} diff --git a/src/lib/index.ts b/src/lib/index.ts index 73cee56..1e789e5 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -13,22 +13,16 @@ export * from './extensions/discord-akairo/BushTask'; export * from './extensions/discord-akairo/BushTaskHandler'; export * from './extensions/discord.js/BushActivity'; export * from './extensions/discord.js/BushApplicationCommand'; -export * from './extensions/discord.js/BushApplicationCommandManager'; -export * from './extensions/discord.js/BushApplicationCommandPermissionsManager'; export * from './extensions/discord.js/BushButtonInteraction'; export * from './extensions/discord.js/BushCategoryChannel'; export * from './extensions/discord.js/BushCommandInteraction'; export * from './extensions/discord.js/BushDMChannel'; export * from './extensions/discord.js/BushEmoji'; export * from './extensions/discord.js/BushGuild'; -export * from './extensions/discord.js/BushGuildApplicationCommandManager'; export * from './extensions/discord.js/BushGuildChannel'; export * from './extensions/discord.js/BushGuildEmoji'; -export * from './extensions/discord.js/BushGuildEmojiRoleManager'; export * from './extensions/discord.js/BushGuildMember'; -// export * from './extensions/discord.js/BushGuildMemberManager'; export * from './extensions/discord.js/BushMessage'; -export * from './extensions/discord.js/BushMessageManager'; export * from './extensions/discord.js/BushMessageReaction'; export * from './extensions/discord.js/BushNewsChannel'; export * from './extensions/discord.js/BushPresence'; @@ -40,9 +34,7 @@ export * from './extensions/discord.js/BushStageInstance'; export * from './extensions/discord.js/BushStoreChannel'; export * from './extensions/discord.js/BushTextChannel'; export * from './extensions/discord.js/BushThreadChannel'; -export * from './extensions/discord.js/BushThreadManager'; export * from './extensions/discord.js/BushThreadMember'; -export * from './extensions/discord.js/BushThreadMemberManager'; export * from './extensions/discord.js/BushUser'; export * from './extensions/discord.js/BushVoiceChannel'; export * from './extensions/discord.js/BushVoiceState'; diff --git a/src/lib/utils/BushConstants.ts b/src/lib/utils/BushConstants.ts index 5a99798..97f86c4 100644 --- a/src/lib/utils/BushConstants.ts +++ b/src/lib/utils/BushConstants.ts @@ -63,7 +63,7 @@ export class BushConstants { EMBED_LINKS: { name: 'Embed Links', important: false }, ATTACH_FILES: { name: 'Attach Files', important: false }, READ_MESSAGE_HISTORY: { name: 'Read Message History', important: false }, - MENTION_EVERYONE: { name: 'Mention @everyone, @here, and All Roles', important: true }, // name has a zero-width space to prevent accidents + MENTION_EVERYONE: { name: 'Mention @\u200Beveryone, @\u200Bhere, and All Roles', important: true }, // name has a zero-width space to prevent accidents USE_EXTERNAL_EMOJIS: { name: 'Use External Emoji', important: false }, VIEW_GUILD_INSIGHTS: { name: 'View Server Insights', important: true }, CONNECT: { name: 'Connect', important: false }, @@ -84,48 +84,51 @@ export class BushConstants { MANAGE_THREADS: { name: 'Manage Threads', important: true } }, + // prettier-ignore features: { - ANIMATED_ICON: { name: 'Animated Icon', important: false, emoji: '<:animatedIcon:850774498071412746>', weight: 14 }, - BANNER: { name: 'Banner', important: false, emoji: '<:banner:850786673150787614>', weight: 15 }, - COMMERCE: { name: 'Store Channels', important: true, emoji: '<:storeChannels:850786692432396338>', weight: 11 }, - COMMUNITY: { name: 'Community', important: false, emoji: '<:community:850786714271875094>', weight: 20 }, - DISCOVERABLE: { name: 'Discoverable', important: true, emoji: '<:discoverable:850786735360966656>', weight: 6 }, - ENABLED_DISCOVERABLE_BEFORE: { - name: 'Enabled Discovery Before', - important: false, - emoji: '<:enabledDiscoverableBefore:850786754670624828>', - weight: 7 - }, - FEATURABLE: { name: 'Featurable', important: true, emoji: '<:featurable:850786776372084756>', weight: 4 }, - INVITE_SPLASH: { name: 'Invite Splash', important: false, emoji: '<:inviteSplash:850786798246559754>', weight: 16 }, - MEMBER_VERIFICATION_GATE_ENABLED: { - name: 'Membership Verification Gate', - important: false, - emoji: '<:memberVerificationGateEnabled:850786829984858212>', - weight: 18 - }, - MONETIZATION_ENABLED: { name: 'Monetization Enabled', important: true, emoji: null, weight: 8 }, - MORE_EMOJI: { name: 'More Emoji', important: true, emoji: '<:moreEmoji:850786853497602080>', weight: 3 }, - MORE_STICKERS: { name: 'More Stickers', important: true, emoji: null, weight: 2 }, - NEWS: { - name: 'Announcement Channels', - important: false, - emoji: '<:announcementChannels:850790491796013067>', - weight: 17 - }, + VERIFIED: { name: 'Verified', important: true, emoji: '<:verified:850795049817473066>', weight: 0 }, PARTNERED: { name: 'Partnered', important: true, emoji: '<:partneredServer:850794851955507240>', weight: 1 }, - PREVIEW_ENABLED: { name: 'Preview Enabled', important: true, emoji: '<:previewEnabled:850790508266913823>', weight: 10 }, + MORE_STICKERS: { name: 'More Stickers', important: true, emoji: null, weight: 2 }, + MORE_EMOJI: { name: 'More Emoji', important: true, emoji: '<:moreEmoji:850786853497602080>', weight: 3 }, + FEATURABLE: { name: 'Featurable', important: true, emoji: '<:featurable:850786776372084756>', weight: 4 }, RELAY_ENABLED: { name: 'Relay Enabled', important: true, emoji: '<:relayEnabled:850790531441229834>', weight: 5 }, + DISCOVERABLE: { name: 'Discoverable', important: true, emoji: '<:discoverable:850786735360966656>', weight: 6 }, + ENABLED_DISCOVERABLE_BEFORE: { name: 'Enabled Discovery Before', important: false, emoji: '<:enabledDiscoverableBefore:850786754670624828>', weight: 7 }, + MONETIZATION_ENABLED: { name: 'Monetization Enabled', important: true, emoji: null, weight: 8 }, TICKETED_EVENTS_ENABLED: { name: 'Ticketed Events Enabled', important: true, emoji: null, weight: 9 }, + PREVIEW_ENABLED: { name: 'Preview Enabled', important: true, emoji: '<:previewEnabled:850790508266913823>', weight: 10 }, + COMMERCE: { name: 'Store Channels', important: true, emoji: '<:storeChannels:850786692432396338>', weight: 11 }, VANITY_URL: { name: 'Vanity URL', important: false, emoji: '<:vanityURL:850790553079644160>', weight: 12 }, - VERIFIED: { name: 'Verified', important: true, emoji: '<:verified:850795049817473066>', weight: 0 }, VIP_REGIONS: { name: 'VIP Regions', important: false, emoji: '<:VIPRegions:850794697496854538>', weight: 13 }, - WELCOME_SCREEN_ENABLED: { - name: 'Welcome Screen Enabled', - important: false, - emoji: '<:welcomeScreenEnabled:850790575875817504>', - weight: 19 - } + ANIMATED_ICON: { name: 'Animated Icon', important: false, emoji: '<:animatedIcon:850774498071412746>', weight: 14 }, + BANNER: { name: 'Banner', important: false, emoji: '<:banner:850786673150787614>', weight: 15 }, + INVITE_SPLASH: { name: 'Invite Splash', important: false, emoji: '<:inviteSplash:850786798246559754>', weight: 16 }, + PRIVATE_THREADS: { name: 'Private Threads', important: false, emoji: '<:privateThreads:869763711894700093>', weight: 17 }, + THREE_DAY_THREAD_ARCHIVE: { name: 'Three Day Thread Archive', important: false, emoji: '<:threeDayThreadArchive:869767841652564008>', weight: 19 }, + SEVEN_DAY_THREAD_ARCHIVE: { name: 'Seven Day Thread Archive', important: false, emoji: '<:sevenDayThreadArchive:869767896123998288>', weight: 20 }, + NEWS: { name: 'Announcement Channels', important: false, emoji: '<:announcementChannels:850790491796013067>', weight: 21 }, + MEMBER_VERIFICATION_GATE_ENABLED: { name: 'Membership Verification Gate', important: false, emoji: '<:memberVerificationGateEnabled:850786829984858212>', weight: 22 }, + WELCOME_SCREEN_ENABLED: { name: 'Welcome Screen Enabled', important: false, emoji: '<:welcomeScreenEnabled:850790575875817504>', weight: 23 }, + COMMUNITY: { name: 'Community', important: false, emoji: '<:community:850786714271875094>', weight: 24 }, + THREADS_ENABLED: {name: 'Threads Enabled', important: false, emoji: '<:threadsEnabled:869756035345317919>', weight: 24 }, + THREADS_ENABLED_TESTING: {name: 'Threads Enabled Testing', important: false, emoji: null, weight: 24 } + }, + + regions: { + 'automatic': ':united_nations: Automatic', + 'brazil': ':flag_br: Brazil', + 'europe': ':flag_eu: Europe', + 'hongkong': ':flag_hk: Hongkong', + 'india': ':flag_in: India', + 'japan': ':flag_jp: Japan', + 'russia': ':flag_ru: Russia', + 'singapore': ':flag_sg: Singapore', + 'southafrica': ':flag_za: South Africa', + 'sydney': ':flag_au: Sydney', + 'us-central': ':flag_us: US Central', + 'us-east': ':flag_us: US East', + 'us-south': ':flag_us: US South', + 'us-west': ':flag_us: US West' }, otherEmojis: { diff --git a/src/listeners/guild/syncUnban.ts b/src/listeners/guild/syncUnban.ts index 389aae6..00f260c 100644 --- a/src/listeners/guild/syncUnban.ts +++ b/src/listeners/guild/syncUnban.ts @@ -12,8 +12,8 @@ export default class SyncUnbanListener extends BushListener { public async exec(...[ban]: ClientEvents['guildBanRemove']): Promise<void> { const bans = await ActivePunishment.findAll({ where: { - user: ban.user, - guild: ban.guild, + user: ban.user.id, + guild: ban.guild.id, type: ActivePunishmentType.BAN } }); diff --git a/src/listeners/message/autoPublisher.ts b/src/listeners/message/autoPublisher.ts new file mode 100644 index 0000000..3941371 --- /dev/null +++ b/src/listeners/message/autoPublisher.ts @@ -0,0 +1,25 @@ +import { BushListener } from '../../lib'; +import { BushClientEvents } from '../../lib/extensions/discord.js/BushClientEvents'; + +export default class autoPublisherListener extends BushListener { + public constructor() { + super('autoPublisher', { + emitter: 'client', + event: 'messageCreate', + category: 'message' + }); + } + + public async exec(...[message]: BushClientEvents['messageCreate']): Promise<void> { + if (!message.guild) return; + const autoPublishChannels = await message.guild.getSetting('autoPublishChannels'); + if (autoPublishChannels) { + if (message.channel.type === 'GUILD_NEWS' && autoPublishChannels.some((x) => message.channel.id.includes(x))) { + const success = await message.crosspost().catch(() => false); + if (!success) + void client.console.warn('AutoPublisher', `Failed to publish <<${message.id}>> in <<${message.guild.name}>>.`); + void client.logger.log('AutoPublisher', `Published message <<${message.id}>> in <<${message.guild.name}>>.`); + } + } + } +} diff --git a/src/listeners/message/automodCreate.ts b/src/listeners/message/automodCreate.ts index b8057c5..b6718fc 100644 --- a/src/listeners/message/automodCreate.ts +++ b/src/listeners/message/automodCreate.ts @@ -10,7 +10,7 @@ export default class AutomodMessageCreateListener extends BushListener { }); } - async exec(...[message]: ClientEvents['messageCreate']): Promise<void> { + async exec(...[message]: ClientEvents['messageCreate']): Promise<unknown> { return await util.automod(message as BushMessage); } } diff --git a/src/listeners/message/automodUpdate.ts b/src/listeners/message/automodUpdate.ts index 9d17ef0..e455a3d 100644 --- a/src/listeners/message/automodUpdate.ts +++ b/src/listeners/message/automodUpdate.ts @@ -10,7 +10,7 @@ export default class AutomodMessageUpdateListener extends BushListener { }); } - async exec(...[message]: ClientEvents['messageUpdate']): Promise<void> { + async exec(...[message]: ClientEvents['messageUpdate']): Promise<unknown> { const fullMessage = message.partial ? await message.fetch() : (message as Message); return await util.automod(fullMessage as BushMessage); } diff --git a/src/listeners/message/blacklistedFile.ts b/src/listeners/message/blacklistedFile.ts new file mode 100644 index 0000000..93ed7b7 --- /dev/null +++ b/src/listeners/message/blacklistedFile.ts @@ -0,0 +1,159 @@ +import crypto from 'crypto'; +import got from 'got'; +import { BushListener } from '../../lib'; +import { BushClientEvents } from '../../lib/extensions/discord.js/BushClientEvents'; + +export default class BlacklistedFileListener extends BushListener { + private blacklistedFiles: { hash: string[]; name: string; description: string }[] = [ + { + hash: ['a0f5e30426234bc9d09306ffc9474422'], + name: 'Play twice audio', + description: 'weird audio files' + }, + { + hash: ['43e55abbcea67d9e6d7abfff944a8d0b'], + name: 'Flashy loud jumpscare', + description: 'flashy, loud gifs' + }, + { + hash: [ + '7a0831239e8c8368e96fb4cacd61b5f2', + '3bdb44bf3702f15d118f04fa63b927a9', + 'b6e45619a68c0e20749edb2412590b15', + 'bb8a27047518a8a7e420509af0e9e0ed', + 'f8076cd51e1ddab4ceded26a764af160', + '1757f0442b5e337bba0340f7b116e6f7', + 'f59185531f0dfa9bdd323b86f796c3bd', + '2825d3d82af65de210e638911e49b3a2', + '5256c3c18b367552e55e463a60af7760' + ], + name: 'Discord crash video/gif', + description: 'media that crashes discord' + }, + { + hash: ['1fd6b3f255946236fd55d3e4bef01c5f', '157d374ec41adeef9601fd87e23f4bf5'], + name: 'Repost lobster video', + description: 'images encouraging spam' + }, + { + hash: ['10ad124fc47cd9b7de2ec629bc945bf2'], + name: 'Jarvis message top user troll thingy', + description: 'gifs encouraging spam' + }, + { + hash: ['312cda77d3e1f5fa00f482aed3b36f6f'], + name: 'Discord token stealer', + description: 'discord token stealers' + }, + { + hash: ['f37f772246db9d690dee0f581682dfb7'], + name: 'Weird nsfw dog vid', + description: 'weird nsfw videos' + }, + { + hash: ['5a5bfdf02a0224d3468499d099ec4eee'], + name: 'Virus (or at least flags antiviruses)', + description: 'viruses' + } + ]; + + constructor() { + super('blacklistedFile', { + emitter: 'client', + event: 'messageCreate', + category: 'message' + }); + } + + public async exec(...[message]: BushClientEvents['messageCreate']): Promise<void> { + const guildWhitelist = [ + client.consts.mappings.guilds.bush, + client.consts.mappings.guilds.tree, + client.consts.mappings.guilds.space_ship + ]; + if (!guildWhitelist.includes(message.guild?.id)) return; + const embedAttachments = message.embeds.filter((e) => ['image', 'video', 'gifv'].includes(e.type)); + const foundEmojis = [...message.content.matchAll(/<(?<animated>a?):\w+:(?<id>\d+)>/g)]; + if (message.attachments.size + embedAttachments.length + foundEmojis.length < 1) return; + const foundFiles = [] as { + name: string; + hash: string[]; + description: string; + }[]; + for (const attachment of message.attachments) { + try { + const req = await got.get(attachment[1].proxyURL); + const rawHash = crypto.createHash('md5'); + rawHash.update(req.rawBody.toString('binary')); + const hash = rawHash.digest('hex'); + const blacklistData = this.blacklistedFiles.find((h) => h.hash.some((h) => h === hash)); + if (blacklistData !== undefined) { + foundFiles.push(blacklistData); + } + } catch { + continue; + } + } + for (const attachment of embedAttachments) { + try { + const req = await got.get(attachment.url); + const rawHash = crypto.createHash('md5'); + rawHash.update(req.rawBody.toString('binary')); + const hash = rawHash.digest('hex'); + const blacklistData = this.blacklistedFiles.find((h) => h.hash.some((h) => h === hash)); + if (blacklistData !== undefined) { + foundFiles.push(blacklistData); + } + } catch { + continue; + } + } + for (const attachment of foundEmojis) { + try { + const req = await got.get( + `https://cdn.discordapp.com/emojis/${attachment.groups.id}.${attachment.groups.animated === 'a' ? 'gif' : 'png'}` + ); + const rawHash = crypto.createHash('md5'); + rawHash.update(req.rawBody.toString('binary')); + const hash = rawHash.digest('hex'); + const blacklistData = this.blacklistedFiles.find((h) => h.hash.some((h) => h === hash)); + if (blacklistData !== undefined) { + foundFiles.push(blacklistData); + } + } catch { + continue; + } + } + if (foundFiles.length > 0) { + try { + for (let i = 0; i < foundFiles.length; i++) { + if (foundFiles[i].name === 'Discord crash video' && !this.client.ownerID.includes(message.author.id)) { + await message.member.roles.add('748912426581229690'); + } + } + await message.delete(); + + await message.util.send( + `<@!${message.author.id}>, please do not send ${foundFiles.map((f) => f.description).join(' or ')}.` + ); + if (message.channel.type === 'DM') return; + void this.client.console.info( + 'BlacklistedFile', + `Deleted <<${foundFiles.map((f) => f.description).join(' and ')}>> sent by <<${message.author.tag}>> in ${ + message.channel.name + }.` + ); + } catch (e) { + void message.util.send( + `<@!${message.author.id}>, please do not send ${foundFiles.map((f) => f.description).join(' or ')}.` + ); + void this.client.console.warn( + 'BlacklistedFile', + `Failed to delete <<${foundFiles.map((f) => f.description).join(' and ')}>> sent by <<${message.author.tag}>> in <<${ + message.channel.type === 'DM' ? `${message.channel.recipient.tag}'s DMs` : message.channel.name + }>>.` + ); + } + } + } +} diff --git a/src/listeners/message/booster.ts b/src/listeners/message/booster.ts new file mode 100644 index 0000000..a042ad1 --- /dev/null +++ b/src/listeners/message/booster.ts @@ -0,0 +1,20 @@ +import { BushListener } from '../../lib'; +import { BushClientEvents } from '../../lib/extensions/discord.js/BushClientEvents'; + +export default class BoosterMessageListener extends BushListener { + public constructor() { + super('boosterMessage', { + emitter: 'client', + event: 'messageCreate', + category: 'message' + }); + } + + public async exec(...[message]: BushClientEvents['messageCreate']): Promise<unknown> { + if (message.type === 'USER_PREMIUM_GUILD_SUBSCRIPTION' && message.guild.id === this.client.consts.mappings.guilds.bush) { + return await message.react('<:nitroboost:785160348885975062>').catch(() => { + void this.client.console.warn('BoosterMessage', `Failed to react to <<${message.id}>>.`); + }); + } + } +} diff --git a/src/listeners/message/directMessage.ts b/src/listeners/message/directMessage.ts new file mode 100644 index 0000000..3dc84ab --- /dev/null +++ b/src/listeners/message/directMessage.ts @@ -0,0 +1,44 @@ +import { MessageEmbed } from 'discord.js'; +import { BushListener } from '../../lib'; +import { BushClientEvents } from '../../lib/extensions/discord.js/BushClientEvents'; + +export default class DirectMessageListener extends BushListener { + public constructor() { + super('directMessage', { + emitter: 'client', + event: 'messageCreate', + category: 'message' + }); + } + + public async exec(...[message]: BushClientEvents['messageCreate']): Promise<void> { + if (message.channel.type === 'DM') { + if (!(message.author.id == this.client.user.id) && message.author.bot) return; + const dmLogEmbed = new MessageEmbed().setTimestamp().setFooter(`User ID • ${message.author.id}`); + + if (message.author.id != this.client.user.id) { + dmLogEmbed + .setAuthor(`From: ${message.author.username}`, `${message.author.displayAvatarURL({ dynamic: true })}`) + .setDescription(`**DM:**\n${message}`) + .setColor(this.client.util.colors.blue); + } else { + dmLogEmbed + .setAuthor( + `To: ${message.channel.recipient.username}`, + `${message.channel.recipient.displayAvatarURL({ dynamic: true })}` + ) + .setDescription(`**DM:**\n${message}`) + .setColor(this.client.util.colors.red) + .setTimestamp() + .setFooter(`ID • ${message.author.id}`); + } + if (message.attachments.filter((a) => typeof a.size == 'number').size == 1) { + dmLogEmbed.setImage(message.attachments.filter((a) => typeof a.size == 'number').first().proxyURL); + } else if (message.attachments.size > 0) { + dmLogEmbed.addField('Attachments', message.attachments.map((a) => a.proxyURL).join('\n')); + } + const dmChannel = await util.getConfigChannel('dm'); + await dmChannel.send({ embeds: [dmLogEmbed] }); + } + } +} diff --git a/src/listeners/message/verbose.ts b/src/listeners/message/verbose.ts new file mode 100644 index 0000000..45bf1de --- /dev/null +++ b/src/listeners/message/verbose.ts @@ -0,0 +1,20 @@ +import { BushListener } from '../../lib'; +import { BushClientEvents } from '../../lib/extensions/discord.js/BushClientEvents'; + +export default class MessageVerboseListener extends BushListener { + public constructor() { + super('messageVerbose', { + emitter: 'client', + event: 'messageCreate', + category: 'message' + }); + } + + public exec(...[message]: BushClientEvents['messageCreate']): Promise<void> { + if (message.channel?.type === 'DM') return; + void this.client.console.verbose( + 'Message', + `A message was sent by <<${message.author.tag}>> in <<${message.channel.name}>> in <<${message.guild.name}>>.` + ); + } +} diff --git a/src/listeners/other/consoleListener.ts b/src/listeners/other/consoleListener.ts index b6f53d8..4e72ec9 100644 --- a/src/listeners/other/consoleListener.ts +++ b/src/listeners/other/consoleListener.ts @@ -17,7 +17,6 @@ export default class ConsoleListener extends BushListener { const sh = promisify(exec), bot = client, config = client.config, - client = client, { ActivePunishment, Global, Guild, Level, ModLog, StickyRole } = await import('@lib'), { ButtonInteraction, |