Add 'source' to beforeNewThread hooks, call hooks in threads.createNewThreadForUser()
parent
75b2920777
commit
3723bf788b
|
@ -12,6 +12,7 @@ const utils = require('../utils');
|
||||||
const updates = require('./updates');
|
const updates = require('./updates');
|
||||||
|
|
||||||
const Thread = require('./Thread');
|
const Thread = require('./Thread');
|
||||||
|
const {callBeforeNewThreadHooks} = require("../hooks/beforeNewThread");
|
||||||
const {THREAD_STATUS} = require('./constants');
|
const {THREAD_STATUS} = require('./constants');
|
||||||
|
|
||||||
const MINUTES = 60 * 1000;
|
const MINUTES = 60 * 1000;
|
||||||
|
@ -49,13 +50,17 @@ function getHeaderGuildInfo(member) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef CreateNewThreadForUserOpts
|
||||||
|
* @property {boolean} quiet If true, doesn't ping mentionRole or reply with responseMessage
|
||||||
|
* @property {boolean} ignoreRequirements If true, creates a new thread even if the account doesn't meet requiredAccountAge
|
||||||
|
* @property {string} source A string identifying the source of the new thread
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new modmail thread for the specified user
|
* Creates a new modmail thread for the specified user
|
||||||
* @param {User} user
|
* @param {User} user
|
||||||
* @param {Object} opts
|
* @param {CreateNewThreadForUserOpts} 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<Thread|undefined>}
|
* @returns {Promise<Thread|undefined>}
|
||||||
* @throws {Error}
|
* @throws {Error}
|
||||||
*/
|
*/
|
||||||
|
@ -68,6 +73,9 @@ async function createNewThreadForUser(user, opts = {}) {
|
||||||
throw new Error('Attempted to create a new thread for a user with an existing open thread!');
|
throw new Error('Attempted to create a new thread for a user with an existing open thread!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hookResult = await callBeforeNewThreadHooks({ user, opts });
|
||||||
|
if (hookResult.cancelled) return;
|
||||||
|
|
||||||
// If set in config, check that the user's account is old enough (time since they registered on Discord)
|
// If set in config, check that the user's account is old enough (time since they registered on Discord)
|
||||||
// If the account is too new, don't start a new thread and optionally reply to them with a message
|
// If the account is too new, don't start a new thread and optionally reply to them with a message
|
||||||
if (config.requiredAccountAge && ! ignoreRequirements) {
|
if (config.requiredAccountAge && ! ignoreRequirements) {
|
||||||
|
@ -131,7 +139,7 @@ async function createNewThreadForUser(user, opts = {}) {
|
||||||
console.log(`[NOTE] Creating new thread channel ${channelName}`);
|
console.log(`[NOTE] Creating new thread channel ${channelName}`);
|
||||||
|
|
||||||
// Figure out which category we should place the thread channel in
|
// Figure out which category we should place the thread channel in
|
||||||
let newThreadCategoryId = opts.categoryId || null;
|
let newThreadCategoryId = hookResult.categoryId || null;
|
||||||
|
|
||||||
if (! newThreadCategoryId && config.categoryAutomation.newThreadFromGuild) {
|
if (! newThreadCategoryId && config.categoryAutomation.newThreadFromGuild) {
|
||||||
// Categories for specific source guilds (in case of multiple main guilds)
|
// Categories for specific source guilds (in case of multiple main guilds)
|
||||||
|
@ -340,11 +348,16 @@ async function getClosedThreadCountByUserId(userId) {
|
||||||
return parseInt(row.thread_count, 10);
|
return parseInt(row.thread_count, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findOrCreateThreadForUser(user) {
|
/**
|
||||||
|
* @param {User} user
|
||||||
|
* @param {CreateNewThreadForUserOpts} opts
|
||||||
|
* @returns {Promise<Thread|undefined>}
|
||||||
|
*/
|
||||||
|
async function findOrCreateThreadForUser(user, opts = {}) {
|
||||||
const existingThread = await findOpenThreadByUserId(user.id);
|
const existingThread = await findOpenThreadByUserId(user.id);
|
||||||
if (existingThread) return existingThread;
|
if (existingThread) return existingThread;
|
||||||
|
|
||||||
return createNewThreadForUser(user);
|
return createNewThreadForUser(user, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getThreadsThatShouldBeClosed() {
|
async function getThreadsThatShouldBeClosed() {
|
||||||
|
|
35
src/hooks.js
35
src/hooks.js
|
@ -1,35 +0,0 @@
|
||||||
/**
|
|
||||||
* @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<void>}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type BeforeNewThreadHook[]
|
|
||||||
*/
|
|
||||||
const beforeNewThreadHooks = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {BeforeNewThreadHook} fn
|
|
||||||
*/
|
|
||||||
function beforeNewThread(fn) {
|
|
||||||
beforeNewThreadHooks.push(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
beforeNewThreadHooks,
|
|
||||||
beforeNewThread,
|
|
||||||
};
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
const Eris = require('eris');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @callback BeforeNewThreadHook_SetCategoryId
|
||||||
|
* @param {String} categoryId
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef BeforeNewThreadHookData
|
||||||
|
* @property {Eris.User} user
|
||||||
|
* @property {CreateNewThreadForUserOpts} opts
|
||||||
|
* @property {Function} cancel
|
||||||
|
* @property {BeforeNewThreadHook_SetCategoryId} setCategoryId
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef BeforeNewThreadHookResult
|
||||||
|
* @property {boolean} cancelled
|
||||||
|
* @property {string|null} categoryId
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @callback BeforeNewThreadHookData
|
||||||
|
* @param {BeforeNewThreadHookData} data
|
||||||
|
* @return {void|Promise<void>}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type BeforeNewThreadHookData[]
|
||||||
|
*/
|
||||||
|
const beforeNewThreadHooks = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {BeforeNewThreadHookData} fn
|
||||||
|
*/
|
||||||
|
function beforeNewThread(fn) {
|
||||||
|
beforeNewThreadHooks.push(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{
|
||||||
|
* user: Eris.User,
|
||||||
|
* opts: CreateNewThreadForUserOpts,
|
||||||
|
* }} input
|
||||||
|
* @return {Promise<BeforeNewThreadHookResult>}
|
||||||
|
*/
|
||||||
|
async function callBeforeNewThreadHooks(input) {
|
||||||
|
/**
|
||||||
|
* @type {BeforeNewThreadHookResult}
|
||||||
|
*/
|
||||||
|
const result = {
|
||||||
|
cancelled: false,
|
||||||
|
categoryId: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {BeforeNewThreadHookData}
|
||||||
|
*/
|
||||||
|
const data = {
|
||||||
|
...input,
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
result.cancelled = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
setCategoryId(value) {
|
||||||
|
result.categoryId = value;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const hook of beforeNewThreadHooks) {
|
||||||
|
await hook(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
beforeNewThread,
|
||||||
|
callBeforeNewThreadHooks,
|
||||||
|
};
|
27
src/main.js
27
src/main.js
|
@ -8,7 +8,7 @@ const {messageQueue} = require('./queue');
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const { createCommandManager } = require('./commands');
|
const { createCommandManager } = require('./commands');
|
||||||
const { getPluginAPI, loadPlugin } = require('./plugins');
|
const { getPluginAPI, loadPlugin } = require('./plugins');
|
||||||
const { beforeNewThreadHooks } = require('./hooks');
|
const { callBeforeNewThreadHooks } = require('./hooks/beforeNewThread');
|
||||||
|
|
||||||
const blocked = require('./data/blocked');
|
const blocked = require('./data/blocked');
|
||||||
const threads = require('./data/threads');
|
const threads = require('./data/threads');
|
||||||
|
@ -132,28 +132,9 @@ function initBaseMessageHandlers() {
|
||||||
// Ignore messages that shouldn't usually open new threads, such as "ok", "thanks", etc.
|
// 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;
|
if (config.ignoreAccidentalThreads && msg.content && ACCIDENTAL_THREAD_MESSAGES.includes(msg.content.trim().toLowerCase())) return;
|
||||||
|
|
||||||
let cancelled = false;
|
thread = await threads.createNewThreadForUser(msg.author, {
|
||||||
let categoryId = null;
|
source: 'dm',
|
||||||
|
});
|
||||||
/**
|
|
||||||
* @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) {
|
if (thread) {
|
||||||
|
|
|
@ -15,7 +15,12 @@ module.exports = ({ bot, knex, config, commands }) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const createdThread = await threads.createNewThreadForUser(user, { quiet: true, ignoreRequirements: true });
|
const createdThread = await threads.createNewThreadForUser(user, {
|
||||||
|
quiet: true,
|
||||||
|
ignoreRequirements: true,
|
||||||
|
source: 'command',
|
||||||
|
});
|
||||||
|
|
||||||
createdThread.postSystemMessage(`Thread was opened by ${msg.author.username}#${msg.author.discriminator}`);
|
createdThread.postSystemMessage(`Thread was opened by ${msg.author.username}#${msg.author.discriminator}`);
|
||||||
|
|
||||||
msg.channel.createMessage(`Thread opened: <#${createdThread.channel_id}>`);
|
msg.channel.createMessage(`Thread opened: <#${createdThread.channel_id}>`);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const attachments = require('./data/attachments');
|
const attachments = require('./data/attachments');
|
||||||
const { beforeNewThread } = require('./hooks');
|
const { beforeNewThread } = require('./hooks/beforeNewThread');
|
||||||
const formats = require('./formatters');
|
const formats = require('./formatters');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
Loading…
Reference in New Issue