Add allowUserClose option. Fixes #69

master
Dragory 2018-09-20 23:31:14 +03:00
parent 2f1b51d97a
commit 21d2a7453c
5 changed files with 79 additions and 39 deletions

View File

@ -52,6 +52,7 @@ const defaultConfig = {
"status": "Message me for help!",
"responseMessage": "Thank you for your message! Our mod team will reply to you here as soon as possible.",
"closeMessage": null,
"allowUserClose": false,
"newThreadCategoryId": null,
"mentionRole": "here",

View File

@ -204,7 +204,7 @@ class Thread {
} catch (e) {
// Channel not found
if (e.code === 10003) {
console.log(`[INFO] Auto-closing thread with ${this.user_name} because the channel no longer exists`);
console.log(`[INFO] Failed to send message to thread channel for ${this.user_name} because the channel no longer exists. Auto-closing the thread.`);
this.close(true);
} else {
throw e;
@ -319,7 +319,7 @@ class Thread {
async close(silent = false) {
if (! silent) {
console.log(`Closing thread ${this.id}`);
await this.postToThreadChannel('Closing thread...');
await this.postSystemMessage('Closing thread...');
}
// Update DB status

View File

@ -2,7 +2,7 @@ const Eris = require('eris');
const config = require('./config');
const bot = require('./bot');
const Queue = require('./queue');
const {messageQueue} = require('./queue');
const utils = require('./utils');
const blocked = require('./data/blocked');
const threads = require('./data/threads');
@ -25,8 +25,6 @@ const alert = require('./modules/alert');
const attachments = require("./data/attachments");
const {ACCIDENTAL_THREAD_MESSAGES} = require('./data/constants');
const messageQueue = new Queue();
// Once the bot has connected, set the status/"playing" message
bot.on('ready', () => {
bot.editStatus(null, {name: config.status});

View File

@ -2,12 +2,12 @@ const humanizeDuration = require('humanize-duration');
const moment = require('moment');
const Eris = require('eris');
const config = require('../config');
const threadUtils = require('../threadUtils');
const utils = require("../utils");
const threads = require("../data/threads");
const utils = require('../utils');
const threads = require('../data/threads');
const blocked = require('../data/blocked');
const {messageQueue} = require('../queue');
module.exports = bot => {
const addInboxServerCommand = (...args) => threadUtils.addInboxServerCommand(bot, ...args);
const humanizeDelay = (delay, opts = {}) => humanizeDuration(delay, Object.assign({conjunction: ' and '}, opts));
// Check for threads that are scheduled to be closed and close them
@ -38,42 +38,71 @@ module.exports = bot => {
scheduledCloseLoop();
// Close a thread. Closing a thread saves a log of the channel's contents and then deletes the channel.
addInboxServerCommand('close', async (msg, args, thread) => {
if (! thread) return;
bot.registerCommand('close', async (msg, args) => {
let thread, closedBy;
// Timed close
if (args.length) {
if (args[0].startsWith('c')) {
// Cancel timed close
if (thread.scheduled_close_at) {
await thread.cancelScheduledClose();
thread.postSystemMessage(`Cancelled scheduled closing`);
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;
// Timed close
if (args.length) {
if (args[0].startsWith('c')) {
// Cancel timed close
if (thread.scheduled_close_at) {
await thread.cancelScheduledClose();
thread.postSystemMessage(`Cancelled scheduled closing`);
}
return;
}
// Set a timed close
const delay = utils.convertDelayStringToMS(args.join(' '));
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);
thread.postSystemMessage(`Thread is now scheduled to be closed in ${humanizeDelay(delay)}. Use \`${config.prefix}close cancel\` to cancel.`);
return;
}
// Set a timed close
const delay = utils.convertDelayStringToMS(args.join(' '));
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);
thread.postSystemMessage(`Thread is now scheduled to be closed in ${humanizeDelay(delay)}. Use \`${config.prefix}close cancel\` to cancel.`);
return;
// Regular close
await thread.close();
closedBy = msg.author.username;
}
// Regular close
if(config.closeMessage) await thread.postToUser(config.closeMessage).catch(() => {});
await thread.close();
if (config.closeMessage) {
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 ${msg.author.username}
Modmail thread with ${thread.user_name} (${thread.user_id}) was closed by ${closedBy}
Logs: ${logUrl}
`));
});
@ -82,11 +111,12 @@ module.exports = bot => {
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(() => {});
if (config.closeMessage) await thread.postToUser(config.closeMessage).catch(() => {});
await thread.close(true);
const logUrl = await thread.getLogUrl();

View File

@ -5,8 +5,16 @@ class Queue {
}
add(fn) {
this.queue.push(fn);
if (! this.running) this.next();
const promise = new Promise(resolve => {
this.queue.push(async () => {
await Promise.resolve(fn());
resolve();
});
if (! this.running) this.next();
});
return promise;
}
next() {
@ -20,10 +28,13 @@ class Queue {
const fn = this.queue.shift();
new Promise(resolve => {
// Either fn() completes or the timeout of 10sec is reached
Promise.resolve(fn()).then(resolve);
fn().then(resolve);
setTimeout(resolve, 10000);
}).then(() => this.next());
}
}
module.exports = Queue;
module.exports = {
Queue,
messageQueue: new Queue()
};