ramirez/src/modules/close.js

152 lines
5.2 KiB
JavaScript

const humanizeDuration = require('humanize-duration');
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 => {
const humanizeDelay = (delay, opts = {}) => humanizeDuration(delay, Object.assign({conjunction: ' and '}, opts));
// 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) {
await thread.postToUser(config.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.
bot.registerCommand('close', 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.length) {
if (args.includes('cancel') || args.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.includes('silent') || args.includes('s')) {
silentClose = true;
}
// Timed close
const delayStringArg = args.find(arg => utils.delayStringRegex.test(arg));
if (delayStringArg) {
// Set a timed close
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 ${humanizeDelay(delay)}. Use \`${config.prefix}close cancel\` to cancel.`;
} else {
response = `Thread is now scheduled to be closed in ${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) {
await thread.postToUser(config.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) await thread.postToUser(config.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}
`));
});
};