From 3c0352ff099ab2a2918eb2f953f392acc0c6dac8 Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Tue, 14 Jul 2020 00:13:32 +0300 Subject: [PATCH] Add support for hooks. Add beforeNewThread hook. Allow overriding new thread category id in createNewThreadForUser(). --- src/data/threads.js | 15 ++++++++++----- src/hooks.js | 35 +++++++++++++++++++++++++++++++++++ src/main.js | 25 +++++++++++++++++++++++-- src/modules/newthread.js | 2 +- 4 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 src/hooks.js diff --git a/src/data/threads.js b/src/data/threads.js index 75685b0..aac6bf2 100644 --- a/src/data/threads.js +++ b/src/data/threads.js @@ -52,12 +52,17 @@ function getHeaderGuildInfo(member) { /** * Creates a new modmail thread for the specified user * @param {User} user - * @param {Member} member - * @param {Boolean} quiet If true, doesn't ping mentionRole or reply with responseMessage + * @param {Object} opts + * @param {Boolean} opts.quiet If true, doesn't ping mentionRole or reply with responseMessage + * @param {Boolean} opts.ignoreRequirements If true, creates a new thread even if the account doesn't meet requiredAccountAge + * @param {String} opts.categoryId Override the category ID for the new thread * @returns {Promise} * @throws {Error} */ -async function createNewThreadForUser(user, quiet = false, ignoreRequirements = false) { +async function createNewThreadForUser(user, opts = {}) { + const quiet = opts.quiet != null ? opts.quiet : false; + const ignoreRequirements = opts.ignoreRequirements != null ? opts.ignoreRequirements : false; + const existingThread = await findOpenThreadByUserId(user.id); if (existingThread) { throw new Error('Attempted to create a new thread for a user with an existing open thread!'); @@ -126,9 +131,9 @@ async function createNewThreadForUser(user, quiet = false, ignoreRequirements = console.log(`[NOTE] Creating new thread channel ${channelName}`); // Figure out which category we should place the thread channel in - let newThreadCategoryId; + let newThreadCategoryId = opts.categoryId || null; - if (config.categoryAutomation.newThreadFromGuild) { + if (! newThreadCategoryId && config.categoryAutomation.newThreadFromGuild) { // Categories for specific source guilds (in case of multiple main guilds) for (const [guildId, categoryId] of Object.entries(config.categoryAutomation.newThreadFromGuild)) { if (userGuildData.has(guildId)) { diff --git a/src/hooks.js b/src/hooks.js new file mode 100644 index 0000000..1896177 --- /dev/null +++ b/src/hooks.js @@ -0,0 +1,35 @@ +/** + * @callback BeforeNewThreadHook_SetCategoryId + * @param {String} categoryId + * @return void + */ + +/** + * @typedef BeforeNewThreadHookEvent + * @property {Function} cancel + * @property {BeforeNewThreadHook_SetCategoryId} setCategoryId + * + */ + +/** + * @callback BeforeNewThreadHook + * @param {BeforeNewThreadHookEvent} ev + * @return {void|Promise} + */ + +/** + * @type BeforeNewThreadHook[] + */ +const beforeNewThreadHooks = []; + +/** + * @param {BeforeNewThreadHook} fn + */ +function beforeNewThread(fn) { + beforeNewThreadHooks.push(fn); +} + +module.exports = { + beforeNewThreadHooks, + beforeNewThread, +}; diff --git a/src/main.js b/src/main.js index e8f7258..c1f9c2c 100644 --- a/src/main.js +++ b/src/main.js @@ -8,6 +8,7 @@ const {messageQueue} = require('./queue'); const utils = require('./utils'); const { createCommandManager } = require('./commands'); const { getPluginAPI, loadPlugin } = require('./plugins'); +const { beforeNewThreadHooks } = require('./hooks'); const blocked = require('./data/blocked'); const threads = require('./data/threads'); @@ -123,13 +124,33 @@ function initBaseMessageHandlers() { messageQueue.add(async () => { let thread = await threads.findOpenThreadByUserId(msg.author.id); - // New thread if (! thread) { // Ignore messages that shouldn't usually open new threads, such as "ok", "thanks", etc. if (config.ignoreAccidentalThreads && msg.content && ACCIDENTAL_THREAD_MESSAGES.includes(msg.content.trim().toLowerCase())) return; - thread = await threads.createNewThreadForUser(msg.author); + let cancelled = false; + let categoryId = null; + + /** + * @type {BeforeNewThreadHookEvent} + */ + const ev = { + cancel() { + cancelled = true; + }, + + setCategoryId(_categoryId) { + categoryId = _categoryId; + }, + }; + + for (const hook of beforeNewThreadHooks) { + await hook(ev, msg); + if (cancelled) return; + } + + thread = await threads.createNewThreadForUser(msg.author, { categoryId }); } if (thread) { diff --git a/src/modules/newthread.js b/src/modules/newthread.js index aca6f54..35f70d8 100644 --- a/src/modules/newthread.js +++ b/src/modules/newthread.js @@ -15,7 +15,7 @@ module.exports = ({ bot, knex, config, commands }) => { return; } - const createdThread = await threads.createNewThreadForUser(user, true, true); + const createdThread = await threads.createNewThreadForUser(user, { quiet: true, ignoreRequirements: true }); createdThread.postSystemMessage(`Thread was opened by ${msg.author.username}#${msg.author.discriminator}`); msg.channel.createMessage(`Thread opened: <#${createdThread.channel_id}>`);