aboutsummaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/extensions/discord-akairo/BushClientUtil.ts81
-rw-r--r--src/lib/extensions/discord.js/BushBaseGuildTextChannel.ts10
-rw-r--r--src/lib/extensions/discord.js/BushGuild.ts63
-rw-r--r--src/lib/extensions/discord.js/BushGuildMember.ts17
-rw-r--r--src/lib/extensions/discord.js/BushNewsChannel.ts4
-rw-r--r--src/lib/extensions/discord.js/BushTextChannel.ts4
-rw-r--r--src/lib/models/ModLog.ts2
-rw-r--r--src/lib/utils/BushConstants.ts69
8 files changed, 211 insertions, 39 deletions
diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts
index a7dd535..55f525b 100644
--- a/src/lib/extensions/discord-akairo/BushClientUtil.ts
+++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts
@@ -13,7 +13,9 @@ import {
Global,
Guild,
ModLog,
- ModLogType
+ ModLogType,
+ Pronoun,
+ PronounCode
} from '@lib';
import { exec } from 'child_process';
import {
@@ -27,7 +29,6 @@ import {
} from 'discord-akairo';
import { APIMessage } from 'discord-api-types';
import {
- ButtonInteraction,
ColorResolvable,
CommandInteraction,
Constants,
@@ -41,13 +42,16 @@ import {
MessageOptions,
Snowflake,
TextChannel,
+ ThreadMember,
User,
+ UserResolvable,
Util as DiscordUtil
} from 'discord.js';
import got from 'got';
import humanizeDuration from 'humanize-duration';
import _ from 'lodash';
import moment from 'moment';
+import fetch from 'node-fetch';
import { inspect, InspectOptions, promisify } from 'util';
import CommandErrorListener from '../../../listeners/commands/commandError';
import { ActivePunishment, ActivePunishmentType } from '../../models/ActivePunishment';
@@ -690,7 +694,7 @@ export class BushClientUtil extends ClientUtil {
});
const style = Constants.MessageButtonStyles.PRIMARY;
- let curPage = startOn ? startOn - 1 : undefined ?? 0;
+ let curPage = startOn ? startOn - 1 : 0;
if (typeof embeds !== 'object') throw new Error('embeds must be an object');
const msg = (await message.util.reply({
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
@@ -698,11 +702,11 @@ export class BushClientUtil extends ClientUtil {
embeds: [embeds[curPage]],
components: [getPaginationRow()]
})) as Message;
- const filter = (interaction: ButtonInteraction) =>
- interaction.customId.startsWith('paginate_') && interaction.message == msg;
+ const filter = (interaction: MessageComponentInteraction) =>
+ interaction.customId.startsWith('paginate_') && interaction.message.id === msg.id;
const collector = msg.createMessageComponentCollector({ filter, time: 300000 });
collector.on('collect', async (interaction: MessageComponentInteraction) => {
- if (interaction.user.id == message.author.id || client.config.owners.includes(interaction.user.id)) {
+ if (interaction.user.id === message.author.id || client.config.owners.includes(interaction.user.id)) {
switch (interaction.customId) {
case 'paginate_beginning': {
curPage = 0;
@@ -722,7 +726,11 @@ export class BushClientUtil extends ClientUtil {
}
} else {
await interaction
- ?.update({ content: `${text ? text + '\n' : ''}Command closed by user.`, embeds: [], components: [] })
+ ?.update({
+ content: `${text ? text + '\n' : ''}Command closed by user.`,
+ embeds: [],
+ components: []
+ })
.catch(() => undefined);
}
return;
@@ -744,7 +752,13 @@ export class BushClientUtil extends ClientUtil {
});
collector.on('end', async () => {
- await msg.edit({ content: text, embeds: [embeds[curPage]], components: [getPaginationRow(true)] }).catch(() => undefined);
+ await msg
+ .edit({
+ content: text,
+ embeds: [embeds[curPage]],
+ components: [getPaginationRow(true)]
+ })
+ .catch(() => undefined);
});
async function edit(interaction: MessageComponentInteraction): Promise<void> {
@@ -766,7 +780,12 @@ export class BushClientUtil extends ClientUtil {
emoji: paginateEmojis.back,
disabled: disableAll || curPage == 0
}),
- new MessageButton({ style, customId: 'paginate_stop', emoji: paginateEmojis.stop, disabled: disableAll }),
+ new MessageButton({
+ style,
+ customId: 'paginate_stop',
+ emoji: paginateEmojis.stop,
+ disabled: disableAll
+ }),
new MessageButton({
style,
customId: 'paginate_next',
@@ -790,7 +809,8 @@ export class BushClientUtil extends ClientUtil {
const paginateEmojis = this.#paginateEmojis;
updateOptions();
const msg = (await message.util.reply(options as MessageOptions & { split?: false })) as Message;
- const filter = (interaction: ButtonInteraction) => interaction.customId == 'paginate__stop' && interaction.message == msg;
+ const filter = (interaction: MessageComponentInteraction) =>
+ interaction.customId == 'paginate__stop' && interaction.message == msg;
const collector = msg.createMessageComponentCollector({ filter, time: 300000 });
collector.on('collect', async (interaction: MessageComponentInteraction) => {
if (interaction.user.id == message.author.id || client.config.owners.includes(interaction.user.id)) {
@@ -1082,14 +1102,14 @@ export class BushClientUtil extends ClientUtil {
type: ModLogType;
user: BushGuildMemberResolvable;
moderator: BushGuildMemberResolvable;
- reason: string | undefined;
+ reason: string | undefined | null;
duration?: number;
guild: BushGuildResolvable;
},
getCaseNumber = false
): Promise<{ log: ModLog | null; caseNum: number | null }> {
- const user = client.users.resolveId(options.user)!;
- const moderator = client.users.resolveId(options.moderator)!;
+ const user = (await util.resolveNonCachedUser(options.user))!.id;
+ const moderator = (await util.resolveNonCachedUser(options.moderator))!.id;
const guild = client.guilds.resolveId(options.guild)!;
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const duration = options.duration || undefined;
@@ -1131,10 +1151,10 @@ export class BushClientUtil extends ClientUtil {
modlog?: string;
extraInfo?: Snowflake;
}): Promise<ActivePunishment | null> {
- const expires = options.duration ? new Date(new Date().getTime() + options.duration) : undefined;
- client.console.debug(expires);
+ const expires = options.duration ? new Date(new Date().getTime() + options.duration ?? 0) : undefined;
+ client.console.debug(expires, 1);
client.console.debug(typeof expires);
- const user = client.users.resolveId(options.user)!;
+ const user = (await util.resolveNonCachedUser(options.user))!.id;
const guild = client.guilds.resolveId(options.guild)!;
const type = this.#findTypeEnum(options.type)!;
@@ -1152,7 +1172,7 @@ export class BushClientUtil extends ClientUtil {
user: BushGuildMemberResolvable;
guild: BushGuildResolvable;
}): Promise<boolean> {
- const user = client.users.resolveId(options.user);
+ const user = await util.resolveNonCachedUser(options.user);
const guild = client.guilds.resolveId(options.guild);
const type = this.#findTypeEnum(options.type);
@@ -1366,6 +1386,33 @@ export class BushClientUtil extends ClientUtil {
});
}
+ public async resolveNonCachedUser(user: UserResolvable | undefined | null): Promise<User | undefined> {
+ if (!user) return undefined;
+ const id =
+ user instanceof User || user instanceof GuildMember || user instanceof ThreadMember
+ ? user.id
+ : user instanceof Message
+ ? user.author.id
+ : typeof user === 'string'
+ ? user
+ : undefined;
+ if (!id) return undefined;
+ else return await client.users.fetch(id).catch(() => undefined);
+ }
+
+ public async getPronounsOf(user: User | Snowflake): Promise<Pronoun | undefined> {
+ const _user = await this.resolveNonCachedUser(user);
+ if (!_user) throw new Error(`Cannot find user ${user}`);
+ const apiRes: { pronouns: PronounCode } | undefined = await fetch(
+ `https://pronoundb.org/api/v1/lookup?platform=discord&id=${_user.id}`
+ ).then(async (r) => (r.ok ? ((await r.json()) as { pronouns: PronounCode }) : undefined));
+
+ if (!apiRes) return undefined;
+ if (!apiRes.pronouns) throw new Error('apiRes.pronouns is undefined');
+
+ return client.constants.pronounMapping[apiRes.pronouns];
+ }
+
//~ modified from https://stackoverflow.com/questions/31054910/get-functions-methods-of-a-class
//~ answer by Bruno Grieder
//~ public getMethods(obj: any): string {
diff --git a/src/lib/extensions/discord.js/BushBaseGuildTextChannel.ts b/src/lib/extensions/discord.js/BushBaseGuildTextChannel.ts
index 734642e..78cfada 100644
--- a/src/lib/extensions/discord.js/BushBaseGuildTextChannel.ts
+++ b/src/lib/extensions/discord.js/BushBaseGuildTextChannel.ts
@@ -1,4 +1,10 @@
-import { AllowedThreadTypeForTextChannel, BaseGuildTextChannel, Collection, Snowflake } from 'discord.js';
+import {
+ AllowedThreadTypeForNewsChannel,
+ AllowedThreadTypeForTextChannel,
+ BaseGuildTextChannel,
+ Collection,
+ Snowflake
+} from 'discord.js';
import { RawGuildChannelData } from 'discord.js/typings/rawDataTypes';
import { BushCategoryChannel, BushClient, BushGuildMember } from '../..';
import { BushGuild } from './BushGuild';
@@ -10,7 +16,7 @@ export class BushBaseGuildTextChannel extends BaseGuildTextChannel {
super(guild, data, client, immediatePatch);
}
public declare messages: BushMessageManager;
- public declare threads: BushThreadManager<AllowedThreadTypeForTextChannel>;
+ public declare threads: BushThreadManager<AllowedThreadTypeForTextChannel | AllowedThreadTypeForNewsChannel>;
public declare readonly client: BushClient;
public declare guild: BushGuild;
public declare readonly members: Collection<Snowflake, BushGuildMember>;
diff --git a/src/lib/extensions/discord.js/BushGuild.ts b/src/lib/extensions/discord.js/BushGuild.ts
index 09e355c..12db49a 100644
--- a/src/lib/extensions/discord.js/BushGuild.ts
+++ b/src/lib/extensions/discord.js/BushGuild.ts
@@ -1,4 +1,4 @@
-import { Guild } from 'discord.js';
+import { Guild, UserResolvable } from 'discord.js';
import { RawGuildData } from 'discord.js/typings/rawDataTypes';
import { Guild as GuildDB, GuildFeatures, GuildModel } from '../../models/Guild';
import { ModLogType } from '../../models/ModLog';
@@ -52,9 +52,56 @@ export class BushGuild extends Guild {
return await row.save();
}
+ public async ban(options: {
+ user: BushUserResolvable | UserResolvable;
+ reason?: string | null;
+ moderator?: BushUserResolvable;
+ duration?: number;
+ deleteDays?: number;
+ }): Promise<
+ 'success' | 'missing permissions' | 'error banning' | 'error creating modlog entry' | 'error creating ban entry'
+ > {
+ // checks
+ if (!this.me!.permissions.has('BAN_MEMBERS')) return 'missing permissions';
+
+ const moderator = (await util.resolveNonCachedUser(options.moderator!)) ?? client.user!;
+
+ // ban
+ const banSuccess = await this.bans
+ .create(options.user, {
+ reason: `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`,
+ days: options.deleteDays
+ })
+ .catch(() => false);
+ if (!banSuccess) return 'error banning';
+
+ // add modlog entry
+ const { log: modlog } = await util.createModLogEntry({
+ type: options.duration ? ModLogType.TEMP_BAN : ModLogType.PERM_BAN,
+ user: options.user as BushUserResolvable,
+ moderator: moderator.id,
+ reason: options.reason,
+ duration: options.duration,
+ guild: this
+ });
+ if (!modlog) return 'error creating modlog entry';
+
+ // add punishment entry so they can be unbanned later
+ const punishmentEntrySuccess = await util.createPunishmentEntry({
+ type: 'ban',
+ user: options.user as BushUserResolvable,
+ guild: this,
+ duration: options.duration,
+ modlog: modlog.id
+ });
+ if (!punishmentEntrySuccess) return 'error creating ban entry';
+
+ return 'success';
+ }
+
public async unban(options: {
user: BushUserResolvable | BushUser;
- reason?: string;
+ reason?: string | null;
moderator?: BushUserResolvable;
}): Promise<
| 'success'
@@ -64,13 +111,13 @@ export class BushGuild extends Guild {
| 'error creating modlog entry'
| 'error removing ban entry'
> {
- const user = client.users.resolveId(options.user)!;
- const moderator = client.users.cache.get(client.users.resolveId(options.moderator!)!)!;
+ const user = (await util.resolveNonCachedUser(options.user))!;
+ const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.me))!;
const bans = await this.bans.fetch();
let notBanned = false;
- if (!bans.has(user)) notBanned = true;
+ if (!bans.has(user.id)) notBanned = true;
const unbanSuccess = await this.bans
.remove(user, `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`)
@@ -86,7 +133,7 @@ export class BushGuild extends Guild {
// add modlog entry
const modlog = await util.createModLogEntry({
type: ModLogType.UNBAN,
- user,
+ user: user.id,
moderator: moderator.id,
reason: options.reason,
guild: this
@@ -96,12 +143,12 @@ export class BushGuild extends Guild {
// remove punishment entry
const removePunishmentEntrySuccess = await util.removePunishmentEntry({
type: 'ban',
- user,
+ user: user.id,
guild: this
});
if (!removePunishmentEntrySuccess) return 'error removing ban entry';
- const userObject = client.users.cache.get(user);
+ const userObject = client.users.cache.get(user.id);
userObject?.send(`You have been unbanned from **${this}** for **${options.reason ?? 'No reason provided'}**.`);
diff --git a/src/lib/extensions/discord.js/BushGuildMember.ts b/src/lib/extensions/discord.js/BushGuildMember.ts
index f71a435..67fa2fa 100644
--- a/src/lib/extensions/discord.js/BushGuildMember.ts
+++ b/src/lib/extensions/discord.js/BushGuildMember.ts
@@ -8,7 +8,7 @@ import { BushRole } from './BushRole';
import { BushUser } from './BushUser';
interface BushPunishmentOptions {
- reason?: string;
+ reason?: string | null;
moderator?: BushUserResolvable;
}
@@ -87,7 +87,8 @@ export class BushGuildMember extends GuildMember {
}
public async warn(options: BushPunishmentOptions): Promise<{ result: WarnResponse | null; caseNum: number | null }> {
- const moderator = client.users.cache.get(client.users.resolveId(options.moderator!)!) ?? client.user!;
+ const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!;
+
// add modlog entry
const result = await util.createModLogEntry(
{
@@ -119,7 +120,7 @@ export class BushGuildMember extends GuildMember {
const ifShouldAddRole = this.#checkIfShouldAddRole(options.role);
if (ifShouldAddRole !== true) return ifShouldAddRole;
- const moderator = client.users.cache.get(client.users.resolveId(options.moderator!)!) ?? client.user!;
+ const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!;
if (options.addToModlog || options.duration) {
const { log: modlog } = options.addToModlog
@@ -159,7 +160,7 @@ export class BushGuildMember extends GuildMember {
const ifShouldAddRole = this.#checkIfShouldAddRole(options.role);
if (ifShouldAddRole !== true) return ifShouldAddRole;
- const moderator = client.users.cache.get(client.users.resolveId(options.moderator!)!) ?? client.user!;
+ const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!;
if (options.addToModlog) {
const { log: modlog } = await util.createModLogEntry({
@@ -207,7 +208,7 @@ export class BushGuildMember extends GuildMember {
if (!muteRole) return 'invalid mute role';
if (muteRole.position >= this.guild.me!.roles.highest.position || muteRole.managed) return 'mute role not manageable';
- const moderator = client.users.cache.get(client.users.resolveId(options.moderator!)!) ?? client.user!;
+ const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!;
// add role
const muteSuccess = await this.roles
@@ -264,7 +265,7 @@ export class BushGuildMember extends GuildMember {
if (!muteRole) return 'invalid mute role';
if (muteRole.position >= this.guild.me!.roles.highest.position || muteRole.managed) return 'mute role not manageable';
- const moderator = client.users.cache.get(client.users.resolveId(options.moderator!)!) ?? client.user!;
+ const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!;
//remove role
const muteSuccess = await this.roles
@@ -309,7 +310,7 @@ export class BushGuildMember extends GuildMember {
// checks
if (!this.guild.me?.permissions.has('KICK_MEMBERS') || !this.kickable) return 'missing permissions';
- const moderator = client.users.cache.get(client.users.resolveId(options.moderator!)!) ?? client.user!;
+ const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!;
// dm user
const ending = await this.guild.getSetting('punishmentEnding');
@@ -340,7 +341,7 @@ export class BushGuildMember extends GuildMember {
// checks
if (!this.guild.me!.permissions.has('BAN_MEMBERS') || !this.bannable) return 'missing permissions';
- const moderator = client.users.cache.get(client.users.resolveId(options.moderator!)!) ?? client.user!;
+ const moderator = (await util.resolveNonCachedUser(options.moderator ?? this.guild.me))!;
// dm user
const ending = await this.guild.getSetting('punishmentEnding');
diff --git a/src/lib/extensions/discord.js/BushNewsChannel.ts b/src/lib/extensions/discord.js/BushNewsChannel.ts
index f44ff4a..d770132 100644
--- a/src/lib/extensions/discord.js/BushNewsChannel.ts
+++ b/src/lib/extensions/discord.js/BushNewsChannel.ts
@@ -1,4 +1,4 @@
-import { AllowedThreadTypeForTextChannel, Collection, NewsChannel, Snowflake } from 'discord.js';
+import { AllowedThreadTypeForNewsChannel, Collection, NewsChannel, Snowflake } from 'discord.js';
import { BushClient } from '../discord-akairo/BushClient';
import { BushGuild } from './BushGuild';
import { BushGuildMember } from './BushGuildMember';
@@ -7,8 +7,8 @@ import { BushThreadManager } from './BushThreadManager';
export class BushNewsChannel extends NewsChannel {
public declare readonly client: BushClient;
+ public declare threads: BushThreadManager<AllowedThreadTypeForNewsChannel>;
public declare guild: BushGuild;
public declare messages: BushMessageManager;
public declare members: Collection<Snowflake, BushGuildMember>;
- public declare threads: BushThreadManager<AllowedThreadTypeForTextChannel>;
}
diff --git a/src/lib/extensions/discord.js/BushTextChannel.ts b/src/lib/extensions/discord.js/BushTextChannel.ts
index db3ad8f..1d4d7fe 100644
--- a/src/lib/extensions/discord.js/BushTextChannel.ts
+++ b/src/lib/extensions/discord.js/BushTextChannel.ts
@@ -1,13 +1,15 @@
-import { TextChannel } from 'discord.js';
+import { AllowedThreadTypeForTextChannel, TextChannel } from 'discord.js';
import { RawGuildChannelData } from 'discord.js/typings/rawDataTypes';
import { BushClient } from '../discord-akairo/BushClient';
import { BushGuild } from './BushGuild';
import { BushMessageManager } from './BushMessageManager';
+import { BushThreadManager } from './BushThreadManager';
export class BushTextChannel extends TextChannel {
public declare readonly client: BushClient;
public declare guild: BushGuild;
public declare messages: BushMessageManager;
+ public declare threads: BushThreadManager<AllowedThreadTypeForTextChannel>;
public constructor(guild: BushGuild, data?: RawGuildChannelData) {
super(guild, data);
}
diff --git a/src/lib/models/ModLog.ts b/src/lib/models/ModLog.ts
index 7787375..0be1ea7 100644
--- a/src/lib/models/ModLog.ts
+++ b/src/lib/models/ModLog.ts
@@ -37,7 +37,7 @@ export interface ModLogModelCreationAttributes {
type: ModLogType;
user: Snowflake;
moderator: Snowflake;
- reason?: string;
+ reason?: string | null;
duration?: number;
guild: Snowflake;
evidence?: string;
diff --git a/src/lib/utils/BushConstants.ts b/src/lib/utils/BushConstants.ts
index f2ca327..17880cb 100644
--- a/src/lib/utils/BushConstants.ts
+++ b/src/lib/utils/BushConstants.ts
@@ -22,6 +22,51 @@ interface bushColors {
orange: '#E86100';
}
+export type PronounCode =
+ | 'unspecified'
+ | 'hh'
+ | 'hi'
+ | 'hs'
+ | 'ht'
+ | 'ih'
+ | 'ii'
+ | 'is'
+ | 'it'
+ | 'shh'
+ | 'sh'
+ | 'si'
+ | 'st'
+ | 'th'
+ | 'ti'
+ | 'ts'
+ | 'tt'
+ | 'any'
+ | 'other'
+ | 'ask'
+ | 'avoid';
+export type Pronoun =
+ | 'Unspecified'
+ | 'He/Him'
+ | 'He/It'
+ | 'He/She'
+ | 'He/They'
+ | 'It/Him'
+ | 'It/Its'
+ | 'It/She'
+ | 'It/They'
+ | 'She/He'
+ | 'She/Her'
+ | 'She/It'
+ | 'She/They'
+ | 'They/He'
+ | 'They/It'
+ | 'They/She'
+ | 'They/Them'
+ | 'Any pronouns'
+ | 'Other pronouns'
+ | 'Ask me my pronouns'
+ | 'Avoid pronouns, use my name';
+
export class BushConstants {
public static emojis = {
success: '<:success:837109864101707807>',
@@ -108,6 +153,30 @@ export class BushConstants {
discordEmoji: /<a?:(?<name>[a-zA-Z0-9\_]+):(?<id>\d{15,21})>/im
};
+ public static pronounMapping: { [x in PronounCode]: Pronoun } = {
+ unspecified: 'Unspecified',
+ hh: 'He/Him',
+ hi: 'He/It',
+ hs: 'He/She',
+ ht: 'He/They',
+ ih: 'It/Him',
+ ii: 'It/Its',
+ is: 'It/She',
+ it: 'It/They',
+ shh: 'She/He',
+ sh: 'She/Her',
+ si: 'She/It',
+ st: 'She/They',
+ th: 'They/He',
+ ti: 'They/It',
+ ts: 'They/She',
+ tt: 'They/Them',
+ any: 'Any pronouns',
+ other: 'Other pronouns',
+ ask: 'Ask me my pronouns',
+ avoid: 'Avoid pronouns, use my name'
+ };
+
/** A bunch of mappings */
public static mappings = {
guilds: {