diff --git a/src/structure/components/commands/administration/Import.js b/src/structure/components/commands/administration/Import.js index 6818d1a..8981233 100644 --- a/src/structure/components/commands/administration/Import.js +++ b/src/structure/components/commands/administration/Import.js @@ -2,7 +2,7 @@ const SettingsMigrator = require("../../../../utilities/SettingsMigrator"); const { SlashCommand } = require("../../../interfaces"); const dbs = { - '2': 'galacticbot', + '2': process.env.NODE_ENV === 'development' ? 'navybot' : 'galacticbot', '3': 'newgbot' }; @@ -23,7 +23,7 @@ class ImportCommand extends SlashCommand { name: 'version', description: 'Which version do you want to import from', choices: [ - // { name: 'v2', value: '2' }, + { name: 'v2', value: '2' }, { name: 'v3', value: '3' } ] }] @@ -56,8 +56,12 @@ class ImportCommand extends SlashCommand { try { imported = await migrator.migrate(); } catch (err) { + this.client.logger.error(err.stack); return { index: 'COMMAND_IMPORT_ERROR', params: { message: err.message }, emoji: 'failure' }; } + await migrator.end(); + + // return console.log(imported); const { webhook, permissions } = imported; if (webhook) { diff --git a/src/utilities/SettingsMigrator.js b/src/utilities/SettingsMigrator.js index 6d8e745..82f85c8 100644 --- a/src/utilities/SettingsMigrator.js +++ b/src/utilities/SettingsMigrator.js @@ -1,13 +1,33 @@ +const { Infractions } = require('../constants/Constants.js'); const Logger = require('./Logger.js'); const MongoWrapper = require('./SimpleMongoWrapper.js'); +const DmInfractions = [ + 'NOTE', + 'WARN', + 'MUTE', + 'UNMUTE', + 'KICK', + 'SOFTBAN', + 'BAN', + 'UNBAN', + 'VCMUTE', + 'VCUNMUTE', + 'VCKICK', + 'VCBAN', + 'VCUNBAN', + 'REMOVEROLE', + 'ADDROLE', + 'NICKNAME' +]; // Fetches old config from the database and translates it to the new format class SettingsMigrator { - constructor(guild, dbConfig) { + constructor(client, guild, dbConfig) { this._config = dbConfig; + this.client = client; this.mongo = new MongoWrapper(dbConfig); this.guild = guild.id || guild; this.logger = new Logger(this); @@ -48,6 +68,8 @@ class SettingsMigrator { const { _version } = settings; if (!_version) return Promise.reject(new Error('Unable to determine configuration version')); + const translated = this[_version](settings); + let webhook = null, permissions = null; if (version === '3') { @@ -56,34 +78,101 @@ class SettingsMigrator { delete webhook._id; delete permissions._id; } else if (version === '2') { - ({ webhook } = settings.chatlogs); - permissions = this.permissions(settings.roles); + ({ webhook = null } = settings.chatlogs || {}); + const resolved = this.permissions(settings.roles); + ({ permissions } = resolved); + const { overrideRoles } = resolved; + + if (overrideRoles.length) { + if (!translated.ignore) { + translated.ignore = { roleBypass: overrideRoles }; + } else if (!translated.ignore.roleBypass) translated.ignore.roleBypass = overrideRoles; + else translated.ignore.roleBypass.push(...overrideRoles); + } + permissions.guildId = this.guild; } + translated._imported = true; + translated.guildId = this.guild; + this.logger.info(`Settings migration for ${this.guild}`); - return { settings: this.cleanUp(this[_version](settings)), webhook, permissions }; + return { settings: this.cleanUp(translated), webhook, permissions }; } - permissions(roles) { + // Translate v2 perms to v3 perms + permissions(roles = {}) { const keys = Object.keys(roles); - const perms = {}; + const permissions = {}; + const overrideRoles = []; + if (!roles) return { permissions, overrideRoles }; + for (const roleId of keys) { - perms[roleId] = { - global: roles[roleId].perms + const channels = {}; + const global = this.resolvePermissions(roles[roleId].perms); + if (global.override && !overrideRoles.includes(roleId)) overrideRoles.push(roleId); + + const channelIds = Object.keys(roles[roleId].ch); + for (const channelId of channelIds) { + const channelPerms = this.resolvePermissions(roles[roleId].ch[channelId]); + if (channelPerms.override && !overrideRoles.includes(roleId)) overrideRoles.push(roleId); + channels[channelId] = channelPerms.resolved; + } + + permissions[roleId] = { + global: global.resolved, + channels }; } - return perms; + return { permissions, overrideRoles }; + } + + // Attempts to resolve the semi-broken permissions from v2 to the v3 standard + resolvePermissions(resolveables) { + + const { resolver } = this.client; + + const resolved = []; + let override = false; + for (const resolveable of resolveables) { + if (resolveable === 'override') { + override = true; + continue; + } + const keys = Object.keys(this.permissiongResolveables); + let [result] = resolver.components(resolveable, 'any'); + + + if (!result) for (const key of keys) { + if (this.permissiongResolveables[key].includes(resolveable)) { + [result] = resolver.components(key, 'any'); + if (result) continue; + } + } + + if (result) { + if (result._type === 'module') resolved.push(...result.components.map((c) => c.resolveable)); + else resolved.push(result.resolveable); + } + } + + return { resolved, override }; + + } + + get permissiongResolveables() { + return { + 'module:moderation': ['mod', 'moderation', 'moderator', 'category:moderation'], + 'module:administration': ['admin', 'administrator', 'administration', 'category:administration'] + }; } '3'(result) { // console.log(result); // most of these should be mostly 1:1 apart from the names that changed const settings = { - guildId: result.guildId, - _imported: true, modpoints: result.moderationPoints, moderation: result.moderationLog, mute: result.mute, @@ -108,17 +197,188 @@ class SettingsMigrator { autorole: result.autorole, stickyrole: result.stickyrole ? { ...result.stickyrole, enabled: Boolean(result.stickyrole.roles.length) } : undefined, welcomer: result.welcomer, - disabledCommands: result.disabledCommands + disabledCommands: result.disabledCommands, + prefix: result.prefix }; return settings; } '2'(result) { - const settings = {}; + const { moderation, modlogs, muterole, mutetype, automod, wordFilter, ignore, invitefilter, + chatlogs, activity, selfrole, killitwithfire, memberlogs, staffRole, staffRule, modpoints, + userlogs, grantable, privatelog, linkfilter, autorole, welcomer, prefix, protection } = result; + const settings = this.client.defaultConfig('GUILD'); + + if (moderation || modlogs) + settings.moderation = { + channel: result.modlogs || null, + infractions: [...Infractions], + anonymous: moderation?.anonymous || false + }; + + + if (muterole || mutetype) + settings.mute = { + role: result.muterole || null, + type: result.mutetype || 0, + defaultDuration: 3600, + permanent: false + }; + + + if (automod) + settings.automod = { + enabled: result.automod?.enabled || false, + thresholds: result.automod?.thresholds || {}, + usePrevious: result.automod?.modBetweenT || false + }; + + + if (wordFilter) settings.wordfilter = { + enabled: result.wordFilter?.enabled || false, + explicit: result.wordFilter?.explicit === true ? result.wordFilter.filter : [], + fuzzy: result.wordFilter?.explicit === false ? result.wordFilter.filter : [], + whitelist: result.wordFilter?.whitelist || [], + regex: [], + silent: result.wordFilter?.silent || false, + actions: [], + ignore: result.wordFilter?.channelExempt || [], + bypass: result.wordFilter?.roleExempt || [] + }; + + if (linkfilter) settings.linkfilter = { + enabled: result.linkfilter?.enabled || false, + silent: result.linkfilter?.silent || false, + ignore: result.linkfilter?.channels || [], + bypass: result.linkfilter?.roles || [], + actions: [], + whitelist: result.linkfilter?.whitelist === true ? result.linkfilter.filter : [], + blacklist: result.linkfilter?.whitelist === false ? result.linkfilter.filter : [], + greylist: [] + }; + + if (ignore) settings.ignore = { + channels: result.ignore || [] + }; + + if (chatlogs) settings.messages = { + enabled: result.chatlogs?.enabled || false, + channel: result.chatlogs?.channel || null, + ignore: result.chatlogs?.channelignore || [], + bypass: result.chatlogs?.roleignore || [], + webhook: result.chatlogs?.webhook || null, + attachments: result.chatlogs?.imageLogs || false + }; + + if (memberlogs) settings.members = { + channel: result.memberlogs?.channel || null, + join: result.memberlogs?.join || '{mention} joined the server.', + leave: result.memberlogs?.leave || '**{tag}** ({id}) left the server.' + }; + + if (userlogs) { + settings.nicknames = { channel: result.userlogs?.nick ? result.userlogs.channel : null }; + settings.voice = { channel: result.userlogs?.vc ? result.userlogs.channel : null }; + } + + if (grantable) settings.grantable = { + roles: result.grantable?.roles || [], + enabled: result.grantable?.enabled || false + }; + + if (protection) settings.protection = { + type: result.protection?.type !== 0 ? 'role' : 'position', + enabled: true, + roles: result.protection?.roles || [] + }; + + if (autorole) settings.autorole = autorole; + if (prefix) settings.prefix = prefix; + if(welcomer) settings.welcomer = welcomer; + + // indexing: {enabled: false, description: result.desc }, + + + // activity: result.activity, + // selfrole: result.selfrole, + // dehoist: result.killitwithfire, + + // staffalert: result.staffRole && result.staffRule, + + + if (result.disabled) { + const resolved = []; + for (const cmd of result.disabled.commands) { + const [result] = this.client.resolver.components(cmd, 'command'); + if(result && !resolved.includes(result.resolveable)) resolved.push(result.resolveable); + } + for (const group of result.disabled.categories) { + const [mod] = this.client.resolver.components(group, 'module'); + if (mod) { + const cmds = mod.components.filter((comp) => comp._type === 'command').map((comp) => comp.resolveable); + for(const cmd of cmds) if(!resolved.includes(cmd)) resolved.push(cmd); + } + } + settings.disabledCommands = resolved; + } + + if (modpoints) { + const { modpoints } = result; + const points = {}, + expirations = {}; + + const entries = Object.entries(modpoints); + const _expirations = entries.filter(([key]) => key.includes('Expire')); + const _points = entries.filter(([key]) => !key.includes('Expire') && !['enabled', 'associations', 'multiplier'].includes(key)); + + for (const [key, value] of _points) points[key.toUpperCase()] = value; + for (let [key, value] of _expirations) { + key = key.replace('Expire', '').toUpperCase(); + expirations[key] = value; + } + + settings.modpoints = { + enabled: modpoints.enabled, + multiplier: modpoints.multiplier, + associations: modpoints.associations, + points, + expirations + }; + } + + if (invitefilter) { + const filter = { + ignore: [], + silent: false, + bypass: result.invitefilter.roles, + enabled: result.invitefilter.enabled, + actions: [] + }; + const channels = Object.entries(result.invitefilter.channels); + for (const [id, value] of channels) { + if(value > 0) filter.ignore.push(id); + } + settings.invitefilter = filter; + } + + if(moderation) { + settings.dminfraction = { + enabled: moderation.modloglevel > -1, + // eslint-disable-next-line no-nested-ternary + infractions: moderation.modloglevel === 2 ? + [...DmInfractions] : moderation.modloglevel === 1 ? + ['KICK', 'BAN', 'SOFTBAN'] : ['BAN'], + messages: { + default: 'You were **{infraction}** {from|on} the server `{server}`, your infraction details are below.' + } + }; + if (privatelog.onban) settings.dminfraction.messages.BAN = moderation.onban; + } + return settings; } - cleanUp(settings) { + cleanUp(settings = {}) { const keys = Object.keys(settings); for (const key of keys) { if(settings[key] === undefined) delete settings[key]; diff --git a/src/utilities/index.js b/src/utilities/index.js index 1998910..f5d4a7e 100644 --- a/src/utilities/index.js +++ b/src/utilities/index.js @@ -2,5 +2,6 @@ module.exports = { Util: require('./Util.js'), BinaryTree: require('./BinaryTree.js'), FilterUtil: require('./FilterUtil.js'), - Logger: require('./Logger.js') + Logger: require('./Logger.js'), + SettingsMigrator: require('./SettingsMigrator') }; \ No newline at end of file