Skip to content

Instantly share code, notes, and snippets.

@appellation
Created January 4, 2017 05:12
Show Gist options
  • Save appellation/b9c8d54209affc0e5d0fb43dcfabf156 to your computer and use it in GitHub Desktop.
Save appellation/b9c8d54209affc0e5d0fb43dcfabf156 to your computer and use it in GitHub Desktop.
const {SettingProvider} = require('discord.js-commando');
const bluebird = require('bluebird');
const redis = require('redis');
const erlpack = require('erlpack');
const mergeOptions = require('merge-options');
bluebird.promisifyAll(redis.RedisClient.prototype);
bluebird.promisifyAll(redis.Multi.prototype);
module.exports = class RedisProvider extends SettingProvider {
constructor(options = {}) {
super();
this.options = mergeOptions(options, {
return_buffers: true
});
this.listeners = new Map();
}
async init(client) {
this.client = client;
this.db = redis.createClient(this.options);
const finished = new Promise((resolve, reject) => {
this.db.once('ready', resolve);
this.db.once('error', reject);
});
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.name}`, enabled))
.set('guildCreate', async guild => {
const settings = await this.getAll(guild);
if(!settings) return;
this.setupGuild(guild.id, settings);
});
for(const [event, listener] of this.listeners) this.client.on(event, listener);
for(const [, guild] of this.client.guilds) this.setupGuild(guild.id, await this.getAll(guild))
return finished;
}
async get(guild, key) {
try {
return erlpack.unpack(await this.db.hgetAsync(`settings:${SettingProvider.getGuildID(guild)}`, key));
} catch (e) {
return null;
}
}
async getAll(guild) {
return new Promise((resolve, reject) => {
this.db.hgetall(`settings:${SettingProvider.getGuildID(guild)}`, (err, res) => {
if(err) return reject(err);
if(res === null) return resolve({});
const out = {};
for(const key of Object.keys(res)) {
out[key] = erlpack.unpack(res[key]);
}
return resolve(out);
});
});
}
async set(guild, key, value) {
return this.db.hsetAsync(`settings:${SettingProvider.getGuildID(guild)}`, key, erlpack.pack(value));
}
async remove(guild, key) {
return this.db.hdelAsync(`settings:${SettingProvider.getGuildID(guild)}`, key);
}
async clear(guild) {
return this.db.delAsync(`settings:${SettingProvider.getGuildID(guild)}`);
}
async incr(guild, key, count = 1) {
return this.db.hincrbyAsync(`settings:${SettingProvider.getGuildID(guild)}`, key, count);
}
async decr(guild, key, count = 1) {
return this.db.hdecrbyAsync(`settings:${SettingProvider.getGuildID(guild)}`, key, count);
}
async keys(guild) {
return this.db.hkeysAsync(`settings:${SettingProvider.getGuildID(guild)}`);
}
async vals(guild) {
const vals = await this.db.hvalsAsync(`settings:${SettingProvider.getGuildID(guild)}`);
return vals.map(v => erlpack.unpack(v));
}
async exists(guild, key) {
return this.db.hexistsAsync(`settings:${SettingProvider.getGuildID(guild)}`, key);
}
async addWarn(guild, user) {
return this.incr(guild, `warns:${user.id}`);
}
async removeWarn(guild, user) {
return this.decr(guild, `warns:${user.id}`);
}
destroy() {
for(const [event, listener] of this.listeners) this.client.removeListener(event, listener);
this.listeners.clear();
this.client.quit();
}
/**
* 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.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 {?Guild} 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 group's status in a guild from the guild's settings
* @param {?Guild} 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.name}`] === 'undefined') return;
if(guild) {
if(!guild._groupsEnabled) guild._groupsEnabled = {};
guild._groupsEnabled[group.name] = settings[`grp:${group.name}`];
} else {
group._globalEnabled = settings[`grp:${group.name}`];
}
}
/**
* 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) {
this.provider.settings.global[${key}] = ${val};
}
`);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment