Bugfixes + improvements

Stronger setting typings
Fixed bug with running settings current in dms
Stronger property checks in reply method
Locale loader bugfix that prevented certain files from loading properly
Make sure bot has permissions in modlog channel before trying to send message
This commit is contained in:
Erik 2023-12-11 15:07:08 +02:00
parent 71b025c514
commit a17420e87a
9 changed files with 157 additions and 130 deletions

View File

@ -297,10 +297,10 @@ export type SettingApiDefinitions = {
// //
} }
export type BaseSetting = object export type BaseSetting = object
export type SettingOptions = { export type SettingOptions<IsGuildSetting extends boolean> = {
name?: string, name?: string,
aliases?: string[], aliases?: string[],
resolve?: SettingTypeResolve, resolve?: If<IsGuildSetting, 'GUILD', 'USER'>,
description?: string, description?: string,
display?: string, display?: string,
emoji?: SettingEmojiOption, emoji?: SettingEmojiOption,

View File

@ -16,10 +16,11 @@ import { Emojis } from '../../constants/index.js';
import DiscordClient from '../DiscordClient.js'; import DiscordClient from '../DiscordClient.js';
import { FormatParams } from '../../../@types/Client.js'; import { FormatParams } from '../../../@types/Client.js';
type Languages = { type Language = {
[key: string]: {
[key: string]: string [key: string]: string
} }
type Languages = {
[key: string]: Language
} }
class LocaleLoader class LocaleLoader
@ -84,14 +85,19 @@ class LocaleLoader
const directory = path.join(root, language); const directory = path.join(root, language);
const files = Util.readdirRecursive(directory); const files = Util.readdirRecursive(directory);
const combined = {}; const combined: Language = {};
for (let file of files) for (let file of files)
{ {
file = fs.readFileSync(file, { file = fs.readFileSync(file, {
encoding: 'utf8' encoding: 'utf8'
}); });
const result = this._loadFile(file); const result = this._loadFile(file);
Object.assign(combined, result); for (const [ key, string ] of Object.entries(result))
{
if (combined[key])
throw new Error('Duplicate key ' + key);
combined[key] = string;
}
} }
this.#languages[language] = combined; this.#languages[language] = combined;
@ -102,8 +108,8 @@ class LocaleLoader
{ {
if (process.platform === 'win32') if (process.platform === 'win32')
{ {
file = file.split('\n').join(''); // file = file.split('\n').join('');
file = file.replace(/\r/gu, '\n'); file = file.replace(/\r/gu, '');
} }
const lines = file.split('\n'); const lines = file.split('\n');

View File

@ -75,7 +75,7 @@ class SettingsCommand extends SlashCommand
return invoker.reply({ embeds: [ embed ] }); return invoker.reply({ embeds: [ embed ] });
} }
async #currentSettings (invoker: InvokerWrapper<true, true>) async #currentSettings (invoker: InvokerWrapper<boolean, true>)
{ {
const { guild } = invoker; const { guild } = invoker;
const { settings } = this.client.registry; const { settings } = this.client.registry;
@ -83,7 +83,9 @@ class SettingsCommand extends SlashCommand
for (const setting of settings.values()) for (const setting of settings.values())
{ {
const settingFields = await setting.fields(guild); if (setting.resolve === 'GUILD' && !guild)
continue;
const settingFields = await setting.fields(guild!);
if (!settingFields.length) if (!settingFields.length)
continue; continue;
let _field = { let _field = {
@ -94,7 +96,7 @@ class SettingsCommand extends SlashCommand
{ {
if (field.name === ZeroWidthChar) if (field.name === ZeroWidthChar)
continue; continue;
const str = `**${guild.format(field.name)}**\n${field.value}`; const str = `**${guild!.format(field.name)}**\n${field.value}`;
if (_field.value.length + str.length >= EmbedLimits.fieldValue) if (_field.value.length + str.length >= EmbedLimits.fieldValue)
{ {
fields.push(_field); fields.push(_field);
@ -126,6 +128,9 @@ class SettingsCommand extends SlashCommand
} }
} }
if (!embeds.length)
return { index: 'COMMAND_SETTINGS_NONE' };
await invoker.reply({ embeds: [ embeds.shift()! ] }); await invoker.reply({ embeds: [ embeds.shift()! ] });
for (const emb of embeds) for (const emb of embeds)
{ {

View File

@ -37,10 +37,8 @@ class ChannelIgnore extends Inhibitor
} }
return super._fail({ error: true, silent: true }); return super._fail({ error: true, silent: true });
} }
return super._succeed(); return super._succeed();
} }
} }
export default ChannelIgnore; export default ChannelIgnore;

View File

@ -88,9 +88,10 @@ class IgnoreSetting extends Setting
}; };
} }
fields (guild: GuildWrapper) async fields (guild: GuildWrapper)
{ {
const setting = guild._settings[this.name] as IgnoreSettings; const settings = await guild.settings();
const setting = settings[this.name] as IgnoreSettings;
return [ return [
{ {
name: 'GENERAL_CHANNELS', name: 'GENERAL_CHANNELS',
@ -101,7 +102,6 @@ class IgnoreSetting extends Setting
value: setting.bypass.map((r) => `<@&${r}>`).join(', ') || '**N/A**' value: setting.bypass.map((r) => `<@&${r}>`).join(', ') || '**N/A**'
} }
]; ];
} }
} }

View File

