aboutsummaryrefslogtreecommitdiff
path: root/src/modules/close.js
diff options
context:
space:
mode:
authorSin-MacBook <[email protected]>2020-08-10 23:44:20 +0200
committerSin-MacBook <[email protected]>2020-08-10 23:44:20 +0200
commit2a53887abba882bf7b63aeda8dfa55fdb3ab8792 (patch)
treead7a95eb41faa6ff13c3142285cdc0eb3ca92183 /src/modules/close.js
downloadmodmail-2a53887abba882bf7b63aeda8dfa55fdb3ab8792.tar.xz
modmail-2a53887abba882bf7b63aeda8dfa55fdb3ab8792.zip
clean this up when home
Diffstat (limited to 'src/modules/close.js')
-rw-r--r--src/modules/close.js153
1 files changed, 153 insertions, 0 deletions
diff --git a/src/modules/close.js b/src/modules/close.js
new file mode 100644
index 0000000..b3e7421
--- /dev/null
+++ b/src/modules/close.js
@@ -0,0 +1,153 @@
+const moment = require('moment');
+const Eris = require('eris');
+const config = require('../config');
+const utils = require('../utils');
+const threads = require('../data/threads');
+const blocked = require('../data/blocked');
+const {messageQueue} = require('../queue');
+
+module.exports = ({ bot, knex, config, commands }) => {
+ // Check for threads that are scheduled to be closed and close them
+ async function applyScheduledCloses() {
+ const threadsToBeClosed = await threads.getThreadsThatShouldBeClosed();
+ for (const thread of threadsToBeClosed) {
+ if (config.closeMessage && ! thread.scheduled_close_silent) {
+ const closeMessage = utils.readMultilineConfigValue(config.closeMessage);
+ await thread.postToUser(closeMessage).catch(() => {});
+ }
+
+ await thread.close(false, thread.scheduled_close_silent);
+
+ const logUrl = await thread.getLogUrl();
+ utils.postLog(utils.trimAll(`
+ Modmail thread with ${thread.user_name} (${thread.user_id}) was closed as scheduled by ${thread.scheduled_close_name}
+ Logs: ${logUrl}
+ `));
+ }
+ }
+
+ async function scheduledCloseLoop() {
+ try {
+ await applyScheduledCloses();
+ } catch (e) {
+ console.error(e);
+ }
+
+ setTimeout(scheduledCloseLoop, 2000);
+ }
+
+ scheduledCloseLoop();
+
+ // Close a thread. Closing a thread saves a log of the channel's contents and then deletes the channel.
+ commands.addGlobalCommand('close', '[opts...]', async (msg, args) => {
+ let thread, closedBy;
+
+ let hasCloseMessage = !! config.closeMessage;
+ let silentClose = false;
+
+ if (msg.channel instanceof Eris.PrivateChannel) {
+ // User is closing the thread by themselves (if enabled)
+ if (! config.allowUserClose) return;
+ if (await blocked.isBlocked(msg.author.id)) return;
+
+ thread = await threads.findOpenThreadByUserId(msg.author.id);
+ if (! thread) return;
+
+ // We need to add this operation to the message queue so we don't get a race condition
+ // between showing the close command in the thread and closing the thread
+ await messageQueue.add(async () => {
+ thread.postSystemMessage('Thread closed by user, closing...');
+ await thread.close(true);
+ });
+
+ closedBy = 'the user';
+ } else {
+ // A staff member is closing the thread
+ if (! utils.messageIsOnInboxServer(msg)) return;
+ if (! utils.isStaff(msg.member)) return;
+
+ thread = await threads.findOpenThreadByChannelId(msg.channel.id);
+ if (! thread) return;
+
+ if (args.opts && args.opts.length) {
+ if (args.opts.includes('cancel') || args.opts.includes('c')) {
+ // Cancel timed close
+ if (thread.scheduled_close_at) {
+ await thread.cancelScheduledClose();
+ thread.postSystemMessage(`Cancelled scheduled closing`);
+ }
+
+ return;
+ }
+
+ // Silent close (= no close message)
+ if (args.opts.includes('silent') || args.opts.includes('s')) {
+ silentClose = true;
+ }
+
+ // Timed close
+ const delayStringArg = args.opts.find(arg => utils.delayStringRegex.test(arg));
+ if (delayStringArg) {
+ const delay = utils.convertDelayStringToMS(delayStringArg);
+ if (delay === 0 || delay === null) {
+ thread.postSystemMessage(`Invalid delay specified. Format: "1h30m"`);
+ return;
+ }
+
+ const closeAt = moment.utc().add(delay, 'ms');
+ await thread.scheduleClose(closeAt.format('YYYY-MM-DD HH:mm:ss'), msg.author, silentClose ? 1 : 0);
+
+ let response;
+ if (silentClose) {
+ response = `Thread is now scheduled to be closed silently in ${utils.humanizeDelay(delay)}. Use \`${config.prefix}close cancel\` to cancel.`;
+ } else {
+ response = `Thread is now scheduled to be closed in ${utils.humanizeDelay(delay)}. Use \`${config.prefix}close cancel\` to cancel.`;
+ }
+
+ thread.postSystemMessage(response);
+
+ return;
+ }
+ }
+
+ // Regular close
+ await thread.close(false, silentClose);
+ closedBy = msg.author.username;
+ }
+
+ // Send close message (unless suppressed with a silent close)
+ if (hasCloseMessage && ! silentClose) {
+ const closeMessage = utils.readMultilineConfigValue(config.closeMessage);
+ await thread.postToUser(closeMessage).catch(() => {});
+ }
+
+ const logUrl = await thread.getLogUrl();
+ utils.postLog(utils.trimAll(`
+ Modmail thread with ${thread.user_name} (${thread.user_id}) was closed by ${closedBy}
+ Logs: ${logUrl}
+ `));
+ });
+
+ // Auto-close threads if their channel is deleted
+ bot.on('channelDelete', async (channel) => {
+ if (! (channel instanceof Eris.TextChannel)) return;
+ if (channel.guild.id !== utils.getInboxGuild().id) return;
+
+ const thread = await threads.findOpenThreadByChannelId(channel.id);
+ if (! thread) return;
+
+ console.log(`[INFO] Auto-closing thread with ${thread.user_name} because the channel was deleted`);
+ if (config.closeMessage) {
+ const closeMessage = utils.readMultilineConfigValue(config.closeMessage);
+ await thread.postToUser(closeMessage).catch(() => {});
+ }
+
+ await thread.close(true);
+
+ const logUrl = await thread.getLogUrl();
+ utils.postLog(utils.trimAll(`
+ Modmail thread with ${thread.user_name} (${thread.user_id}) was closed automatically because the channel was deleted
+ Logs: ${logUrl}
+ `));
+ });
+};