UNPKG

muskytape

Version:

Framework não oficial do Discord.js

250 lines (220 loc) 7.81 kB
const SettingProvider = require('./base'); /** * Uses an SQLite database to store settings with guilds * @extends {SettingProvider} */ class SQLiteProvider extends SettingProvider { /** * @external SQLiteDatabase * @see {@link https://www.npmjs.com/package/sqlite} */ /** * @param {SQLiteDatabase} db - Database for the provider */ constructor(db) { super(); /** * Database that will be used for storing/retrieving settings * @type {SQLiteDatabase} */ this.db = db; /** * Client that the provider is for (set once the client is ready, after using {@link CommandoClient#setProvider}) * @name SQLiteProvider#client * @type {CommandoClient} * @readonly */ Object.defineProperty(this, 'client', { value: null, writable: true }); /** * Settings cached in memory, mapped by guild ID (or 'global') * @type {Map} * @private */ this.settings = new Map(); /** * Listeners on the Client, mapped by the event name * @type {Map} * @private */ this.listeners = new Map(); /** * Prepared statement to insert or replace a settings row * @type {SQLiteStatement} * @private */ this.insertOrReplaceStmt = null; /** * Prepared statement to delete an entire settings row * @type {SQLiteStatement} * @private */ this.deleteStmt = null; /** * @external SQLiteStatement * @see {@link https://www.npmjs.com/package/sqlite} */ } async init(client) { this.client = client; await this.db.run('CREATE TABLE IF NOT EXISTS settings (guild INTEGER PRIMARY KEY, settings TEXT)'); // Load all settings const rows = await this.db.all('SELECT CAST(guild as TEXT) as guild, settings FROM settings'); for(const row of rows) { let settings; try { settings = JSON.parse(row.settings); } catch(err) { client.emit('warn', `SQLiteProvider couldn't parse the settings stored for guild ${row.guild}.`); continue; } const guild = row.guild !== '0' ? row.guild : 'global'; this.settings.set(guild, settings); if(guild !== 'global' && !client.guilds.cache.has(row.guild)) continue; this.setupGuild(guild, settings); } // Prepare statements const statements = await Promise.all([ this.db.prepare('INSERT OR REPLACE INTO settings VALUES(?, ?)'), this.db.prepare('DELETE FROM settings WHERE guild = ?') ]); this.insertOrReplaceStmt = statements[0]; this.deleteStmt = statements[1]; // Listen for changes this.listeners .set('commandPrefixChange', (guild, prefix) => this.set(guild, 'prefix', prefix)) .set('commandStatusChange', (guild, command, enabled) => this.set(guild, `cmd-${command.name}`, enabled)) .set('groupStatusChange', (guild, group, enabled) => this.set(guild, `grp-${group.id}`, enabled)) .set('guildCreate', guild => { const settings = this.settings.get(guild.id); if(!settings) return; this.setupGuild(guild.id, settings); }) .set('commandRegister', command => { for(const [guild, settings] of this.settings) { if(guild !== 'global' && !client.guilds.cache.has(guild)) continue; this.setupGuildCommand(client.guilds.cache.get(guild), command, settings); } }) .set('groupRegister', group => { for(const [guild, settings] of this.settings) { if(guild !== 'global' && !client.guilds.cache.has(guild)) continue; this.setupGuildGroup(client.guilds.cache.get(guild), group, settings); } }); for(const [event, listener] of this.listeners) client.on(event, listener); } async destroy() { // Finalise prepared statements await Promise.all([ this.insertOrReplaceStmt.finalize(), this.deleteStmt.finalize() ]); // Remove all listeners from the client for(const [event, listener] of this.listeners) this.client.removeListener(event, listener); this.listeners.clear(); } get(guild, key, defVal) { const settings = this.settings.get(this.constructor.getGuildID(guild)); return settings ? typeof settings[key] !== 'undefined' ? settings[key] : defVal : defVal; } async set(guild, key, val) { guild = this.constructor.getGuildID(guild); let settings = this.settings.get(guild); if(!settings) { settings = {}; this.settings.set(guild, settings); } settings[key] = val; await this.insertOrReplaceStmt.run(guild !== 'global' ? guild : 0, JSON.stringify(settings)); if(guild === 'global') this.updateOtherShards(key, val); return val; } async remove(guild, key) { guild = this.constructor.getGuildID(guild); const settings = this.settings.get(guild); if(!settings || typeof settings[key] === 'undefined') return undefined; const val = settings[key]; settings[key] = undefined; await this.insertOrReplaceStmt.run(guild !== 'global' ? guild : 0, JSON.stringify(settings)); if(guild === 'global') this.updateOtherShards(key, undefined); return val; } async clear(guild) { guild = this.constructor.getGuildID(guild); if(!this.settings.has(guild)) return; this.settings.delete(guild); await this.deleteStmt.run(guild !== 'global' ? guild : 0); } /** * Loads all settings for a guild * @param {string} guild - Guild ID to load the settings of (or 'global') * @param {Object} settings - Settings to load * @private */ setupGuild(guild, settings) { if(typeof guild !== 'string') throw new TypeError('The guild must be a guild ID or "global".'); guild = this.client.guilds.cache.get(guild) || null; // Load the command prefix if(typeof settings.prefix !== 'undefined') { if(guild) guild._commandPrefix = settings.prefix; else this.client._commandPrefix = settings.prefix; } // Load all command/group statuses for(const command of this.client.registry.commands.values()) this.setupGuildCommand(guild, command, settings); for(const group of this.client.registry.groups.values()) this.setupGuildGroup(guild, group, settings); } /** * Sets up a command's status in a guild from the guild's settings * @param {?CommandoGuild} guild - Guild to set the status in * @param {Command} command - Command to set the status of * @param {Object} settings - Settings of the guild * @private */ setupGuildCommand(guild, command, settings) { if(typeof settings[`cmd-${command.name}`] === 'undefined') return; if(guild) { if(!guild._commandsEnabled) guild._commandsEnabled = {}; guild._commandsEnabled[command.name] = settings[`cmd-${command.name}`]; } else { command._globalEnabled = settings[`cmd-${command.name}`]; } } /** * Sets up a command group's status in a guild from the guild's settings * @param {?CommandoGuild} guild - Guild to set the status in * @param {CommandGroup} group - Group to set the status of * @param {Object} settings - Settings of the guild * @private */ setupGuildGroup(guild, group, settings) { if(typeof settings[`grp-${group.id}`] === 'undefined') return; if(guild) { if(!guild._groupsEnabled) guild._groupsEnabled = {}; guild._groupsEnabled[group.id] = settings[`grp-${group.id}`]; } else { group._globalEnabled = settings[`grp-${group.id}`]; } } /** * Updates a global setting on all other shards if using the {@link ShardingManager}. * @param {string} key - Key of the setting to update * @param {*} val - Value of the setting * @private */ updateOtherShards(key, val) { if(!this.client.shard) return; key = JSON.stringify(key); val = typeof val !== 'undefined' ? JSON.stringify(val) : 'undefined'; this.client.shard.broadcastEval(` if(this.shard.id !== ${this.client.shard.id} && this.provider && this.provider.settings) { let global = this.provider.settings.get('global'); if(!global) { global = {}; this.provider.settings.set('global', global); } global[${key}] = ${val}; } `); } } module.exports = SQLiteProvider;