Add allowUserClose option. Fixes #69
parent
2f1b51d97a
commit
21d2a7453c
|
@ -52,6 +52,7 @@ const defaultConfig = {
|
||||||
"status": "Message me for help!",
|
"status": "Message me for help!",
|
||||||
"responseMessage": "Thank you for your message! Our mod team will reply to you here as soon as possible.",
|
"responseMessage": "Thank you for your message! Our mod team will reply to you here as soon as possible.",
|
||||||
"closeMessage": null,
|
"closeMessage": null,
|
||||||
|
"allowUserClose": false,
|
||||||
|
|
||||||
"newThreadCategoryId": null,
|
"newThreadCategoryId": null,
|
||||||
"mentionRole": "here",
|
"mentionRole": "here",
|
||||||
|
|
|
@ -204,7 +204,7 @@ class Thread {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Channel not found
|
// Channel not found
|
||||||
if (e.code === 10003) {
|
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);
|
this.close(true);
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -319,7 +319,7 @@ class Thread {
|
||||||
async close(silent = false) {
|
async close(silent = false) {
|
||||||
if (! silent) {
|
if (! silent) {
|
||||||
console.log(`Closing thread ${this.id}`);
|
console.log(`Closing thread ${this.id}`);
|
||||||
await this.postToThreadChannel('Closing thread...');
|
await this.postSystemMessage('Closing thread...');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update DB status
|
// Update DB status
|
||||||
|
|
|
@ -2,7 +2,7 @@ const Eris = require('eris');
|
||||||
|
|
||||||
const config = require('./config');
|
const config = require('./config');
|
||||||
const bot = require('./bot');
|
const bot = require('./bot');
|
||||||
const Queue = require('./queue');
|
const {messageQueue} = require('./queue');
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const blocked = require('./data/blocked');
|
const blocked = require('./data/blocked');
|
||||||
const threads = require('./data/threads');
|
const threads = require('./data/threads');
|
||||||
|
@ -25,8 +25,6 @@ const alert = require('./modules/alert');
|
||||||
const attachments = require("./data/attachments");
|
const attachments = require("./data/attachments");
|
||||||
const {ACCIDENTAL_THREAD_MESSAGES} = require('./data/constants');
|
const {ACCIDENTAL_THREAD_MESSAGES} = require('./data/constants');
|
||||||
|
|
||||||
const messageQueue = new Queue();
|
|
||||||
|
|
||||||
// Once the bot has connected, set the status/"playing" message
|
// Once the bot has connected, set the status/"playing" message
|
||||||
bot.on('ready', () => {
|
bot.on('ready', () => {
|
||||||
bot.editStatus(null, {name: config.status});
|
bot.editStatus(null, {name: config.status});
|
||||||
|
|
|
@ -2,12 +2,12 @@ const humanizeDuration = require('humanize-duration');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const Eris = require('eris');
|
const Eris = require('eris');
|
||||||
const config = require('../config');
|
const config = require('../config');
|
||||||
const threadUtils = require('../threadUtils');
|
const utils = require('../utils');
|
||||||
const utils = require("../utils");
|
const threads = require('../data/threads');
|
||||||
const threads = require("../data/threads");
|
const blocked = require('../data/blocked');
|
||||||
|
const {messageQueue} = require('../queue');
|
||||||
|
|
||||||
module.exports = bot => {
|
module.exports = bot => {
|
||||||
const addInboxServerCommand = (...args) => threadUtils.addInboxServerCommand(bot, ...args);
|
|
||||||
const humanizeDelay = (delay, opts = {}) => humanizeDuration(delay, Object.assign({conjunction: ' and '}, opts));
|
const humanizeDelay = (delay, opts = {}) => humanizeDuration(delay, Object.assign({conjunction: ' and '}, opts));
|
||||||
|
|
||||||
// Check for threads that are scheduled to be closed and close them
|
// Check for threads that are scheduled to be closed and close them
|
||||||
|
@ -38,42 +38,71 @@ module.exports = bot => {
|
||||||
scheduledCloseLoop();
|
scheduledCloseLoop();
|
||||||
|
|
||||||
// Close a thread. Closing a thread saves a log of the channel's contents and then deletes the channel.
|
// 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) => {
|
bot.registerCommand('close', async (msg, args) => {
|
||||||
if (! thread) return;
|
let thread, closedBy;
|
||||||
|
|
||||||
// Timed close
|
if (msg.channel instanceof Eris.PrivateChannel) {
|
||||||
if (args.length) {
|
// User is closing the thread by themselves (if enabled)
|
||||||
if (args[0].startsWith('c')) {
|
if (! config.allowUserClose) return;
|
||||||
// Cancel timed close
|
if (await blocked.isBlocked(msg.author.id)) return;
|
||||||
if (thread.scheduled_close_at) {
|
|
||||||
await thread.cancelScheduledClose();
|
thread = await threads.findOpenThreadByUserId(msg.author.id);
|
||||||
thread.postSystemMessage(`Cancelled scheduled closing`);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a timed close
|
// Regular close
|
||||||
const delay = utils.convertDelayStringToMS(args.join(' '));
|
await thread.close();
|
||||||
if (delay === 0 || delay === null) {
|
closedBy = msg.author.username;
|
||||||
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
|
if (config.closeMessage) {
|
||||||
if(config.closeMessage) await thread.postToUser(config.closeMessage).catch(() => {});
|
await thread.postToUser(config.closeMessage).catch(() => {});
|
||||||
await thread.close();
|
}
|
||||||
|
|
||||||
const logUrl = await thread.getLogUrl();
|
const logUrl = await thread.getLogUrl();
|
||||||
utils.postLog(utils.trimAll(`
|
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}
|
Logs: ${logUrl}
|
||||||
`));
|
`));
|
||||||
});
|
});
|
||||||
|
@ -82,11 +111,12 @@ module.exports = bot => {
|
||||||
bot.on('channelDelete', async (channel) => {
|
bot.on('channelDelete', async (channel) => {
|
||||||
if (! (channel instanceof Eris.TextChannel)) return;
|
if (! (channel instanceof Eris.TextChannel)) return;
|
||||||
if (channel.guild.id !== utils.getInboxGuild().id) return;
|
if (channel.guild.id !== utils.getInboxGuild().id) return;
|
||||||
|
|
||||||
const thread = await threads.findOpenThreadByChannelId(channel.id);
|
const thread = await threads.findOpenThreadByChannelId(channel.id);
|
||||||
if (! thread) return;
|
if (! thread) return;
|
||||||
|
|
||||||
console.log(`[INFO] Auto-closing thread with ${thread.user_name} because the channel was deleted`);
|
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);
|
await thread.close(true);
|
||||||
|
|
||||||
const logUrl = await thread.getLogUrl();
|
const logUrl = await thread.getLogUrl();
|
||||||
|
|
19
src/queue.js
19
src/queue.js
|
@ -5,8 +5,16 @@ class Queue {
|
||||||
}
|
}
|
||||||
|
|
||||||
add(fn) {
|
add(fn) {
|
||||||
this.queue.push(fn);
|
const promise = new Promise(resolve => {
|
||||||
if (! this.running) this.next();
|
this.queue.push(async () => {
|
||||||
|
await Promise.resolve(fn());
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (! this.running) this.next();
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
next() {
|
next() {
|
||||||
|
@ -20,10 +28,13 @@ class Queue {
|
||||||
const fn = this.queue.shift();
|
const fn = this.queue.shift();
|
||||||
new Promise(resolve => {
|
new Promise(resolve => {
|
||||||
// Either fn() completes or the timeout of 10sec is reached
|
// Either fn() completes or the timeout of 10sec is reached
|
||||||
Promise.resolve(fn()).then(resolve);
|
fn().then(resolve);
|
||||||
setTimeout(resolve, 10000);
|
setTimeout(resolve, 10000);
|
||||||
}).then(() => this.next());
|
}).then(() => this.next());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Queue;
|
module.exports = {
|
||||||
|
Queue,
|
||||||
|
messageQueue: new Queue()
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue