From 0098d232771eb4f560a218e7a7ddec3f83b4c97b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Sevilla=20Mart=C3=ADn?= Date: Sat, 14 Oct 2017 13:15:27 -0400 Subject: [PATCH] fix(models): fix no config found errors by creating config on the spot --- lib/Discord/Client.js | 27 +++-- lib/Discord/Commands/Conf.js | 8 +- lib/Discord/Commands/GitlabInit.js | 12 +-- lib/Discord/Commands/GitlabInitOrg.js | 8 +- lib/Discord/Commands/GitlabIssue.js | 4 +- lib/Discord/Commands/GitlabMergeRequest.js | 4 +- lib/Discord/Commands/GitlabRemove.js | 2 +- lib/Models/ChannelConfig.js | 115 +++++++++++++-------- lib/Models/ServerConfig.js | 5 +- lib/Web.js | 2 +- 10 files changed, 115 insertions(+), 72 deletions(-) diff --git a/lib/Discord/Client.js b/lib/Discord/Client.js index c522e52..452c25e 100644 --- a/lib/Discord/Client.js +++ b/lib/Discord/Client.js @@ -123,6 +123,9 @@ class Client extends DiscordClient { this.emit('error', `Module | ${f}`, error); } }); + + this.middleware = this.middleware.sort((a, b) => b.priority - a.priority); + return this; }); } @@ -168,30 +171,36 @@ class Client extends DiscordClient { }); } + run(msg) { + this.execute(msg) + .catch(e => this.middleware.last().run(msg, null, null, null, e)) + .catch(Log.error); + } + /** * Message event handling, uses modules (aka middleware) * @param {Message} msg * @return {Client} */ - run(msg) { + async execute(msg) { if (msg.author.equals(this.user) || msg.author.bot) return; - let serverConf = msg.guild ? ServerConfig.get(msg.guild.id) : {}; - let prefix = serverConf.prefix || Math.random(); + const serverConf = msg.guild.available ? ServerConfig.get(msg.guild.id) : await ServerConfig.add(msg.guild); + const prefix = serverConf.prefix || Math.random(); if (msg.channel.type !== 'dm' && !msg.content.startsWith(this.user.toString()) && !msg.content.startsWith(prefix) && !msg.content.startsWith(this.prefix)) return false; - let content = (msg.content.startsWith(prefix) && msg.content.replace(prefix, '')) || (msg.content.startsWith(this.user.toString()) && msg.content.replace(`${this.user.toString()} `, '')) || (msg.content.startsWith(this.prefix) && msg.content.replace(this.prefix, '')) || msg.content; - let command = content.split(' ')[0].toLowerCase(); - let args = content.split(' ').slice(1); + const content = (msg.content.startsWith(prefix) && msg.content.replace(prefix, '')) || (msg.content.startsWith(this.user.toString()) && msg.content.replace(`${this.user.toString()} `, '')) || (msg.content.startsWith(this.prefix) && msg.content.replace(this.prefix, '')) || msg.content; + const command = content.split(' ')[0].toLowerCase(); + const args = content.split(' ').slice(1); - let middleware = this.middleware.array().sort((a, b) => b.priority - a.priority); + const middleware = this.middleware.array(); let i = 0; const handleErr = (err, currentMiddleware) => middleware[middleware.length - 1].run(msg, args, next, currentMiddleware, err); const next = (err) => { - let currentMiddleware = middleware[i] || middleware[i - 1]; - let nextMiddleware = middleware[i++]; + const currentMiddleware = middleware[i] || middleware[i - 1]; + const nextMiddleware = middleware[i++]; if (err) return handleErr(err, currentMiddleware); if (nextMiddleware) { try { diff --git a/lib/Discord/Commands/Conf.js b/lib/Discord/Commands/Conf.js index 2684ec7..68295d1 100644 --- a/lib/Discord/Commands/Conf.js +++ b/lib/Discord/Commands/Conf.js @@ -22,10 +22,11 @@ class ConfCommand extends Command { guildOnly: true, }); } - run(msg, args) { + + async run(msg, args) { const action = args.filter(e => !e.includes('-'))[0] || 'view'; const serverConf = ServerConfig.get(msg.guild.id); - const channelConf = ChannelConfig.FindByChannel(msg.channel.id); + const channelConf = ChannelConfig.get(msg.channel.id) || await ChannelConfig.add(msg.channel); if (action === 'view') return this._view(msg, args, channelConf, serverConf); if (action === 'get') return this._get(msg, args, channelConf, serverConf); @@ -61,7 +62,7 @@ class ConfCommand extends Command { const key = args.filter(e => !e.includes('-'))[1]; const conf = (args.includes('--global') || args.includes('-g')) ? serverConf : channelConf; - let embed = new this.embed() + const embed = new this.embed() .setColor('#FB9738') .setAuthor(conf === channelConf ? `${guild.name} #${channel.name}` : guild.name, guild.iconURL) .setDescription(`This is your current ${conf === channelConf ? 'channel' : 'server'}'s configuration.`) @@ -82,6 +83,7 @@ class ConfCommand extends Command { author: conf === channelConf ? `${guild.name} #${channel.name}` : guild.name, confName: conf === channelConf ? 'channel' : 'server', }; + if (!validKeys.includes(key)) { const embed = new this.embed() .setColor('#CE0814') diff --git a/lib/Discord/Commands/GitlabInit.js b/lib/Discord/Commands/GitlabInit.js index 88f5991..e80e2ec 100644 --- a/lib/Discord/Commands/GitlabInit.js +++ b/lib/Discord/Commands/GitlabInit.js @@ -45,12 +45,13 @@ class GitlabInitCommand extends Command { }, }); + const conf = ChannelConfig.get(channelid) || await ChannelConfig.add(msg.channel); + if (!repository.isGitlab || isPrivate) { - // GitlabCache.add(repository.repo); - const conf = ChannelConfig.FindByChannel(channelid); + // GitlabCache.add(repository.repo);; const exists = conf && conf.repos && conf.repos.includes(repoFullName); if (exists) return this.commandError(msg, `Repository \`${repository.repo}\` is already initialized in this channel`); - return ChannelConfig.AddRepoToChannel(channelid, repoFullName) + return ChannelConfig.addRepoToChannel(channelid, repoFullName) .then(() => { let embed = this._successMessage(repository.repo); return workingMsg.edit({ embed }); @@ -58,16 +59,15 @@ class GitlabInitCommand extends Command { } return Gitlab.getRepo(repository.repo).then(() => { - const conf = ChannelConfig.FindByChannel(channelid); const exists = conf && conf.repos && conf.repos.includes(repoFullName); if (exists) return this.commandError(msg, `Repository \`${repository.repo}\` is already initialized in this channel`); - return ChannelConfig.AddRepoToChannel(channelid, repoFullName) + return ChannelConfig.addRepoToChannel(channelid, repoFullName) .then(() => { let embed = this._successMessage(repository.repo); return workingMsg.edit({ embed }); }); }).catch(err => { - let errorMessage = err && err.name ? err.name : err || null; + const errorMessage = err && err.name ? err.name : err || null; if (errorMessage && errorMessage !== 'Gitlab404Error') return this.commandError(msg, `Unable to get repository info for \`${repo}\`\n${err}`); return this.commandError(msg, `Unable to initialize! The repository \`${repository.repo}\` could not be found!\nIf it's private, please add \`private\` after the command as a separate argument`); }); diff --git a/lib/Discord/Commands/GitlabInitOrg.js b/lib/Discord/Commands/GitlabInitOrg.js index ccfc055..e6bc88b 100644 --- a/lib/Discord/Commands/GitlabInitOrg.js +++ b/lib/Discord/Commands/GitlabInitOrg.js @@ -39,18 +39,18 @@ class GitlabInitOrgCommand extends Command { }, }); - return Gitlab.getGroupProjects(orgName).then(data => { - const conf = ChannelConfig.FindByChannel(channelid); + return Gitlab.getGroupProjects(orgName).then(async data => { + const conf = ChannelConfig.get(channelid) || await ChannelConfig.add(msg.channel); const repos = data.body.filter(e => !e.private && !conf.repos.includes(e.name_with_namespace.toLowerCase())).map(e => e.name_with_namespace); - return Promise.all(repos.map(repo => ChannelConfig.AddRepoToChannel(channelid, repo))).then(() => repos); + return Promise.all(repos.map(repo => ChannelConfig.addRepoToChannel(channelid, repo))).then(() => repos); }) .then(repos => { const embed = this._successMessage(orgName, repos); return workingMsg.edit({ embed }); }) .catch(err => { - let errorMessage = err && err.message ? err.message : err || null; + const errorMessage = err && err.message ? err.message : err || null; Log.error(err); diff --git a/lib/Discord/Commands/GitlabIssue.js b/lib/Discord/Commands/GitlabIssue.js index 92b8580..7c2d5ad 100644 --- a/lib/Discord/Commands/GitlabIssue.js +++ b/lib/Discord/Commands/GitlabIssue.js @@ -34,7 +34,7 @@ class GitlabIssue extends Command { _issue(msg, args) { const issueNumber = parseInt(args[0].replace(/#/g, '')); - let repository = ChannelConfig.FindByChannel(msg.channel.id).repo; + let repository = ChannelConfig.get(msg.channel.id).repo; if (!repository) return this.commandError(msg, Gitlab.Constants.Errors.NO_REPO_CONFIGURED(this)); if (!issueNumber) return this.errorUsage(msg); @@ -75,7 +75,7 @@ class GitlabIssue extends Command { if (!query) return false; - let repository = ChannelConfig.FindByChannel(channelId).repo; + let repository = ChannelConfig.get(channelId).repo; if (!repository) return this.commandError(msg, Gitlab.Constants.Errors.NO_REPO_CONFIGURED(this)); diff --git a/lib/Discord/Commands/GitlabMergeRequest.js b/lib/Discord/Commands/GitlabMergeRequest.js index 9f49799..1adb0c3 100644 --- a/lib/Discord/Commands/GitlabMergeRequest.js +++ b/lib/Discord/Commands/GitlabMergeRequest.js @@ -37,7 +37,7 @@ class GitlabIssue extends Command { _mr(msg, args) { const mrNumber = parseInt(args[0].replace(/!/g, '')); - let repository = ChannelConfig.FindByChannel(msg.channel.id).repo; + let repository = ChannelConfig.get(msg.channel.id).repo; if (!repository) return this.commandError(msg, Gitlab.Constants.Errors.NO_REPO_CONFIGURED(this)); if (!mrNumber) return this.errorUsage(msg); @@ -75,7 +75,7 @@ class GitlabIssue extends Command { _list(msg, args) { let channelId = msg.channel.id; let page = args[1] ? parseInt(args) : 1; - let repository = ChannelConfig.FindByChannel(channelId).repo; + let repository = ChannelConfig.get(channelId).repo; if (!repository) return this.commandError(msg, Gitlab.Constants.Errors.NO_REPO_CONFIGURED(this)); return Gitlab.getProjectMergeRequests(repository, null, { diff --git a/lib/Discord/Commands/GitlabRemove.js b/lib/Discord/Commands/GitlabRemove.js index 8947b77..f296428 100644 --- a/lib/Discord/Commands/GitlabRemove.js +++ b/lib/Discord/Commands/GitlabRemove.js @@ -24,7 +24,7 @@ class GitlabRemoveCommand extends Command { async run(msg, args) { const channelid = msg.channel.id; - const conf = ChannelConfig.FindByChannel(channelid); + const conf = ChannelConfig.get(channelid) || await ChannelConfig.add(msg.channel); const repo = args[0] ? parse(args[0].toLowerCase()) : {}; let repoFound = repo && !!repo.repo; diff --git a/lib/Models/ChannelConfig.js b/lib/Models/ChannelConfig.js index e63c220..7128cf3 100644 --- a/lib/Models/ChannelConfig.js +++ b/lib/Models/ChannelConfig.js @@ -56,11 +56,11 @@ class ChannelConfigItem { * Set a specific config property to a value for this config item * @param {String} prop Property to modify * @param {String} value The new value for the property - * @see ChannelConfig#SetChannel + * @see ChannelConfig#set * @return {Promise} */ set(prop, value) { - return this._client.SetChannel(this.channelID, prop, value); + return this._client.set(this.channelID, prop, value); } /** * Delete repo events from channel @@ -96,9 +96,9 @@ class ChannelConfig { this.setupEvents = false; /** - * Loaded - * @type {Boolean} - */ + * Loaded + * @type {Boolean} + */ this.loaded = false; } /** @@ -113,17 +113,17 @@ class ChannelConfig { }).catch(Log.error); } /** - * Initialize configuration and Discord bot events - * @param {external:Client} bot - */ + * Initialize configuration and Discord bot events + * @param {external:Client} bot + */ init(bot) { if (!this.loaded) return setTimeout(() => this.init(bot), 5000); for (const ch of bot.channels) { const channel = ch[1]; if (!channel || channel.type !== 'text') continue; - if (!this.FindByChannel(channel.id)) { + if (!this.has(channel.id)) { Log.info(`ChannelConf | Adding "${channel.guild.name}"'s #${channel.name} (${channel.id})`); - this.AddChannel(channel).catch(e => bot.emit('error', e)); + this.add(channel).catch(e => bot.emit('error', e)); } } if (this.setupEvents) return; @@ -131,14 +131,13 @@ class ChannelConfig { bot.on('channelDelete', channel => { if (!channel || channel.type !== 'text') return; Log.info(`ChannelConf | Deleting "${channel.guild.name}"'s #${channel.name} (${channel.id})`); - this.DeleteChannel(channel.id).catch(Log.error); + this.delete(channel.id).catch(Log.error); }); bot.on('channelCreate', channel => { if (!channel || channel.type !== 'text') return; - let ch = this.FindByChannel(channel.id); - if (ch) return; + if (this.has(channel.id)) return; Log.info(`ChannelConf | Adding "${channel.guild.name}"'s #${channel.name} (${channel.id})`); - this.AddChannel(channel); + this.add(channel).catch(e => bot.emit('error', e)); }); } /** @@ -146,27 +145,26 @@ class ChannelConfig { * @param {String} repo Repo for the events * @return {ChannelConfigItem} */ - FindByRepo(repo) { + findByRepo(repo) { let re = repo.toLowerCase(); return this._data.filter(e => e.repos.filter(r => r === re)[0]); } /** - * Find events for channel - * @param {String} channel Channel (ID) with the events + * Get channel config + * @param {String} channel Channel ID * @return {ChannelConfigItem} */ - FindByChannel(channel) { - return this._data.find('channelID', channel); + get(channel) { + return this._data.get(channel); } /** - * Find channels with events for repo - * @param {String} channel Channel (ID) with the repo's events - * @param {String} repo Repo to check in channel - * @return {ChannelConfigItem} + * Has channel in config + * @param {String} channel Channel ID + * @return {Boolean} */ - FindRepoInChannel(channel, repo) { - return this._data.find(e => e.channelID = channel && e.repos.includes(repo)); + has(channel) { + return this._data.has(channel); } /** @@ -174,26 +172,40 @@ class ChannelConfig { * @param {String} channel Channel with the events to delete * @return {Promise} */ - DeleteChannel(channel) { + delete(channelID) { return channelConfig.findOneAndRemove({ - channelID: channel, + channelID: channelID, }).then(() => { - let oldData = this._data; - let newData = oldData.filter(e => e.channel !== channel); - this._data = newData; + this._data.delete(channelID); return Promise.resolve(this); }); } + /** + * Delete all channels from guild + * @param {String} guildID Guild (ID) to delete channel configs for + * @return {Promise} + */ + deleteFromGuild(guildID) { + return Promise.all( + this._data + .filter(c => c.guildID === guildID) + .map(c => { + Log.info(`ChannelConf | Deleting "${c.guildName}"'s #${c.channelName} (${c.channelID})`); + return this.delete(c.channelID); + }), + ).then(() => this); + } + /** * Add channel to config * @param {Channel} channel Channel to add repo events * @return {Promise} */ - AddChannel(channel) { + add(channel) { if (!channel || !channel.id) return Promise.reject(`No channel passed!`); - if (channel && channel.id && this.FindByChannel(channel.id)) return Promise.reject(`Channel already has an entry in database`); - let conf = { + if (channel && channel.id && this.get(channel.id)) return Promise.reject(`Channel already has an entry in database`); + const conf = { guildID: channel.guild && channel.guild.id, guildName: channel.guild && channel.guild.name, channelID: channel.id, @@ -206,11 +218,28 @@ class ChannelConfig { ], }; return channelConfig.create(conf).then(() => { - this._data.set(conf.channelID, new ChannelConfigItem(this, conf)); - return Promise.resolve(this); + const item = new ChannelConfigItem(this, conf); + this._data.set(conf.channelID, item); + return item; }); } + /** + * Add all channels from guild + * @param {Guild} guild Guild obj to add channel configs for + * @return {Promise} + */ + addFromGuild(guild) { + return Promise.all( + guild.channels + .filter(c => c.type === 'text') + .map(c => { + Log.info(`ChannelConf | Adding "${c.guildName}"'s #${c.channelName} (${c.channelID})`); + return this.add(c); + }), + ).then(() => this); + } + /** * Replace specific channel config prop with value * @param {String} channel Channel with the repo events @@ -218,7 +247,7 @@ class ChannelConfig { * @param {String} value Value to set property to * @return {Promise} updated config item */ - SetChannel(channel, prop, value) { + set(channel, prop, value) { return new Promise((resolve, reject) => { let oldConfig = this._data.find('channelID', channel); let newConfig = oldConfig; @@ -239,31 +268,31 @@ class ChannelConfig { * Add repo to channel * @param {Channel} channel Channel to add repo to * @param {String} repo repo to add to channel - * @see ChannelConfig#SetChannel + * @see ChannelConfig#set * @return {Promise} */ - AddRepoToChannel(channel, repo) { + addRepoToChannel(channel, repo) { if (!channel || !repo) return Promise.reject(`Invalid arguments.`); - let conf = this.FindByChannel(channel); + let conf = this.get(channel); let repos = conf.repos; repos.push(repo.toLowerCase()); - return this.SetChannel(channel, 'repos', repos); + return this.set(channel, 'repos', repos); } /** * Delete repo events from specific channel * @param {String} channel Channel with the repo events * @param {String} repo Repo event to remove from Channel - * @see ChannelConfig#SetChannel + * @see ChannelConfig#set * @return {Promise} */ - DeleteRepo(channel, repo) { + deleteRepo(channel, repo) { return channelConfig.findOneAndRemove({ repo, }).then(() => { let oldRepos = this._data.find('channelID', channel); let newRepos = oldRepos.slice(0, oldRepos.indexOf(repo)); - return this.SetChannel(channel, 'repos', newRepos); + return this.set(channel, 'repos', newRepos); }); } } diff --git a/lib/Models/ServerConfig.js b/lib/Models/ServerConfig.js index 1602446..6caa14a 100644 --- a/lib/Models/ServerConfig.js +++ b/lib/Models/ServerConfig.js @@ -1,6 +1,7 @@ const mongoose = require('mongoose'); const Collection = require('discord.js').Collection; const Schema = mongoose.Schema; +const ChannelConfig = require('./ChannelConfig'); /** * The server config Schema @@ -109,6 +110,7 @@ class ServerConfig { if (!guild || !guild.available) return; Log.info(`ServerConf | Deleting "${guild.name}"`); this.delete(guild.id).catch(e => bot.emit('error', e)); + ChannelConfig.deleteFromGuild(guild.id).catch(e => bot.emit('error', e)); }); bot.on('guildCreate', (guild) => { if (!guild || !guild.available) return; @@ -116,6 +118,7 @@ class ServerConfig { if (g) return; Log.info(`ServerConf | Adding "${guild.name}"`); this.add(guild).catch(e => bot.emit('error', e)); + ChannelConfig.addFromGuild(guild).catch(e => bot.emit('error', e)); }); } @@ -138,7 +141,7 @@ class ServerConfig { /** * Add channel to config - * @param {Guild} guildID Guild to add config of + * @param {Guild} guild Guild to add config of * @return {Promise} */ add(guild) { diff --git a/lib/Web.js b/lib/Web.js index 3850bef..217ab2c 100644 --- a/lib/Web.js +++ b/lib/Web.js @@ -46,7 +46,7 @@ app.post('/', (req, res) => { if (!event || !data || !data.project) return res.status(403).send('Invalid data. Plz use Gitlab webhooks.'); const repo = data.project.path_with_namespace; - const channels = ChannelConfig.FindByRepo(repo); + const channels = ChannelConfig.findByRepo(repo); const actionText = data.object_attributes && data.object_attributes.action ? `/${data.object_attributes.action}` : ''; Log.verbose(`GitLab | ${repo} - ${eventName}${actionText} (${channels.size} channels)`); res.send(`${data.project.path_with_namespace} : Received ${eventName}${actionText}, emitting to ${channels.size} channels...`);