aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/commands/info/pronouns.ts62
-rw-r--r--src/commands/info/userInfo.ts5
-rw-r--r--src/commands/leveling/leaderboard.ts2
-rw-r--r--src/commands/moderation/ban.ts51
-rw-r--r--src/commands/moderation/kick.ts5
-rw-r--r--src/commands/moderation/modlog.ts9
-rw-r--r--src/commands/moderation/mute.ts5
-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
15 files changed, 273 insertions, 116 deletions
diff --git a/src/commands/info/pronouns.ts b/src/commands/info/pronouns.ts
index ea20d41..77612da 100644
--- a/src/commands/info/pronouns.ts
+++ b/src/commands/info/pronouns.ts
@@ -1,32 +1,6 @@
import { BushCommand, BushMessage, BushSlashMessage } from '@lib';
import { Snowflake } from 'discord-api-types';
import { MessageEmbed, User } from 'discord.js';
-import got, { HTTPError } from 'got';
-
-export const pronounMapping = {
- 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'
-};
-export type pronounsType = keyof typeof pronounMapping;
export default class PronounsCommand extends BushCommand {
public constructor() {
@@ -62,43 +36,31 @@ export default class PronounsCommand extends BushCommand {
});
}
override async exec(message: BushMessage | BushSlashMessage, args: { user?: User | Snowflake }): Promise<unknown> {
- const user =
- args?.user === undefined || args?.user === null
- ? message.author
- : typeof args.user === 'object'
- ? args.user
- : await client.users.fetch(`${args.user}`).catch(() => undefined);
+ const user = (await util.resolveNonCachedUser(args.user)) ?? message.author;
- if (user === undefined) return message.util.reply(`${util.emojis.error} Invalid user.`);
+ if (!user) return message.util.reply(`${util.emojis.error} Invalid user.`);
const author = user.id === message.author.id;
- try {
- const apiRes: { pronouns: pronounsType } = await got
- .get(`https://pronoundb.org/api/v1/lookup?platform=discord&id=${user.id}`)
- .json();
+
+ const pronouns = await util.getPronounsOf(user);
+ if (!pronouns) {
+ return await message.util.reply(
+ `${author ? 'You do' : `${user.tag} does`} not appear to have any pronouns set. Please ${
+ author ? '' : 'tell them to'
+ } go to https://pronoundb.org/ and set ${author ? 'your' : 'their'} pronouns.`
+ );
+ } else {
return await message.util.reply({
embeds: [
new MessageEmbed({
title: `${author ? 'Your' : `${user.tag}'s`} pronouns:`,
- description: pronounMapping[apiRes.pronouns],
+ description: pronouns,
footer: {
text: 'Data provided by https://pronoundb.org/'
}
})
]
});
- } catch (e) {
- if (e instanceof HTTPError && e.response.statusCode === 404) {
- if (author) {
- return await message.util.reply(
- 'You do not appear to have any pronouns set. Please go to https://pronoundb.org/ and set your pronouns.'
- );
- } else {
- return await message.util.reply(
- `${user.tag} does not appear to have any pronouns set. Please tell them to go to https://pronoundb.org/ and set their pronouns.`
- );
- }
- } else throw e;
}
}
}
diff --git a/src/commands/info/userInfo.ts b/src/commands/info/userInfo.ts
index ae204f7..9fddd67 100644
--- a/src/commands/info/userInfo.ts
+++ b/src/commands/info/userInfo.ts
@@ -96,6 +96,11 @@ export default class UserInfoCommand extends BushCommand {
`**ID:** ${user.id}`,
`**Created: **${createdAt} (${createdAtDelta} ago)`
];
+ if (user.accentColor !== null) generalInfo.push(`**Accent Color:** ${user.hexAccentColor}`);
+ if (user.banner) generalInfo.push(`**Banner**: [link](${user.bannerURL({ dynamic: true, format: 'png' })})`);
+ const pronouns = await util.getPronounsOf(user);
+ if (pronouns) generalInfo.push(`**Pronouns:** ${pronouns}`);
+
userEmbed.addField('» General Info', generalInfo.join('\n'));
// Server User Info
diff --git a/src/commands/leveling/leaderboard.ts b/src/commands/leveling/leaderboard.ts
index b8838b7..d29c15e 100644
--- a/src/commands/leveling/leaderboard.ts
+++ b/src/commands/leveling/leaderboard.ts
@@ -47,6 +47,6 @@ export default class LeaderboardCommand extends BushCommand {
const embeds = chunked.map((c) =>
new MessageEmbed().setTitle(`${message.guild!.name}'s Leaderboard`).setDescription(c.join('\n'))
);
- return await util.buttonPaginate(message, embeds, null, true, args?.page ?? undefined);
+ return await util.buttonPaginate(message, embeds, undefined, true, args?.page ?? undefined);
}
}
diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts
index c33b39a..2c3e429 100644
--- a/src/commands/moderation/ban.ts
+++ b/src/commands/moderation/ban.ts
@@ -1,5 +1,5 @@
-import { AllowedMentions, BushCommand, BushGuildMember, BushMessage, BushSlashMessage } from '@lib';
-import { User } from 'discord.js';
+import { AllowedMentions, BushCommand, BushMessage, BushSlashMessage } from '@lib';
+import { Snowflake, User } from 'discord.js';
export default class BanCommand extends BushCommand {
public constructor() {
@@ -14,7 +14,7 @@ export default class BanCommand extends BushCommand {
args: [
{
id: 'user',
- type: 'user',
+ customType: util.arg.union('user', 'snowflake'),
prompt: {
start: 'What user would you like to ban?',
retry: '{error} Choose a valid user to ban.'
@@ -83,17 +83,19 @@ export default class BanCommand extends BushCommand {
public override async exec(
message: BushMessage | BushSlashMessage,
{
- user,
+ user: _user,
reason,
days,
force
- }: { user: User; reason?: { duration: number; contentWithoutTime: string }; days?: number; force: boolean }
+ }: { user: User | Snowflake; reason?: { duration: number; contentWithoutTime: string }; days?: number; force: boolean }
): Promise<unknown> {
if (!message.guild) return message.util.reply(`${util.emojis.error} This command cannot be used in dms.`);
- const member = message.guild!.members.cache.get(user.id) as BushGuildMember;
+ const member = message.guild!.members.cache.get((_user as User)?.id);
+ const user = member?.user ?? (await util.resolveNonCachedUser(_user));
+ if (!user) return message.util.reply(`${util.emojis.error} Invalid user.`);
const useForce = force && message.author.isOwner();
if (!message.member) throw new Error(`message.member is null`);
- const canModerateResponse = util.moderationPermissionCheck(message.member, member, 'ban', true, useForce);
+ const canModerateResponse = member ? util.moderationPermissionCheck(message.member, member, 'ban', true, useForce) : true;
if (canModerateResponse !== true) {
return message.util.reply(canModerateResponse);
@@ -112,31 +114,40 @@ export default class BanCommand extends BushCommand {
? await util.arg.cast('duration', client.commandHandler.resolver, message as BushMessage, reason)
: reason.duration;
}
- const parsedReason = reason?.contentWithoutTime ?? '';
+ const parsedReason = reason?.contentWithoutTime ?? null;
- const responseCode = await member.bushBan({
- reason: parsedReason,
- moderator: message.author,
- duration: time! ?? 0,
- deleteDays: days ?? 0
- });
+ const responseCode = member
+ ? await member.bushBan({
+ reason: parsedReason,
+ moderator: message.author,
+ duration: time! ?? 0,
+ deleteDays: days ?? 0
+ })
+ : await message.guild.ban({
+ user,
+ reason: parsedReason,
+ moderator: message.author,
+ duration: time! ?? 0,
+ deleteDays: days ?? 0
+ });
const responseMessage = () => {
switch (responseCode) {
case 'missing permissions':
- return `${util.emojis.error} Could not ban **${member.user.tag}** because I do not have permissions`;
+ return `${util.emojis.error} Could not ban **${user.tag}** because I do not have permissions`;
case 'error banning':
- return `${util.emojis.error} An error occurred while trying to ban **${member.user.tag}**.`;
+ return `${util.emojis.error} An error occurred while trying to ban **${user.tag}**.`;
case 'error creating ban entry':
- return `${util.emojis.error} While banning **${member.user.tag}**, there was an error creating a ban entry, please report this to my developers.`;
+ return `${util.emojis.error} While banning **${user.tag}**, there was an error creating a ban entry, please report this to my developers.`;
case 'error creating modlog entry':
- return `${util.emojis.error} While banning **${member.user.tag}**, there was an error creating a modlog entry, please report this to my developers.`;
+ return `${util.emojis.error} While banning **${user.tag}**, there was an error creating a modlog entry, please report this to my developers.`;
case 'failed to dm':
- return `${util.emojis.warn} Banned **${member.user.tag}** however I could not send them a dm.`;
+ return `${util.emojis.warn} Banned **${user.tag}** however I could not send them a dm.`;
case 'success':
- return `${util.emojis.success} Successfully banned **${member.user.tag}**.`;
+ return `${util.emojis.success} Successfully banned **${user.tag}**.`;
}
};
+ client.console.debug(responseCode);
return await message.util.reply({ content: responseMessage(), allowedMentions: AllowedMentions.none() });
}
}
diff --git a/src/commands/moderation/kick.ts b/src/commands/moderation/kick.ts
index 2315712..341d83c 100644
--- a/src/commands/moderation/kick.ts
+++ b/src/commands/moderation/kick.ts
@@ -61,7 +61,10 @@ export default class KickCommand extends BushCommand {
): Promise<unknown> {
const member = message.guild!.members.cache.get(user.id) as BushGuildMember;
- if (!member) return await message.util.reply(`${util.emojis.error} You cannot kick members that are not in the server.`);
+ if (!member)
+ return await message.util.reply(
+ `${util.emojis.error} The user you selected is not in the server or is not a valid user.`
+ );
if (!message.member) throw new Error(`message.member is null`);
const useForce = force && message.author.isOwner();
const canModerateResponse = util.moderationPermissionCheck(message.member, member, 'kick', true, useForce);
diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts
index ef0a56e..fd53ea7 100644
--- a/src/commands/moderation/modlog.ts
+++ b/src/commands/moderation/modlog.ts
@@ -35,6 +35,7 @@ export default class ModlogCommand extends BushCommand {
}
#generateModlogInfo(log: ModLog): string {
+ const trim = (str: string): string => (str.endsWith('\n') ? str.substring(0, str.length - 1).trim() : str.trim());
const modLog = [
`**Case ID**: ${log.id}`,
`**Type**: ${log.type.toLowerCase()}`,
@@ -42,8 +43,8 @@ export default class ModlogCommand extends BushCommand {
`**Moderator**: <@!${log.moderator}> (${log.moderator})`
];
if (log.duration) modLog.push(`**Duration**: ${util.humanizeDuration(log.duration)}`);
- modLog.push(`**Reason**: ${log.reason ?? 'No Reason Specified.'}`);
- if (log.evidence) modLog.push(`**Evidence:** ${log.evidence}`);
+ modLog.push(`**Reason**: ${trim(log.reason ?? 'No Reason Specified.')}`);
+ if (log.evidence) modLog.push(`**Evidence:** ${trim(log.evidence)}`);
return modLog.join(`\n`);
}
@@ -70,11 +71,11 @@ export default class ModlogCommand extends BushCommand {
(chunk) =>
new MessageEmbed({
title: `${foundUser.tag}'s Mod Logs`,
- description: chunk.join('\n**―――――――――――――――――――――――――――**\n'),
+ description: chunk.join('\n━━━━━━━━━━━━━━━\n'),
color: util.colors.default
})
);
- return await util.buttonPaginate(message, embedPages, '', true);
+ return await util.buttonPaginate(message, embedPages, undefined, true);
} else if (search) {
const entry = await ModLog.findByPk(search as string);
if (!entry) return message.util.send(`${util.emojis.error} That modlog does not exist.`);
diff --git a/src/commands/moderation/mute.ts b/src/commands/moderation/mute.ts
index 915302e..ea2ff41 100644
--- a/src/commands/moderation/mute.ts
+++ b/src/commands/moderation/mute.ts
@@ -61,7 +61,10 @@ export default class MuteCommand extends BushCommand {
{ user, reason, force }: { user: BushUser; reason?: { duration: number; contentWithoutTime: string }; force: boolean }
): Promise<unknown> {
const member = message.guild!.members.cache.get(user.id);
- if (!member) return await message.util.reply(`${util.emojis.error} You cannot kick members that are not in the server.`);
+ if (!member)
+ return await message.util.reply(
+ `${util.emojis.error} The user you selected is not in the server or is not a valid user.`
+ );
if (!message.member) throw new Error(`message.member is null`);
const useForce = force && message.author.isOwner();
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: {