diff --git a/src/structure/components/observers/AuditLog.js b/src/structure/components/observers/AuditLog.js index 84e4465..725d57f 100644 --- a/src/structure/components/observers/AuditLog.js +++ b/src/structure/components/observers/AuditLog.js @@ -22,10 +22,10 @@ class AuditLogObserver extends Observer { } - async guildBanAdd({ guild, user, guildWrapper: wrapper }) { + async guildBanAdd({ user, guildWrapper: wrapper }) { const settings = await wrapper.settings(); if (!settings.moderation.channel || !settings.moderation.infractions.includes('BAN')) return undefined; //This is checked by the infraction handling, but it may save resources if checked earlier. - const audit = await this._fetchFirstEntry(guild, user, 'MemberBanAdd'); + const audit = await this._fetchFirstEntry(wrapper, user, 'MemberBanAdd'); if (!audit) return undefined; new Infraction(this.client, { type: 'BAN', @@ -37,10 +37,10 @@ class AuditLogObserver extends Observer { }).handle(); } - async guildBanRemove({ guild, user, guildWrapper: wrapper }) { + async guildBanRemove({ user, guildWrapper: wrapper }) { const settings = await wrapper.settings(); if (!settings.moderation.channel || !settings.moderation.infractions.includes('UNBAN')) return undefined; //This is checked by the infraction handling, but it may save resources if checked earlier. - const audit = await this._fetchFirstEntry(guild, user, 'MemberBanRemove'); + const audit = await this._fetchFirstEntry(wrapper, user, 'MemberBanRemove'); if (!audit) return undefined; new Infraction(this.client, { type: 'UNBAN', @@ -56,7 +56,7 @@ class AuditLogObserver extends Observer { const { guildWrapper: wrapper } = member; const settings = await wrapper.settings(); if (!settings.moderation.channel || !settings.moderation.infractions.includes('KICK')) return undefined; //This is checked by the infraction handling, but it may save resources if checked earlier. - const audit = await this._fetchFirstEntry(member.guild, member.user, 'MemberKick'); + const audit = await this._fetchFirstEntry(wrapper, member.user, 'MemberKick'); if (!audit) return undefined; new Infraction(this.client, { type: 'KICK', @@ -123,7 +123,7 @@ class AuditLogObserver extends Observer { const mutedRole = settings.mute.role; if (!mutedRole) return undefined; - const audit = await this._fetchFirstEntry(newMember.guild, newMember.user, 'MemberRoleUpdate'); + const audit = await this._fetchFirstEntry(wrapper, newMember.user, 'MemberRoleUpdate'); if (!audit) return undefined; let type = null; @@ -148,7 +148,8 @@ class AuditLogObserver extends Observer { } async _fetchFirstEntry(guild, user, type, subtype = null) { - if (!guild.members.me.permissions.has('ViewAuditLog')) return null; + const me = await guild.resolveMember(this.client.user); + if (!me.permissions.has('ViewAuditLog')) return null; type = AuditLogEvent[type]; const audit = await guild.fetchAuditLogs({ limit: 1, type }); if (audit.entries.size === 0) return null; diff --git a/src/structure/components/observers/Automoderation.js b/src/structure/components/observers/Automoderation.js index 00a7bef..47d13b6 100644 --- a/src/structure/components/observers/Automoderation.js +++ b/src/structure/components/observers/Automoderation.js @@ -133,7 +133,7 @@ module.exports = class AutoModeration extends Observer { if (!enabled || roles.some((r) => bypass.includes(r)) || ignore.includes(channel.id)) return; - const missing = channel.permissionsFor(guild.members.me).missing('ManageMessages'); + const missing = channel.permissionsFor(this.client.user).missing('ManageMessages'); if (missing.length) { this.client.emit('filterMissingPermissions', { channel, guild: wrapper, filter: 'word', permissions: missing }); return; @@ -432,7 +432,7 @@ module.exports = class AutoModeration extends Observer { if (roles.some((r) => bypass.includes(r)) || ignore.includes(channel.id)) return; - const missing = channel.permissionsFor(guild.members.me).missing('ManageMessages'); + const missing = channel.permissionsFor(this.client.user).missing('ManageMessages'); if (missing.length) { this.client.emit('filterMissingPermissions', { channel, guild: wrapper, filter: 'link', permissions: missing }); return; @@ -454,9 +454,10 @@ module.exports = class AutoModeration extends Observer { let log = `${guild.name} Link filter debug:`; for (const match of matches) { - const { domain } = match.match(this.regex.linkReg).groups; + let { domain } = match.match(this.regex.linkReg).groups; + domain = domain.toLowerCase(); // Invites are filtered separately - if (domain.toLowerCase() === 'discord.gg') continue; + if (domain === 'discord.gg') continue; log += `\nMatched link ${match}: `; const predicate = (dom) => { @@ -550,13 +551,13 @@ module.exports = class AutoModeration extends Observer { const member = message.member || await guild.members.fetch(author.id).catch(() => null); const settings = await wrapper.settings(); const { invitefilter: setting } = settings; - const { bypass, ignore, actions, silent, enabled, whitelist } = setting; + const { bypass, ignore, actions, silent, enabled, whitelist = [] } = setting; if (!enabled) return; const roles = member?.roles.cache.map((r) => r.id) || []; if (roles.some((r) => bypass.includes(r)) || ignore.includes(channel.id)) return; - const missing = channel.permissionsFor(guild.members.me).missing('ManageMessages'); + const missing = channel.permissionsFor(this.client.user).missing('ManageMessages'); if (missing.length) { this.client.emit('filterMissingPermissions', { channel, guild: wrapper, filter: 'invite', permissions: missing }); return; @@ -613,7 +614,7 @@ module.exports = class AutoModeration extends Observer { if (!enabled || roles.some((r) => bypass.includes(r)) || ignore.includes(channel.id)) return; - const missing = channel.permissionsFor(guild.members.me).missing('ManageMessages'); + const missing = channel.permissionsFor(this.client.user).missing('ManageMessages'); if (missing.length) { this.client.emit('filterMissingPermissions', { channel, guild: wrapper, filter: 'mention', permissions: missing }); return; diff --git a/src/structure/components/observers/GuildLogging.js b/src/structure/components/observers/GuildLogging.js index 77767cd..d76836f 100644 --- a/src/structure/components/observers/GuildLogging.js +++ b/src/structure/components/observers/GuildLogging.js @@ -1,5 +1,5 @@ /* eslint-disable no-labels */ -const { WebhookClient, AttachmentBuilder } = require('discord.js'); +const { WebhookClient, AttachmentBuilder, AuditLogEvent } = require('discord.js'); const { stripIndents } = require('common-tags'); const moment = require('moment'); const { inspect } = require('util'); @@ -103,22 +103,23 @@ class GuildLogger extends Observer { const hook = await guild.getWebhook('messages'); if (!hook) { - this.logger.debug(`Missing messageLog hook in ${guild.name} (${guild.id})`); + // this.logger.debug(`Missing messageLog hook in ${guild.name} (${guild.id})`); return this.client.emit('logError', { guild, logger: 'threadLogger', reason: 'MSGLOG_NO_HOOK' }); } let actor = null; - const auditLogPerm = guild.members.me.permissions.has('ViewAuditLog'); + const me = await this.client.resolver.resolveMember(this.client.user, null, guild); + const auditLogPerm = me.permissions.has('ViewAuditLog'); if (type === 'CREATE') actor = owner; else if (type === 'DELETE' && auditLogPerm) { - const auditLogs = await guild.fetchAuditLogs({ type: 'THREAD_DELETE', limit: 1 }); + const auditLogs = await guild.fetchAuditLogs({ type: AuditLogEvent.ThreadDelete, limit: 1 }); const log = auditLogs.entries.first(); if (log) { if (thread.id !== log.target.id) return; actor = log.executor; } } else if (['ARCHIVE', 'UNARCHIVE'].includes(type) && auditLogPerm) { - const auditLogs = await guild.fetchAuditLogs({ type: 'THREAD_UPDATE', limit: 1 }); + const auditLogs = await guild.fetchAuditLogs({ type: AuditLogEvent.ThreadUpdate, limit: 1 }); const log = auditLogs.entries.first(); if (log) { if (thread.id !== log.target.id) return; @@ -178,7 +179,7 @@ class GuildLogger extends Observer { const hook = await wrapper.getWebhook('messages'); if (!hook) { - this.logger.debug(`Missing messageLog hook in ${wrapper.name} (${wrapper.id})`); + // this.logger.debug(`Missing messageLog hook in ${wrapper.name} (${wrapper.id})`); return this.client.emit('logError', { guild: wrapper, logger: 'messageLogger', reason: 'MSGLOG_NO_HOOK' }); } @@ -340,13 +341,13 @@ class GuildLogger extends Observer { const { ignore, bypass } = chatlogs; if (ignore.includes(channel.id)) return; - const missing = logChannel.permissionsFor(guild.members.me).missing(['ViewChannel', 'EmbedLinks', 'SendMessages', 'ManageWebhooks']); + const missing = logChannel.permissionsFor(this.client.user).missing(['ViewChannel', 'EmbedLinks', 'SendMessages', 'ManageWebhooks']); if (missing.length) return this.client.emit('logError', { guild: wrapper, logger: 'messageLogger', reason: 'MSGLOG_NO_PERMS', params: { missing: missing.join(', ') } }); const hook = await wrapper.getWebhook('messages'); if (!hook) { - this.logger.debug(`Missing messageLog hook in ${guild.name} (${guild.id})`); + // this.logger.debug(`Missing messageLog hook in ${guild.name} (${guild.id})`); return this.client.emit('logError', { guild: wrapper, logger: 'messageLogger', reason: 'MSGLOG_NO_HOOK' }); } @@ -364,6 +365,7 @@ class GuildLogger extends Observer { content = Util.escapeMarkdown(content); if (author.bot) continue; + // TODO: Apparently the cache for this doesn't work properly and the bot hits rate limits occasionally, make own cache at some point if (!member || member.partial) member = await guild.members.fetch(message.author.id).catch(() => { return false; }); @@ -533,7 +535,7 @@ class GuildLogger extends Observer { const hook = await wrapper.getWebhook('messages'); if (!hook) { - this.logger.debug(`Missing messageLog hook in ${guild.name} (${guild.id})`); + // this.logger.debug(`Missing messageLog hook in ${guild.name} (${guild.id})`); return this.client.emit('logError', { guild: wrapper, logger: 'messageLogger', reason: 'MSGLOG_NO_HOOK' }); } @@ -710,7 +712,7 @@ class GuildLogger extends Observer { async memberLeave(member) { - const { guild, guildWrapper: wrapper } = member; + const { guildWrapper: wrapper } = member; const settings = await wrapper.settings(); const setting = settings.members; if (!setting.channel || !setting.enabled) return; @@ -732,7 +734,7 @@ class GuildLogger extends Observer { if (oldMember.nickname === newMember.nickname) return; - const { guild, user, guildWrapper: wrapper } = oldMember; + const { user, guildWrapper: wrapper } = oldMember; const settings = await wrapper.settings(); const setting = settings.nicknames; if (!setting.channel || !setting.enabled) return; diff --git a/src/structure/components/observers/Metrics.js b/src/structure/components/observers/Metrics.js new file mode 100644 index 0000000..dd4e321 --- /dev/null +++ b/src/structure/components/observers/Metrics.js @@ -0,0 +1,31 @@ +const { Observer } = require("../../interfaces"); + +class Metrics extends Observer { + + constructor(client) { + super(client, { + name: 'metrics', + priority: 10, + disabled: false + }); + + this.hooks = [ + ['apiRequest', this.request.bind(this)], + ['apiResponse', this.response.bind(this)] + ]; + + this.cache = {}; + + } + + async request(request) { + // console.log(request); + } + + async response(request, response) { + // console.log(response, await response.json(), response.status); + } + +} + +module.exports = Metrics; \ No newline at end of file diff --git a/src/structure/components/observers/UtilityHook.js b/src/structure/components/observers/UtilityHook.js index 78dc52c..d562098 100644 --- a/src/structure/components/observers/UtilityHook.js +++ b/src/structure/components/observers/UtilityHook.js @@ -51,7 +51,8 @@ class UtilityHook extends Observer { const { guildWrapper: guild } = member; const settings = await guild.settings(); const setting = settings.mute; - if (!guild.members.me.permissions.has('ManageRoles')) return; + const me = await guild.resolveMember(this.client.user); + if (!me.permissions.has('ManageRoles')) return; const infraction = await this.client.storageManager.mongodb.infractions.findOne({ duration: { $gt: 0 }, @@ -79,7 +80,7 @@ class UtilityHook extends Observer { if (managed) { await member.roles.add(setting.role, 'automute upon rejoin, type 1'); await member.roles.remove(remove, 'removing excess roles for type 1 mute'); - } else await member.roles.set(setting.role, 'automute upon join, type 1'); + } else await member.roles.set([role], 'automute upon join, type 1'); } else if (infraction.data.muteType === 2) { @@ -100,7 +101,8 @@ class UtilityHook extends Observer { const settings = await guild.settings(); const setting = settings.stickyrole; if (!setting.roles.length || guild.premium < 1) return; - if (!guild.members.me.permissions.has('ManageRoles')) return; + const me = await guild.resolveMember(this.client.user); + if (!me.permissions.has('ManageRoles')) return; const data = await this.client.storageManager.mongodb.role_cache.findOne({ guild: guild.id, member: member.id }); if (!data) return; @@ -115,7 +117,8 @@ class UtilityHook extends Observer { const settings = await guild.settings(); const setting = settings.autorole; if (!setting.enabled) return; - if (!guild.members.me.permissions.has('ManageRoles')) return; + const me = await guild.resolveMember(this.client.user); + if (!me.permissions.has('ManageRoles')) return; const _roles = await guild.resolveRoles(setting.roles); const roles = _roles.map((r) => r.id); @@ -153,7 +156,8 @@ class UtilityHook extends Observer { const { guild } = invite; if (!guild) return; - if (!guild.members.me.permissions.has('ManageGuild')) return; + const me = await this.client.resolver.resolveMember(this.client.user, null, guild); + if (!me.permissions.has('ManageGuild')) return; if (!guild.invites) guild.invites = await guild.fetchInvites(); guild.invites.set(invite.code, invite); @@ -163,7 +167,8 @@ class UtilityHook extends Observer { const { guild } = invite; if (!guild) return; - if (!guild.members.me.permissions.has('ManageGuild')) return; + const me = await this.client.resolver.resolveMember(this.client.user, null, guild); + if (!me.permissions.has('ManageGuild')) return; if (!guild.invites) guild.invites = await guild.fetchInvites(); guild.invites.delete(invite.code); @@ -203,7 +208,8 @@ class UtilityHook extends Observer { !selfrole.channel || selfrole.channel !== channel.id || !selfrole.roles.length) return; - const missing = guild.members.me.permissions.missing(['ManageRoles']); + const me = await guild.resolveMember(this.client.user); + const missing = me.permissions.missing(['ManageRoles']); if (missing.length) return this.client.emit('utilityError', { guild, utility: 'selfrole', reason: 'UTILITY_SELFROLE_PERMS', params: { missing: missing.join(', ') } }); diff --git a/src/structure/infractions/Addrole.js b/src/structure/infractions/Addrole.js index e0ab45d..04a8c2e 100644 --- a/src/structure/infractions/Addrole.js +++ b/src/structure/infractions/Addrole.js @@ -53,7 +53,8 @@ class AddroleInfraction extends Infraction { const { grantable } = await this.guild.settings(); let filtered = []; - const { highest: clientHighest } = this.guild.members.me.roles; + const me = await this.guild.resolveMember(this.client.user); + const { highest: clientHighest } = me.roles; filtered = this.data.roles.filter((r) => r.comparePositionTo(clientHighest) < 0); if (filtered.length === 0) { return super._fail('C_ADDROLE_ROLEHIERARCHYBOT'); diff --git a/src/structure/infractions/Lockdown.js b/src/structure/infractions/Lockdown.js index fb7b193..79140c1 100644 --- a/src/structure/infractions/Lockdown.js +++ b/src/structure/infractions/Lockdown.js @@ -140,7 +140,7 @@ class LockdownInfraction extends Infraction { async verify() { - const perms = this.target.permissionsFor(this.guild.members.me); + const perms = this.target.permissionsFor(this.client.user); if(perms.missing('ManageRoles').length) return this._fail('INFRACTION_LOCKDOWN_MISSING_PERMS'); return super._verify(); diff --git a/src/structure/infractions/Mute.js b/src/structure/infractions/Mute.js index 68a0d43..780aa72 100644 --- a/src/structure/infractions/Mute.js +++ b/src/structure/infractions/Mute.js @@ -48,6 +48,8 @@ class MuteInfraction extends Infraction { role = await this.client.resolver.resolveRole(setting.role, true, this.guild); } + const me = await this.guild.resolveMember(this.client.user); + let removed = []; switch (setting.type) { case 0: @@ -60,12 +62,12 @@ class MuteInfraction extends Infraction { break; case 1: removed = this.member.roles.cache.filter((r) => !r.managed && - r.comparePositionTo(this.guild.members.me.roles.highest) < 0 && + r.comparePositionTo(me.roles.highest) < 0 && r.id !== this.guild.id); try { await this.member.roles.set([ ...this.member.roles.cache.filter((r) => r.managed || - r.comparePositionTo(this.guild.members.me.roles.highest) >= 0 || + r.comparePositionTo(me.roles.highest) >= 0 || r.id === this.guild.id).values(), role ], this._reason); @@ -76,11 +78,11 @@ class MuteInfraction extends Infraction { break; case 2: removed = this.member.roles.cache.filter((r) => !r.managed && - r.comparePositionTo(this.guild.members.me.roles.highest) < 0 && + r.comparePositionTo(me.roles.highest) < 0 && r.id !== this.guild.id); try { await this.member.roles.set(this.member.roles.cache.filter((r) => r.managed || - r.comparePositionTo(this.guild.members.me.roles.highest) >= 0 || + r.comparePositionTo(me.roles.highest) >= 0 || r.id === this.guild.id), this._reason); } catch (error) { this.client.logger.error(`Mute infraction failed to calculate removeable roles, might want to check this out.\n${error.stack || error}`); @@ -96,6 +98,8 @@ class MuteInfraction extends Infraction { } } + if (this.member.voice.channel) await this.member.voice.disconnect(this._reason); + this.data = { removedRoles: removed.map((r) => r.id), muteType: setting.type, @@ -107,7 +111,7 @@ class MuteInfraction extends Infraction { if (callback) { this.data.removedRoles = [...new Set([...this.data.removedRoles, ...callback.infraction.data.removedRoles])]; - this.client.moderationManager.removeCallback(callback.infraction); + this.client.moderationManager.removeCallback(callback.infraction, true); } // if(callbacks.size > 0) callbacks.map((c) => this.client.moderationManager._removeExpiration(c)); @@ -134,10 +138,12 @@ class MuteInfraction extends Infraction { return this._fail('COMMAND_MUTE_INVALIDMUTEROLE', true); } } + const me = await this.guild.resolveMember(this.client.user); if (settings.mute.type === 3) { - if (this.guild.members.me.permissions.missing('ModerateMembers').length) return this._fail('COMMAND_MUTE_MISSING_MODERATE_PERM', true); + if (me.permissions.missing('ModerateMembers').length) return this._fail('COMMAND_MUTE_MISSING_MODERATE_PERM', true); + if (me.roles.highest.position <= this.member.roles.highest.position) return this._fail('COMMAND_MUTE_HIERARCHY_ERROR'); // if (!this.duration && !settings.mute.default) - } else if (this.guild.members.me.permissions.missing('ManageRoles').length) return this._fail('COMMAND_MUTE_MISSING_MANAGEROLE_PERM'); + } else if (me.permissions.missing('ManageRoles').length) return this._fail('COMMAND_MUTE_MISSING_MANAGEROLE_PERM'); return super._verify(); diff --git a/src/structure/infractions/Nickname.js b/src/structure/infractions/Nickname.js index 5721dbe..b0618db 100644 --- a/src/structure/infractions/Nickname.js +++ b/src/structure/infractions/Nickname.js @@ -82,7 +82,8 @@ class NicknameInfraction extends Infraction { async verify() { const { highest } = this.member.roles; - if (highest.comparePositionTo(this.guild.members.me.roles.highest) > 0 || !this.guild.members.me.permissions.has('ManageNicknames')) { + const me = await this.guild.resolveMember(this.client.user); + if (highest.comparePositionTo(me.roles.highest) > 0 || !me.permissions.has('ManageNicknames')) { return this._fail('C_NICKNAME_MISSINGPERMISSIONS'); } diff --git a/src/structure/infractions/Removerole.js b/src/structure/infractions/Removerole.js index 58ff10b..b3b041b 100644 --- a/src/structure/infractions/Removerole.js +++ b/src/structure/infractions/Removerole.js @@ -53,7 +53,8 @@ class RemoveroleInfraction extends Infraction { const { grantable } = await this.guild.settings(); let filtered = []; - const { highest: clientHighest } = this.guild.members.me.roles; + const me = await this.guild.resolveMember(this.client.user); + const { highest: clientHighest } = me.roles; filtered = this.data.roles.filter((r) => r.comparePositionTo(clientHighest) < 0); if (filtered.length === 0) { return super._fail('C_REMOVEROLE_ROLEHIERARCHYBOT'); diff --git a/src/structure/infractions/Unban.js b/src/structure/infractions/Unban.js index 4e25ba2..6d757ef 100644 --- a/src/structure/infractions/Unban.js +++ b/src/structure/infractions/Unban.js @@ -39,7 +39,7 @@ class UnbanInfraction extends Infraction { const callbacks = this.client.moderationManager.callbacks.filter((c) => c.infraction.type === 'BAN' && c.infraction.target === this.target.id); - if (callbacks.size > 0) callbacks.map((c) => this.client.moderationManager.removeCallback(c.infraction)); + if (callbacks.size > 0) callbacks.map((c) => this.client.moderationManager.removeCallback(c.infraction, true)); await this.handle(); return this._succeed(); diff --git a/src/structure/infractions/Unlockdown.js b/src/structure/infractions/Unlockdown.js index 9349510..ad951ad 100644 --- a/src/structure/infractions/Unlockdown.js +++ b/src/structure/infractions/Unlockdown.js @@ -95,7 +95,7 @@ class UnlockdownInfraction extends Infraction { async verify() { - const perms = this.target.permissionsFor(this.guild.members.me); + const perms = this.target.permissionsFor(this.client.user); if (perms.missing('ManageRoles').length) return this._fail('INFRACTION_LOCKDOWN_MISSING_PERMS'); return super._verify(); diff --git a/src/utilities/FilterUtil.js b/src/utilities/FilterUtil.js index 39e0ae4..f2c0199 100644 --- a/src/utilities/FilterUtil.js +++ b/src/utilities/FilterUtil.js @@ -321,7 +321,9 @@ module.exports = class FilterUtility { matched: true, _matcher: _word, matcher: `fuzzy [\`${_word}\`, \`${sim}\`, \`${threshold}\`]`, - type: 'fuzzy' + type: 'fuzzy', + _threshold: threshold, + _sim: sim }; } diff --git a/src/utilities/SettingsMigrator.js b/src/utilities/SettingsMigrator.js index 12676ef..9e93a3d 100644 --- a/src/utilities/SettingsMigrator.js +++ b/src/utilities/SettingsMigrator.js @@ -206,7 +206,7 @@ class SettingsMigrator { stickyrole: result.stickyrole ? { ...result.stickyrole, enabled: Boolean(result.stickyrole.roles.length) } : undefined, welcomer: result.welcomer, commands: { disabled: result.disabledCommands, custom: {} }, - prefix: result.prefix + textcommands: { prefix: result.prefix, enabled: false } }; return settings; } @@ -287,14 +287,15 @@ class SettingsMigrator { }; if (linkfilter) settings.linkfilter = { - enabled: result.linkfilter?.enabled || false, - silent: result.linkfilter?.silent || false, - ignore: result.linkfilter?.channels || [], - bypass: result.linkfilter?.roles || [], + enabled: linkfilter.enabled || false, + silent: linkfilter.silent || false, + ignore: linkfilter.channels || [], + bypass: linkfilter.roles || [], actions: [], - whitelist: result.linkfilter?.whitelist === true ? result.linkfilter.filter : [], - blacklist: result.linkfilter?.whitelist === false ? result.linkfilter.filter : [], - greylist: [] + whitelist: linkfilter.whitelist === true ? linkfilter.filter : [], + blacklist: linkfilter.whitelist === false ? linkfilter.filter : [], + greylist: [], + whitelistMode: linkfilter.whitelist || false }; if (ignore) settings.ignore = { @@ -334,7 +335,7 @@ class SettingsMigrator { }; if (autorole) settings.autorole = autorole; - if (prefix) settings.prefix = prefix; + if (prefix) settings.textcommands = { prefix, enabled: false }; if (welcomer) { settings.welcomer.enabled = welcomer.enabled; settings.welcomer.message = welcomer.message || null; @@ -398,7 +399,8 @@ class SettingsMigrator { silent: false, bypass: result.invitefilter.roles, enabled: result.invitefilter.enabled, - actions: [] + actions: [], + whitelist: [] }; const channels = Object.entries(invitefilter.channels || {}); for (const [id, value] of channels) {