@ -244,9 +244,13 @@ class InvokerWrapper<InGuild extends boolean = boolean, HasCommand extends boole
if (options.embed) if (options.embed)
options.embeds = [ options.embed ]; options.embeds = [ options.embed ];
if (options.embeds && !(options.embeds instanceof Array))
throw new Error('Invalid embeds type');
if (options.embeds) if (options.embeds)
options.embeds.forEach((embed) => options.embeds.forEach((embed) =>
{ {
if (!embed)
throw new Error('Embed cannot be undefined');
if (embed instanceof EmbedBuilder && !embed.data.color) if (embed instanceof EmbedBuilder && !embed.data.color)
embed.setColor(Constants.EmbedDefaultColor); embed.setColor(Constants.EmbedDefaultColor);
else if (!(embed instanceof EmbedBuilder) && !embed.color) else if (!(embed instanceof EmbedBuilder) && !embed.color)

View File

@ -189,11 +189,15 @@ class Infraction
{ {
if (moderation.infractions.includes(this.#type!)) if (moderation.infractions.includes(this.#type!))
{ {
this.#moderationLog = await this.#client.resolver.resolveChannel(moderation.channel, true, this.#guild); this.#moderationLog = await this.#guild.resolveChannel<TextChannel>(moderation.channel, true);
if (this.#moderationLog) const perms = this.#moderationLog?.permissionsFor(this.client.user!);
const missing = perms?.missing([ 'SendMessages', 'EmbedLinks' ]);
if (this.#moderationLog && !missing?.length)
{ {
this.#modLogId = this.#moderationLog.id; this.#modLogId = this.#moderationLog.id;
this.#dmLogMessage = await this.#moderationLog.send({ embeds: [ await this.#embed() ] }).catch(null); this.#dmLogMessage = await this.#moderationLog
.send({ embeds: [ await this.#embed() ] })
.catch((err) => this.logger.error(err)) ?? null;
this.#modLogMessageId = this.#dmLogMessage?.id || null; this.#modLogMessageId = this.#dmLogMessage?.id || null;
} }
} }
@ -203,9 +207,7 @@ class Infraction
} }
} }
if (dminfraction.enabled && !this.#silent) if (dminfraction.enabled && !this.#silent && this.#targetType === 'USER' && dminfraction.infractions.includes(this.#type!))
{
if (this.#targetType === 'USER' && dminfraction.infractions.includes(this.#type!))
{ {
let message = dminfraction.messages[this.#type!] || dminfraction.messages.default; let message = dminfraction.messages[this.#type!] || dminfraction.messages.default;
if (!message) if (!message)
@ -225,7 +227,6 @@ class Infraction
this.#dmLogMessageId = logMessage?.id ?? null; this.#dmLogMessageId = logMessage?.id ?? null;
} }
} }
}
if (this.#duration) if (this.#duration)
await this.#client.moderation.handleCallbacks([ this.json ]); await this.#client.moderation.handleCallbacks([ this.json ]);
@ -606,7 +607,17 @@ class Infraction
protected async _verify () protected async _verify ()
{ {
const { protection } = await this.#guild.settings(); const {
protection,
// moderation
} = await this.#guild.settings();
// const modlog = moderation.channel ? await this.guild.resolveChannel<TextChannel>(moderation.channel).catch(() => null) : null;
// const perms = modlog?.permissionsFor(this.client.user!);
// if (modlog && perms)
// {
// if (perms.missing([ 'SendMessages', 'EmbedLinks' ]).length)
// return this._fail('INFRACTION_MISSING_MODLOG_PERMS');
// }
if (this.#executor?.id === this.#guild.ownerId) if (this.#executor?.id === this.#guild.ownerId)
return this._succeed(); return this._succeed();
if ( if (

View File

@ -1,5 +1,5 @@
import Component from './Component.js'; import Component from './Component.js';
import { EmbedBuilder, PermissionsString, TextChannel, APIEmbed, APIEmbedField, User } from 'discord.js'; import { EmbedBuilder, PermissionsString, TextChannel, APIEmbed, APIEmbedField, User, If } from 'discord.js';
import { GuildWrapper, InvokerWrapper, MemberWrapper, UserWrapper } from '../components/wrappers/index.js'; import { GuildWrapper, InvokerWrapper, MemberWrapper, UserWrapper } from '../components/wrappers/index.js';
import DiscordClient from '../DiscordClient.js'; import DiscordClient from '../DiscordClient.js';
@ -58,7 +58,7 @@ type HelperResult = {
* @class Setting * @class Setting
* @extends {Component} * @extends {Component}
*/ */
abstract class Setting extends Component abstract class Setting<IsGuildSetting extends boolean = true> extends Component
{ {
#name: string; #name: string;
#resolve: SettingTypeResolve; #resolve: SettingTypeResolve;
@ -86,7 +86,7 @@ abstract class Setting extends Component
* @param {Object} [options={}] * @param {Object} [options={}]
* @memberof Setting * @memberof Setting
*/ */
constructor (client: DiscordClient, options: SettingOptions) constructor (client: DiscordClient, options: SettingOptions<IsGuildSetting>)
{ {
if (!options) if (!options)
throw new Error('No options provided to setting'); throw new Error('No options provided to setting');
@ -266,7 +266,7 @@ abstract class Setting extends Component
* @return {Array<void>} * @return {Array<void>}
* @memberof Setting * @memberof Setting
*/ */
fields (_guild: GuildWrapper): Promise<APIEmbedField[]> | APIEmbedField[] fields (_guild: If<IsGuildSetting, GuildWrapper, GuildWrapper | undefined>): Promise<APIEmbedField[]> | APIEmbedField[]
{ {
return Promise.resolve([]); return Promise.resolve([]);
} }

View File

@ -54,6 +54,9 @@ No permissions granted
[COMMAND_SETTINGS_HELP] [COMMAND_SETTINGS_HELP]
Configure bot settings for the server. Configure bot settings for the server.
[COMMAND_SETTINGS_NONE]
No settings found.
[COMMAND_MODERATION_HELP] [COMMAND_MODERATION_HELP]
Configure moderation related settings. Configure moderation related settings.