Code refactoring and clean-up
parent
ddff708ff5
commit
b934f8eb25
|
@ -1,5 +1,10 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Sep 19, 2017
|
||||||
|
* Added `logChannelId` option
|
||||||
|
* Some code clean-up. Please open an issue if you encounter any bugs!
|
||||||
|
* The bot now throws an error for unknown options in `config.json` (assuming they're typos) and tells you if you haven't configured the token or mail guild id.
|
||||||
|
|
||||||
## Aug 3, 2017
|
## Aug 3, 2017
|
||||||
* Fixed user nicknames not showing in new threads
|
* Fixed user nicknames not showing in new threads
|
||||||
* The "manageRoles" permission is no longer required to use commands on the inbox server.
|
* The "manageRoles" permission is no longer required to use commands on the inbox server.
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node src/index.js",
|
"start": "node --trace-warnings src/index.js",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"lint": "./node_modules/.bin/eslint ./src"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
const Eris = require('eris');
|
const Eris = require('eris');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const https = require('https');
|
const https = require('https');
|
||||||
const config = require('../config');
|
const config = require('./config');
|
||||||
const utils = require('./utils');
|
|
||||||
|
const getUtils = () => require('./utils');
|
||||||
|
|
||||||
const attachmentDir = config.attachmentDir || `${__dirname}/../attachments`;
|
const attachmentDir = config.attachmentDir || `${__dirname}/../attachments`;
|
||||||
|
|
||||||
|
@ -64,7 +65,7 @@ function saveAttachmentsInMessage(msg) {
|
||||||
*/
|
*/
|
||||||
function getUrl(attachmentId, desiredName = null) {
|
function getUrl(attachmentId, desiredName = null) {
|
||||||
if (desiredName == null) desiredName = 'file.bin';
|
if (desiredName == null) desiredName = 'file.bin';
|
||||||
return utils.getSelfUrl(`attachments/${attachmentId}/${desiredName}`);
|
return getUtils().getSelfUrl(`attachments/${attachmentId}/${desiredName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
const Eris = require('eris');
|
||||||
|
const config = require('./config');
|
||||||
|
|
||||||
|
const bot = new Eris.CommandClient(config.token, {}, {
|
||||||
|
prefix: config.prefix,
|
||||||
|
ignoreSelf: true,
|
||||||
|
ignoreBots: true,
|
||||||
|
defaultHelpCommand: false,
|
||||||
|
getAllUsers: true,
|
||||||
|
defaultCommandOptions: {
|
||||||
|
caseInsensitive: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = bot;
|
|
@ -0,0 +1,49 @@
|
||||||
|
let userConfig;
|
||||||
|
|
||||||
|
try {
|
||||||
|
userConfig = require('../config');
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Config file could not be found or read! The error given was: ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultConfig = {
|
||||||
|
"token": null,
|
||||||
|
"mailGuildId": null,
|
||||||
|
"mainGuildId": null,
|
||||||
|
"logChannelId": null,
|
||||||
|
|
||||||
|
"prefix": "!",
|
||||||
|
"snippetPrefix": "!!",
|
||||||
|
|
||||||
|
"status": "Message me for help!",
|
||||||
|
"responseMessage": "Thank you for your message! Our mod team will reply to you here as soon as possible.",
|
||||||
|
|
||||||
|
"inboxServerPermission": null,
|
||||||
|
"alwaysReply": false,
|
||||||
|
"alwaysReplyAnon": false,
|
||||||
|
"useNicknames": false,
|
||||||
|
"ignoreAccidentalThreads": false,
|
||||||
|
|
||||||
|
"enableGreeting": false,
|
||||||
|
"greetingMessage": "The message the bot sends to a new user",
|
||||||
|
"greetingAttachment": "Put a file path here",
|
||||||
|
|
||||||
|
"port": 8890,
|
||||||
|
"url": null
|
||||||
|
};
|
||||||
|
|
||||||
|
const finalConfig = Object.assign({}, defaultConfig);
|
||||||
|
|
||||||
|
for (const [prop, value] of Object.entries(userConfig)) {
|
||||||
|
if (! defaultConfig.hasOwnProperty(prop)) {
|
||||||
|
throw new Error(`Invalid option: ${prop}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
finalConfig[prop] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! finalConfig.token) throw new Error('Missing token!');
|
||||||
|
if (! finalConfig.mailGuildId) throw new Error('Missing mailGuildId (inbox server id)!');
|
||||||
|
if (! finalConfig.mainGuildId) throw new Error('Missing mainGuildId!');
|
||||||
|
|
||||||
|
module.exports = finalConfig;
|
|
@ -1,6 +1,6 @@
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const config = require('../config');
|
const config = require('./config');
|
||||||
|
|
||||||
const greetingGuildId = config.mainGuildId || config.greetingGuildId;
|
const greetingGuildId = config.mainGuildId || config.greetingGuildId;
|
||||||
|
|
||||||
|
|
397
src/index.js
397
src/index.js
|
@ -1,8 +1,10 @@
|
||||||
const Eris = require('eris');
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const Eris = require('eris');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const humanizeDuration = require('humanize-duration');
|
const humanizeDuration = require('humanize-duration');
|
||||||
const config = require('../config');
|
|
||||||
|
const config = require('./config');
|
||||||
|
const bot = require('./bot');
|
||||||
const Queue = require('./queue');
|
const Queue = require('./queue');
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const blocked = require('./blocked');
|
const blocked = require('./blocked');
|
||||||
|
@ -13,132 +15,98 @@ const snippets = require('./snippets');
|
||||||
const webserver = require('./webserver');
|
const webserver = require('./webserver');
|
||||||
const greeting = require('./greeting');
|
const greeting = require('./greeting');
|
||||||
|
|
||||||
const prefix = config.prefix || '!';
|
|
||||||
const snippetPrefix = config.snippetPrefix || prefix.repeat(2);
|
|
||||||
|
|
||||||
const bot = new Eris.CommandClient(config.token, {}, {
|
|
||||||
prefix: prefix,
|
|
||||||
ignoreSelf: true,
|
|
||||||
ignoreBots: true,
|
|
||||||
defaultHelpCommand: false,
|
|
||||||
getAllUsers: true,
|
|
||||||
defaultCommandOptions: {
|
|
||||||
caseInsensitive: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const messageQueue = new Queue();
|
const messageQueue = new Queue();
|
||||||
|
|
||||||
|
// Force crash on unhandled rejections (use something like forever/pm2 to restart)
|
||||||
|
process.on('unhandledRejection', err => {
|
||||||
|
if (err instanceof utils.BotError) {
|
||||||
|
// BotErrors don't need a stack trace
|
||||||
|
console.error(`Error: ${err.message}`);
|
||||||
|
} else {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Once the bot has connected, set the status/"playing" message
|
||||||
bot.on('ready', () => {
|
bot.on('ready', () => {
|
||||||
bot.editStatus(null, {name: config.status || 'Message me for help'});
|
bot.editStatus(null, {name: config.status || 'Message me for help'});
|
||||||
console.log('Bot started, listening to DMs');
|
console.log('Bot started, listening to DMs');
|
||||||
});
|
});
|
||||||
|
|
||||||
function isStaff(member) {
|
// If the alwaysReply option is set to true, send all messages in modmail threads as replies, unless they start with a command prefix
|
||||||
if (! config.inboxServerPermission) return true;
|
|
||||||
return member.permission.has(config.inboxServerPermission);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatAttachment(attachment) {
|
|
||||||
let filesize = attachment.size || 0;
|
|
||||||
filesize /= 1024;
|
|
||||||
|
|
||||||
return attachments.getUrl(attachment.id, attachment.filename).then(attachmentUrl => {
|
|
||||||
return `**Attachment:** ${attachment.filename} (${filesize.toFixed(1)}KB)\n${attachmentUrl}`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the alwaysReply option is set to true, send all messages in modmail threads as replies, unless they start with the prefix
|
|
||||||
if (config.alwaysReply) {
|
if (config.alwaysReply) {
|
||||||
bot.on('messageCreate', msg => {
|
bot.on('messageCreate', msg => {
|
||||||
if (! msg.channel.guild) return;
|
if (! utils.messageIsOnInboxServer(msg)) return;
|
||||||
if (msg.channel.guild.id !== utils.getInboxGuild(bot).id) return;
|
if (! utils.isStaff(msg)) return;
|
||||||
if (! isStaff(msg.member)) return;
|
|
||||||
if (msg.author.bot) return;
|
if (msg.author.bot) return;
|
||||||
if (msg.content[0] == bot.commandOptions.prefix) return;
|
if (msg.content.startsWith(config.prefix) || msg.content.startsWith(config.snippetPrefix)) return;
|
||||||
|
|
||||||
reply(msg, msg.content.trim(), config.alwaysReplyAnon || false);
|
reply(msg, msg.content.trim(), config.alwaysReplyAnon || false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Bot was mentioned in #general-discussion"
|
// "Bot was mentioned in #general-discussion"
|
||||||
bot.on('messageCreate', msg => {
|
bot.on('messageCreate', async msg => {
|
||||||
if (msg.author.id === bot.user.id) return;
|
if (! utils.messageIsOnMainServer(msg)) return;
|
||||||
|
if (! msg.mentions.some(user => user.id === bot.user.id)) return;
|
||||||
|
|
||||||
if (msg.mentions.some(user => user.id === bot.user.id)) {
|
// If the person who mentioned the modmail bot is also on the modmail server, ignore them
|
||||||
// If the person who mentioned the modmail bot is on the modmail server, don't ping about it
|
if (utils.getInboxGuild().members.get(msg.author.id)) return;
|
||||||
if (utils.getInboxGuild(bot).members.get(msg.author.id)) return;
|
|
||||||
|
|
||||||
blocked.isBlocked(msg.author.id).then(isBlocked => {
|
// If the person who mentioned the bot is blocked, ignore them
|
||||||
if (isBlocked) return;
|
if (await blocked.isBlocked(msg.author.id)) return;
|
||||||
|
|
||||||
bot.createMessage(utils.getLogChannel(bot).id, {
|
bot.createMessage(utils.getLogChannel(bot).id, {
|
||||||
content: `@here Bot mentioned in ${msg.channel.mention} by **${msg.author.username}#${msg.author.discriminator}**: "${msg.cleanContent}"`,
|
content: `@here Bot mentioned in ${msg.channel.mention} by **${msg.author.username}#${msg.author.discriminator}**: "${msg.cleanContent}"`,
|
||||||
disableEveryone: false,
|
disableEveryone: false,
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// When we get a private message, forward the contents to the corresponding modmail thread
|
// When we get a private message, forward the contents to the corresponding modmail thread
|
||||||
bot.on('messageCreate', (msg) => {
|
bot.on('messageCreate', async msg => {
|
||||||
if (! (msg.channel instanceof Eris.PrivateChannel)) return;
|
if (! (msg.channel instanceof Eris.PrivateChannel)) return;
|
||||||
if (msg.author.id === bot.user.id) return;
|
if (msg.author.id === bot.user.id) return;
|
||||||
|
|
||||||
blocked.isBlocked(msg.author.id).then(isBlocked => {
|
if (await blocked.isBlocked(msg.author.id)) return;
|
||||||
if (isBlocked) return;
|
|
||||||
|
|
||||||
// Download and save copies of attachments in the background
|
// Download and save copies of attachments in the background
|
||||||
const attachmentSavePromise = attachments.saveAttachmentsInMessage(msg);
|
const attachmentSavePromise = attachments.saveAttachmentsInMessage(msg);
|
||||||
|
|
||||||
let thread, userLogs;
|
|
||||||
let threadCreationFailed = false;
|
let threadCreationFailed = false;
|
||||||
|
|
||||||
// Private message handling is queued so e.g. multiple message in quick succession don't result in multiple channels being created
|
// Private message handling is queued so e.g. multiple message in quick succession don't result in multiple channels being created
|
||||||
messageQueue.add(() => {
|
messageQueue.add(async () => {
|
||||||
return threads.getForUser(bot, msg.author, true, msg)
|
let thread;
|
||||||
.then(userThread => {
|
|
||||||
thread = userThread;
|
|
||||||
return logs.getLogsByUserId(msg.author.id);
|
|
||||||
}, err => {
|
|
||||||
console.log(`[ERROR] Modmail channel for ${msg.author.username}#${msg.author.discriminator} could not be created:\n${err.message}`);
|
|
||||||
threadCreationFailed = true;
|
|
||||||
})
|
|
||||||
.then(foundUserLogs => {
|
|
||||||
userLogs = foundUserLogs;
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
let content = msg.content;
|
|
||||||
|
|
||||||
if (threadCreationFailed) {
|
// Find the corresponding modmail thread
|
||||||
// If the thread could not be created, send a warning about this to all mods so they can DM the user directly instead
|
try {
|
||||||
let warningMessage = `
|
thread = await threads.getForUser(msg.author, true, msg);
|
||||||
@here Error creating modmail thread for ${msg.author.username}#${msg.author.discriminator} (${msg.author.id})!
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
utils.postError(`
|
||||||
|
Modmail thread for ${msg.author.username}#${msg.author.discriminator} (${msg.author.id}) could not be created:
|
||||||
|
\`\`\`${e.message}\`\`\`
|
||||||
|
|
||||||
Here's what their message contained:
|
Here's what their message contained:
|
||||||
\`\`\`${content}\`\`\`
|
\`\`\`${msg.cleanContent}\`\`\``);
|
||||||
`.trim();
|
|
||||||
|
|
||||||
bot.createMessage(utils.getLogChannel(bot).id, {
|
|
||||||
content: warningMessage,
|
|
||||||
disableEveryone: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
} else if (! thread) {
|
|
||||||
// No thread but creation didn't fail either -> probably ignored
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let threadInitDonePromise = Promise.resolve();
|
if (! thread) {
|
||||||
|
utils.postError(`
|
||||||
|
Modmail thread for ${msg.author.username}#${msg.author.discriminator} (${msg.author.id}) was not found.
|
||||||
|
|
||||||
|
Here's what their message contained:
|
||||||
|
\`\`\`${msg.cleanContent}\`\`\`
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
// If the thread was just created, do some extra stuff
|
|
||||||
if (thread._wasCreated) {
|
if (thread._wasCreated) {
|
||||||
const member = utils.getMainGuild(bot).members.get(msg.author.id);
|
const mainGuild = utils.getMainGuild();
|
||||||
|
const member = (mainGuild ? mainGuild.members.get(msg.author.id) : null);
|
||||||
if (! member) {
|
if (! member) console.log(`[INFO] Member ${msg.author.id} not found in main guild ${config.mainGuildId}`);
|
||||||
console.log(`Member ${msg.author.id} not found in main guild ${config.mainGuildId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mainGuildNickname = null;
|
let mainGuildNickname = null;
|
||||||
if (member && member.nick) mainGuildNickname = member.nick;
|
if (member && member.nick) mainGuildNickname = member.nick;
|
||||||
|
@ -147,85 +115,79 @@ Here's what their message contained:
|
||||||
|
|
||||||
if (mainGuildNickname == null) mainGuildNickname = 'UNKNOWN';
|
if (mainGuildNickname == null) mainGuildNickname = 'UNKNOWN';
|
||||||
|
|
||||||
|
const userLogs = await logs.getLogsByUserId(msg.author.id);
|
||||||
const accountAge = humanizeDuration(Date.now() - msg.author.createdAt, {largest: 2});
|
const accountAge = humanizeDuration(Date.now() - msg.author.createdAt, {largest: 2});
|
||||||
const infoHeader = `ACCOUNT AGE **${accountAge}**, ID **${msg.author.id}**, NICKNAME **${mainGuildNickname}**, LOGS **${userLogs.length}**\n-------------------------------`;
|
const infoHeader = `ACCOUNT AGE **${accountAge}**, ID **${msg.author.id}**, NICKNAME **${mainGuildNickname}**, LOGS **${userLogs.length}**\n-------------------------------`;
|
||||||
|
|
||||||
threadInitDonePromise = bot.createMessage(thread.channelId, infoHeader).then(() => {
|
await bot.createMessage(thread.channelId, infoHeader);
|
||||||
|
|
||||||
// Ping mods of the new thread
|
// Ping mods of the new thread
|
||||||
return bot.createMessage(thread.channelId, {
|
await bot.createMessage(thread.channelId, {
|
||||||
content: `@here New modmail thread (${msg.author.username}#${msg.author.discriminator})`,
|
content: `@here New modmail thread (${msg.author.username}#${msg.author.discriminator})`,
|
||||||
disableEveryone: false,
|
disableEveryone: false,
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// Send an automatic reply to the user informing them of the successfully created modmail thread
|
// Send an automatic reply to the user informing them of the successfully created modmail thread
|
||||||
msg.channel.createMessage(config.responseMessage || "Thank you for your message! Our mod team will reply to you here as soon as possible.").then(null, (err) => {
|
msg.channel.createMessage(config.responseMessage).catch(err => {
|
||||||
bot.createMessage(utils.getLogChannel(bot).id, {
|
utils.postError(`There is an issue sending messages to ${msg.author.username}#${msg.author.discriminator} (${msg.author.id}); consider messaging manually`);
|
||||||
content: `There is an issue sending messages to ${msg.author.username}#${msg.author.discriminator} (id ${msg.author.id}); consider messaging manually`
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const timestamp = utils.getTimestamp();
|
||||||
const attachmentsPendingStr = '\n\n*Attachments pending...*';
|
const attachmentsPendingStr = '\n\n*Attachments pending...*';
|
||||||
|
|
||||||
|
let content = msg.content;
|
||||||
if (msg.attachments.length > 0) content += attachmentsPendingStr;
|
if (msg.attachments.length > 0) content += attachmentsPendingStr;
|
||||||
|
|
||||||
threadInitDonePromise.then(() => {
|
const createdMsg = await bot.createMessage(thread.channelId, `[${timestamp}] « **${msg.author.username}#${msg.author.discriminator}:** ${content}`);
|
||||||
const timestamp = utils.getTimestamp();
|
|
||||||
bot.createMessage(thread.channelId, `[${timestamp}] « **${msg.author.username}#${msg.author.discriminator}:** ${content}`).then(createdMsg => {
|
|
||||||
if (msg.attachments.length === 0) return;
|
|
||||||
|
|
||||||
// Once attachments have been saved, add links to them to the message
|
|
||||||
attachmentSavePromise.then(() => {
|
|
||||||
const attachmentFormatPromises = msg.attachments.map(formatAttachment);
|
|
||||||
Promise.all(attachmentFormatPromises).then(formattedAttachments => {
|
|
||||||
let attachmentMsg = '';
|
|
||||||
|
|
||||||
formattedAttachments.forEach(str => {
|
|
||||||
attachmentMsg += `\n\n${str}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
if (msg.attachments.length > 0) {
|
||||||
|
await attachmentSavePromise;
|
||||||
|
const formattedAttachments = await Promise.all(msg.attachments.map(utils.formatAttachment));
|
||||||
|
const attachmentMsg = `\n\n` + formattedAttachments.reduce((str, formatted) => str + `\n\n${formatted}`);
|
||||||
createdMsg.edit(createdMsg.content.replace(attachmentsPendingStr, attachmentMsg));
|
createdMsg.edit(createdMsg.content.replace(attachmentsPendingStr, attachmentMsg));
|
||||||
});
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Edits in DMs
|
// Edits in DMs
|
||||||
bot.on('messageUpdate', (msg, oldMessage) => {
|
bot.on('messageUpdate', async (msg, oldMessage) => {
|
||||||
if (! (msg.channel instanceof Eris.PrivateChannel)) return;
|
if (! (msg.channel instanceof Eris.PrivateChannel)) return;
|
||||||
if (msg.author.id === bot.user.id) return;
|
if (msg.author.id === bot.user.id) return;
|
||||||
|
|
||||||
blocked.isBlocked(msg.author.id).then(isBlocked => {
|
if (await blocked.isBlocked(msg.author.id)) return;
|
||||||
if (isBlocked) return;
|
|
||||||
|
|
||||||
let oldContent = oldMessage.content;
|
let oldContent = oldMessage.content;
|
||||||
const newContent = msg.content;
|
const newContent = msg.content;
|
||||||
|
|
||||||
|
// Old message content doesn't persist between bot restarts
|
||||||
if (oldContent == null) oldContent = '*Unavailable due to bot restart*';
|
if (oldContent == null) oldContent = '*Unavailable due to bot restart*';
|
||||||
|
|
||||||
// Ignore bogus edit events with no changes
|
// Ignore bogus edit events with no changes
|
||||||
if (newContent.trim() === oldContent.trim()) return;
|
if (newContent.trim() === oldContent.trim()) return;
|
||||||
|
|
||||||
threads.getForUser(bot, msg.author).then(thread => {
|
const thread = await threads.getForUser(msg.author);
|
||||||
if (! thread) return;
|
if (! thread) return;
|
||||||
|
|
||||||
const editMessage = utils.disableLinkPreviews(`**The user edited their message:**\n\`B:\` ${oldContent}\n\`A:\` ${newContent}`);
|
const editMessage = utils.disableLinkPreviews(`**The user edited their message:**\n\`B:\` ${oldContent}\n\`A:\` ${newContent}`);
|
||||||
|
|
||||||
bot.createMessage(thread.channelId, editMessage);
|
bot.createMessage(thread.channelId, editMessage);
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function reply(msg, text, anonymous = false) {
|
/**
|
||||||
threads.getByChannelId(msg.channel.id).then(thread => {
|
* Sends a reply to the modmail thread where `msg` was posted.
|
||||||
|
* @param {Eris.Message} msg
|
||||||
|
* @param {string} text
|
||||||
|
* @param {bool} anonymous
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async function reply(msg, text, anonymous = false) {
|
||||||
|
const thread = await threads.getByChannelId(msg.channel.id);
|
||||||
if (! thread) return;
|
if (! thread) return;
|
||||||
|
|
||||||
attachments.saveAttachmentsInMessage(msg).then(() => {
|
await attachments.saveAttachmentsInMessage(msg);
|
||||||
bot.getDMChannel(thread.userId).then(dmChannel => {
|
|
||||||
|
const dmChannel = await bot.getDMChannel(thread.userId);
|
||||||
|
|
||||||
let modUsername, logModUsername;
|
let modUsername, logModUsername;
|
||||||
const mainRole = utils.getMainRole(msg.member);
|
const mainRole = utils.getMainRole(msg.member);
|
||||||
|
|
||||||
|
@ -241,53 +203,46 @@ function reply(msg, text, anonymous = false) {
|
||||||
let content = `**${modUsername}:** ${text}`;
|
let content = `**${modUsername}:** ${text}`;
|
||||||
let logContent = `**${logModUsername}:** ${text}`;
|
let logContent = `**${logModUsername}:** ${text}`;
|
||||||
|
|
||||||
function sendMessage(file, attachmentUrl) {
|
async function sendMessage(file, attachmentUrl) {
|
||||||
dmChannel.createMessage(content, file).then(() => {
|
try {
|
||||||
|
await dmChannel.createMessage(content, file);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.resp && e.resp.statusCode === 403) {
|
||||||
|
msg.channel.createMessage(`Could not send reply; the user has likely left the server or blocked the bot`);
|
||||||
|
} else if (e.resp) {
|
||||||
|
msg.channel.createMessage(`Could not send reply; error code ${e.resp.statusCode}`);
|
||||||
|
} else {
|
||||||
|
msg.channel.createMessage(`Could not send reply: ${e.toString()}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (attachmentUrl) {
|
if (attachmentUrl) {
|
||||||
content += `\n\n**Attachment:** ${attachmentUrl}`;
|
content += `\n\n**Attachment:** ${attachmentUrl}`;
|
||||||
logContent += `\n\n**Attachment:** ${attachmentUrl}`;
|
logContent += `\n\n**Attachment:** ${attachmentUrl}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the message in the modmail thread as well
|
// Show the message in the modmail thread as well
|
||||||
const timestamp = utils.getTimestamp();
|
msg.channel.createMessage(`[${utils.getTimestamp()}] » ${logContent}`);
|
||||||
msg.channel.createMessage(`[${timestamp}] » ${logContent}`);
|
|
||||||
}, (err) => {
|
|
||||||
if (err.resp && err.resp.statusCode === 403) {
|
|
||||||
msg.channel.createMessage(`Could not send reply; the user has likely left the server or blocked the bot`);
|
|
||||||
} else if (err.resp) {
|
|
||||||
msg.channel.createMessage(`Could not send reply; error code ${err.resp.statusCode}`);
|
|
||||||
} else {
|
|
||||||
msg.channel.createMessage(`Could not send reply: ${err.toString()}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
msg.delete();
|
msg.delete();
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the reply has an attachment, relay it as is
|
|
||||||
if (msg.attachments.length > 0) {
|
if (msg.attachments.length > 0) {
|
||||||
fs.readFile(attachments.getPath(msg.attachments[0].id), (err, data) => {
|
// If the reply has an attachment, relay it as is
|
||||||
|
fs.readFile(attachments.getPath(msg.attachments[0].id), async (err, data) => {
|
||||||
const file = {file: data, name: msg.attachments[0].filename};
|
const file = {file: data, name: msg.attachments[0].filename};
|
||||||
|
|
||||||
attachments.getUrl(msg.attachments[0].id, msg.attachments[0].filename).then(attachmentUrl => {
|
const attachmentUrl = await attachments.getUrl(msg.attachments[0].id, msg.attachments[0].filename);
|
||||||
sendMessage(file, attachmentUrl);
|
sendMessage(file, attachmentUrl);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
|
// Otherwise just send the message regularly
|
||||||
sendMessage();
|
sendMessage();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mods can reply to modmail threads using !r or !reply
|
// Mods can reply to modmail threads using !r or !reply
|
||||||
// These messages get relayed back to the DM thread between the bot and the user
|
// These messages get relayed back to the DM thread between the bot and the user
|
||||||
bot.registerCommand('reply', (msg, args) => {
|
utils.addInboxCommand('reply', (msg, args) => {
|
||||||
if (! msg.channel.guild) return;
|
|
||||||
if (msg.channel.guild.id !== utils.getInboxGuild(bot).id) return;
|
|
||||||
if (! isStaff(msg.member)) return;
|
|
||||||
|
|
||||||
const text = args.join(' ').trim();
|
const text = args.join(' ').trim();
|
||||||
reply(msg, text, false);
|
reply(msg, text, false);
|
||||||
});
|
});
|
||||||
|
@ -295,102 +250,74 @@ bot.registerCommand('reply', (msg, args) => {
|
||||||
bot.registerCommandAlias('r', 'reply');
|
bot.registerCommandAlias('r', 'reply');
|
||||||
|
|
||||||
// Anonymous replies only show the role, not the username
|
// Anonymous replies only show the role, not the username
|
||||||
bot.registerCommand('anonreply', (msg, args) => {
|
utils.addInboxCommand('anonreply', (msg, args) => {
|
||||||
if (! msg.channel.guild) return;
|
|
||||||
if (msg.channel.guild.id !== utils.getInboxGuild(bot).id) return;
|
|
||||||
if (! isStaff(msg.member)) return;
|
|
||||||
|
|
||||||
const text = args.join(' ').trim();
|
const text = args.join(' ').trim();
|
||||||
reply(msg, text, true);
|
reply(msg, text, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.registerCommandAlias('ar', 'anonreply');
|
bot.registerCommandAlias('ar', 'anonreply');
|
||||||
|
|
||||||
bot.registerCommand('close', (msg, args) => {
|
// Close a thread. Closing a thread saves a log of the channel's contents and then deletes the channel.
|
||||||
if (! msg.channel.guild) return;
|
utils.addInboxCommand('close', async (msg, args, thread) => {
|
||||||
if (msg.channel.guild.id !== utils.getInboxGuild(bot).id) return;
|
|
||||||
if (! isStaff(msg.member)) return;
|
|
||||||
|
|
||||||
threads.getByChannelId(msg.channel.id).then(thread => {
|
|
||||||
if (! thread) return;
|
if (! thread) return;
|
||||||
|
|
||||||
msg.channel.createMessage('Saving logs and closing channel...');
|
await msg.channel.createMessage('Saving logs and closing channel...');
|
||||||
msg.channel.getMessages(10000).then(messages => {
|
|
||||||
const log = messages.reverse().map(msg => {
|
const logMessages = await msg.channel.getMessages(10000);
|
||||||
|
const log = logMessages.reverse().map(msg => {
|
||||||
const date = moment.utc(msg.timestamp, 'x').format('YYYY-MM-DD HH:mm:ss');
|
const date = moment.utc(msg.timestamp, 'x').format('YYYY-MM-DD HH:mm:ss');
|
||||||
return `[${date}] ${msg.author.username}#${msg.author.discriminator}: ${msg.content}`;
|
return `[${date}] ${msg.author.username}#${msg.author.discriminator}: ${msg.content}`;
|
||||||
}).join('\n') + '\n';
|
}).join('\n') + '\n';
|
||||||
|
|
||||||
logs.getNewLogFile(thread.userId).then(logFilename => {
|
const logFilename = await logs.getNewLogFile(thread.userId);
|
||||||
logs.saveLogFile(logFilename, log)
|
await logs.saveLogFile(logFilename, log);
|
||||||
.then(() => logs.getLogFileUrl(logFilename))
|
|
||||||
.then(url => {
|
const logUrl = await logs.getLogFileUrl(logFilename);
|
||||||
const closeMessage = `Modmail thread with ${thread.username} (${thread.userId}) was closed by ${msg.author.username}
|
const closeMessage = `Modmail thread with ${thread.username} (${thread.userId}) was closed by ${msg.author.username}
|
||||||
Logs: <${url}>`;
|
Logs: <${logUrl}>`;
|
||||||
|
|
||||||
bot.createMessage(utils.getLogChannel(bot).id, closeMessage);
|
bot.createMessage(utils.getLogChannel(bot).id, closeMessage);
|
||||||
threads.close(thread.channelId).then(() => msg.channel.delete());
|
await threads.close(thread.channelId);
|
||||||
});
|
msg.channel.delete();
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.registerCommand('block', (msg, args) => {
|
utils.addInboxCommand('block', (msg, args, thread) => {
|
||||||
if (! msg.channel.guild) return;
|
async function block(userId) {
|
||||||
if (msg.channel.guild.id !== utils.getInboxGuild(bot).id) return;
|
await blocked.block(userId);
|
||||||
if (! isStaff(msg.member)) return;
|
|
||||||
|
|
||||||
function block(userId) {
|
|
||||||
blocked.block(userId).then(() => {
|
|
||||||
msg.channel.createMessage(`Blocked <@${userId}> (id ${userId}) from modmail`);
|
msg.channel.createMessage(`Blocked <@${userId}> (id ${userId}) from modmail`);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
|
// User mention/id as argument
|
||||||
const userId = utils.getUserMention(args.join(' '));
|
const userId = utils.getUserMention(args.join(' '));
|
||||||
if (! userId) return;
|
if (! userId) return;
|
||||||
block(userId);
|
block(userId);
|
||||||
} else {
|
} else if (thread) {
|
||||||
// Calling !block without args in a modmail thread blocks the user of that thread
|
// Calling !block without args in a modmail thread blocks the user of that thread
|
||||||
threads.getByChannelId(msg.channel.id).then(thread => {
|
|
||||||
if (! thread) return;
|
|
||||||
block(thread.userId);
|
block(thread.userId);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.registerCommand('unblock', (msg, args) => {
|
utils.addInboxCommand('unblock', (msg, args, thread) => {
|
||||||
if (! msg.channel.guild) return;
|
async function unblock(userId) {
|
||||||
if (msg.channel.guild.id !== utils.getInboxGuild(bot).id) return;
|
await blocked.unblock(userId);
|
||||||
if (! isStaff(msg.member)) return;
|
|
||||||
|
|
||||||
function unblock(userId) {
|
|
||||||
blocked.unblock(userId).then(() => {
|
|
||||||
msg.channel.createMessage(`Unblocked <@${userId}> (id ${userId}) from modmail`);
|
msg.channel.createMessage(`Unblocked <@${userId}> (id ${userId}) from modmail`);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
|
// User mention/id as argument
|
||||||
const userId = utils.getUserMention(args.join(' '));
|
const userId = utils.getUserMention(args.join(' '));
|
||||||
if (! userId) return;
|
if (! userId) return;
|
||||||
unblock(userId);
|
unblock(userId);
|
||||||
} else {
|
} else if (thread) {
|
||||||
// Calling !unblock without args in a modmail thread unblocks the user of that thread
|
// Calling !unblock without args in a modmail thread unblocks the user of that thread
|
||||||
threads.getByChannelId(msg.channel.id).then(thread => {
|
|
||||||
if (! thread) return;
|
|
||||||
unblock(thread.userId);
|
unblock(thread.userId);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.registerCommand('logs', (msg, args) => {
|
utils.addInboxCommand('logs', (msg, args, thread) => {
|
||||||
if (! msg.channel.guild) return;
|
async function getLogs(userId) {
|
||||||
if (msg.channel.guild.id !== utils.getInboxGuild(bot).id) return;
|
const infos = await logs.getLogsWithUrlByUserId(userId);
|
||||||
if (! isStaff(msg.member)) return;
|
|
||||||
|
|
||||||
function getLogs(userId) {
|
|
||||||
logs.getLogsWithUrlByUserId(userId).then(infos => {
|
|
||||||
let message = `**Log files for <@${userId}>:**\n`;
|
let message = `**Log files for <@${userId}>:**\n`;
|
||||||
|
|
||||||
message += infos.map(info => {
|
message += infos.map(info => {
|
||||||
|
@ -406,32 +333,33 @@ bot.registerCommand('logs', (msg, args) => {
|
||||||
chunks.forEach(lines => {
|
chunks.forEach(lines => {
|
||||||
root = root.then(() => msg.channel.createMessage(lines.join('\n')));
|
root = root.then(() => msg.channel.createMessage(lines.join('\n')));
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
|
// User mention/id as argument
|
||||||
const userId = utils.getUserMention(args.join(' '));
|
const userId = utils.getUserMention(args.join(' '));
|
||||||
if (! userId) return;
|
if (! userId) return;
|
||||||
getLogs(userId);
|
getLogs(userId);
|
||||||
} else {
|
} else if (thread) {
|
||||||
// Calling !logs without args in a modmail thread returns the logs of the user of that thread
|
// Calling !logs without args in a modmail thread returns the logs of the user of that thread
|
||||||
threads.getByChannelId(msg.channel.id).then(thread => {
|
|
||||||
if (! thread) return;
|
|
||||||
getLogs(thread.userId);
|
getLogs(thread.userId);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ---!!!!!!!!!!!--- REFACTOR LINE ---!!!!!!!!!!!---
|
||||||
|
*/
|
||||||
|
|
||||||
// Snippets
|
// Snippets
|
||||||
bot.on('messageCreate', async msg => {
|
bot.on('messageCreate', async msg => {
|
||||||
if (! msg.channel.guild) return;
|
if (! utils.messageIsOnInboxServer(msg)) return;
|
||||||
if (msg.channel.guild.id !== utils.getInboxGuild(bot).id) return;
|
if (! utils.isStaff(msg.member)) return;
|
||||||
if (! isStaff(msg.member)) return;
|
|
||||||
if (msg.author.bot) return;
|
if (msg.author.bot) return;
|
||||||
if (! msg.content) return;
|
if (! msg.content) return;
|
||||||
if (! msg.content.startsWith(snippetPrefix)) return;
|
if (! msg.content.startsWith(config.snippetPrefix)) return;
|
||||||
|
|
||||||
const shortcut = msg.content.replace(snippetPrefix, '').toLowerCase();
|
const shortcut = msg.content.replace(config.snippetPrefix, '').toLowerCase();
|
||||||
const snippet = await snippets.get(shortcut);
|
const snippet = await snippets.get(shortcut);
|
||||||
if (! snippet) return;
|
if (! snippet) return;
|
||||||
|
|
||||||
|
@ -439,24 +367,20 @@ bot.on('messageCreate', async msg => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show or add a snippet
|
// Show or add a snippet
|
||||||
bot.registerCommand('snippet', async (msg, args) => {
|
utils.addInboxCommand('snippet', async (msg, args) => {
|
||||||
if (! msg.channel.guild) return;
|
|
||||||
if (msg.channel.guild.id !== utils.getInboxGuild(bot).id) return;
|
|
||||||
if (! isStaff(msg.member)) return;
|
|
||||||
|
|
||||||
const shortcut = args[0];
|
const shortcut = args[0];
|
||||||
|
if (! shortcut) return
|
||||||
|
|
||||||
const text = args.slice(1).join(' ').trim();
|
const text = args.slice(1).join(' ').trim();
|
||||||
|
|
||||||
if (! shortcut) return;
|
|
||||||
|
|
||||||
const snippet = await snippets.get(shortcut);
|
const snippet = await snippets.get(shortcut);
|
||||||
|
|
||||||
if (snippet) {
|
if (snippet) {
|
||||||
if (text) {
|
if (text) {
|
||||||
// If the snippet exists and we're trying to create a new one, inform the user the snippet already exists
|
// If the snippet exists and we're trying to create a new one, inform the user the snippet already exists
|
||||||
msg.channel.createMessage(`Snippet "${shortcut}" already exists! You can edit or delete it with ${prefix}edit_snippet and ${prefix}delete_snippet respectively.`);
|
msg.channel.createMessage(`Snippet "${shortcut}" already exists! You can edit or delete it with ${prefix}edit_snippet and ${prefix}delete_snippet respectively.`);
|
||||||
} else {
|
} else {
|
||||||
// If the snippet exists and we're NOT trying to create a new one, show info about the existing snippet
|
// If the snippet exists and we're NOT trying to create a new one, show info about the existing snippet
|
||||||
msg.channel.createMessage(`\`${snippetPrefix}${shortcut}\` replies ${snippet.isAnonymous ? 'anonymously ' : ''}with:\n${snippet.text}`);
|
msg.channel.createMessage(`\`${config.snippetPrefix}${shortcut}\` replies ${snippet.isAnonymous ? 'anonymously ' : ''}with:\n${snippet.text}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (text) {
|
if (text) {
|
||||||
|
@ -466,17 +390,13 @@ bot.registerCommand('snippet', async (msg, args) => {
|
||||||
} else {
|
} else {
|
||||||
// If the snippet doesn't exist and the user isn't trying to create it, inform them how to create it
|
// If the snippet doesn't exist and the user isn't trying to create it, inform them how to create it
|
||||||
msg.channel.createMessage(`Snippet "${shortcut}" doesn't exist! You can create it with \`${prefix}snippet ${shortcut} text\``);
|
msg.channel.createMessage(`Snippet "${shortcut}" doesn't exist! You can create it with \`${prefix}snippet ${shortcut} text\``);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.registerCommandAlias('s', 'snippet');
|
bot.registerCommandAlias('s', 'snippet');
|
||||||
|
|
||||||
bot.registerCommand('delete_snippet', async (msg, args) => {
|
utils.addInboxCommand('delete_snippet', async (msg, args) => {
|
||||||
if (! msg.channel.guild) return;
|
|
||||||
if (msg.channel.guild.id !== utils.getInboxGuild(bot).id) return;
|
|
||||||
if (! isStaff(msg.member)) return;
|
|
||||||
|
|
||||||
const shortcut = args[0];
|
const shortcut = args[0];
|
||||||
if (! shortcut) return;
|
if (! shortcut) return;
|
||||||
|
|
||||||
|
@ -489,17 +409,14 @@ bot.registerCommand('delete_snippet', async (msg, args) => {
|
||||||
await snippets.del(shortcut);
|
await snippets.del(shortcut);
|
||||||
msg.channel.createMessage(`Snippet "${shortcut}" deleted!`);
|
msg.channel.createMessage(`Snippet "${shortcut}" deleted!`);
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.registerCommandAlias('ds', 'delete_snippet');
|
bot.registerCommandAlias('ds', 'delete_snippet');
|
||||||
|
|
||||||
bot.registerCommand('edit_snippet', async (msg, args) => {
|
utils.addInboxCommand('edit_snippet', async (msg, args) => {
|
||||||
if (! msg.channel.guild) return;
|
|
||||||
if (msg.channel.guild.id !== utils.getInboxGuild(bot).id) return;
|
|
||||||
if (! isStaff(msg.member)) return;
|
|
||||||
|
|
||||||
const shortcut = args[0];
|
const shortcut = args[0];
|
||||||
const text = args.slice(1).join(' ').trim();
|
|
||||||
|
|
||||||
if (! shortcut) return;
|
if (! shortcut) return;
|
||||||
|
|
||||||
|
const text = args.slice(1).join(' ').trim();
|
||||||
if (! text) return;
|
if (! text) return;
|
||||||
|
|
||||||
const snippet = await snippets.get(shortcut);
|
const snippet = await snippets.get(shortcut);
|
||||||
|
@ -513,20 +430,18 @@ bot.registerCommand('edit_snippet', async (msg, args) => {
|
||||||
|
|
||||||
msg.channel.createMessage(`Snippet "${shortcut}" edited!`);
|
msg.channel.createMessage(`Snippet "${shortcut}" edited!`);
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.registerCommandAlias('es', 'edit_snippet');
|
bot.registerCommandAlias('es', 'edit_snippet');
|
||||||
|
|
||||||
bot.registerCommand('snippets', async msg => {
|
utils.addInboxCommand('snippets', async msg => {
|
||||||
if (! msg.channel.guild) return;
|
|
||||||
if (msg.channel.guild.id !== utils.getInboxGuild(bot).id) return;
|
|
||||||
if (! isStaff(msg.member)) return;
|
|
||||||
|
|
||||||
const allSnippets = await snippets.all();
|
const allSnippets = await snippets.all();
|
||||||
const shortcuts = Object.keys(allSnippets);
|
const shortcuts = Object.keys(allSnippets);
|
||||||
shortcuts.sort();
|
shortcuts.sort();
|
||||||
|
|
||||||
msg.channel.createMessage(`Available snippets (prefix ${snippetPrefix}):\n${shortcuts.join(', ')}`);
|
msg.channel.createMessage(`Available snippets (prefix ${config.snippetPrefix}):\n${shortcuts.join(', ')}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Start the bot!
|
||||||
bot.connect();
|
bot.connect();
|
||||||
webserver.run();
|
webserver.run();
|
||||||
greeting.init(bot);
|
greeting.init(bot);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const config = require('../config');
|
const config = require('./config');
|
||||||
|
|
||||||
const dbDir = config.dbDir || `${__dirname}/../db`;
|
const dbDir = config.dbDir || `${__dirname}/../db`;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const config = require('../config');
|
const config = require('./config');
|
||||||
const utils = require('./utils');
|
|
||||||
|
const getUtils = () => require('./utils');
|
||||||
|
|
||||||
const logDir = config.logDir || `${__dirname}/../logs`;
|
const logDir = config.logDir || `${__dirname}/../logs`;
|
||||||
const logFileFormatRegex = /^([0-9\-]+?)__([0-9]+?)__([0-9a-f]+?)\.txt$/;
|
const logFileFormatRegex = /^([0-9\-]+?)__([0-9]+?)__([0-9a-f]+?)\.txt$/;
|
||||||
|
@ -51,7 +52,7 @@ function getLogFilePath(logFilename) {
|
||||||
*/
|
*/
|
||||||
function getLogFileUrl(logFilename) {
|
function getLogFileUrl(logFilename) {
|
||||||
const info = getLogFileInfo(logFilename);
|
const info = getLogFileInfo(logFilename);
|
||||||
return utils.getSelfUrl(`logs/${info.token}`);
|
return getUtils().getSelfUrl(`logs/${info.token}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
const Eris = require('eris');
|
const Eris = require('eris');
|
||||||
const transliterate = require('transliteration');
|
const transliterate = require('transliteration');
|
||||||
const utils = require('./utils');
|
|
||||||
const jsonDb = require('./jsonDb');
|
const jsonDb = require('./jsonDb');
|
||||||
const config = require('../config');
|
const config = require('./config');
|
||||||
|
|
||||||
|
const getUtils = () => require('./utils');
|
||||||
|
|
||||||
// If the following messages would be used to start a thread, ignore it instead
|
// If the following messages would be used to start a thread, ignore it instead
|
||||||
// This is to prevent accidental threads from e.g. irrelevant replies after the thread was already closed
|
// This is to prevent accidental threads from e.g. irrelevant replies after the thread was already closed
|
||||||
|
@ -56,12 +57,11 @@ const accidentalThreadMessages = [
|
||||||
/**
|
/**
|
||||||
* Returns information about the modmail thread channel for the given user. We can't return channel objects
|
* Returns information about the modmail thread channel for the given user. We can't return channel objects
|
||||||
* directly since they're not always available immediately after creation.
|
* directly since they're not always available immediately after creation.
|
||||||
* @param {Eris.Client} bot
|
|
||||||
* @param {Eris.User} user
|
* @param {Eris.User} user
|
||||||
* @param {Boolean} allowCreate
|
* @param {Boolean} allowCreate
|
||||||
* @returns {Promise<ModMailThread>}
|
* @returns {Promise<ModMailThread>}
|
||||||
*/
|
*/
|
||||||
function getForUser(bot, user, allowCreate = true, originalMessage = null) {
|
function getForUser(user, allowCreate = true, originalMessage = null) {
|
||||||
return jsonDb.get('threads', []).then(threads => {
|
return jsonDb.get('threads', []).then(threads => {
|
||||||
const thread = threads.find(t => t.userId === user.id);
|
const thread = threads.find(t => t.userId === user.id);
|
||||||
if (thread) return thread;
|
if (thread) return thread;
|
||||||
|
@ -85,7 +85,7 @@ function getForUser(bot, user, allowCreate = true, originalMessage = null) {
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[NOTE] Creating new thread channel ${channelName}`);
|
console.log(`[NOTE] Creating new thread channel ${channelName}`);
|
||||||
return utils.getInboxGuild(bot).createChannel(`${channelName}`)
|
return getUtils().getInboxGuild().createChannel(`${channelName}`)
|
||||||
.then(channel => {
|
.then(channel => {
|
||||||
const thread = {
|
const thread = {
|
||||||
channelId: channel.id,
|
channelId: channel.id,
|
||||||
|
|
110
src/utils.js
110
src/utils.js
|
@ -1,38 +1,119 @@
|
||||||
const Eris = require('eris');
|
const Eris = require('eris');
|
||||||
|
const bot = require('./bot');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const publicIp = require('public-ip');
|
const publicIp = require('public-ip');
|
||||||
const config = require('../config');
|
const threads = require('./threads');
|
||||||
const utils = require('./utils');
|
const attachments = require('./attachments');
|
||||||
|
const config = require('./config');
|
||||||
|
|
||||||
|
class BotError extends Error {}
|
||||||
|
|
||||||
|
const userMentionRegex = /^<@\!?([0-9]+?)>$/;
|
||||||
|
|
||||||
let inboxGuild = null;
|
let inboxGuild = null;
|
||||||
let mainGuild = null;
|
let mainGuild = null;
|
||||||
let logChannel = null;
|
let logChannel = null;
|
||||||
|
|
||||||
function getInboxGuild(bot) {
|
function getInboxGuild() {
|
||||||
if (! inboxGuild) inboxGuild = bot.guilds.find(g => g.id === config.mailGuildId);
|
if (! inboxGuild) inboxGuild = bot.guilds.find(g => g.id === config.mailGuildId);
|
||||||
|
if (! inboxGuild) throw new BotError('The bot is not on the modmail (inbox) server!');
|
||||||
return inboxGuild;
|
return inboxGuild;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMainGuild(bot) {
|
function getMainGuild() {
|
||||||
if (! mainGuild) mainGuild = bot.guilds.find(g => g.id === config.mainGuildId);
|
if (! mainGuild) mainGuild = bot.guilds.find(g => g.id === config.mainGuildId);
|
||||||
|
if (! mainGuild) console.warn('[WARN] The bot is not on the main server! If this is intentional, you can ignore this warning.');
|
||||||
return mainGuild;
|
return mainGuild;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLogChannel(bot) {
|
/**
|
||||||
const inboxGuild = getInboxGuild(bot);
|
* Returns the designated log channel, or the default channel if none is set
|
||||||
|
* @param bot
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
|
function getLogChannel() {
|
||||||
|
const inboxGuild = getInboxGuild();
|
||||||
|
|
||||||
if (! config.logChannelId) {
|
if (! config.logChannelId) {
|
||||||
return inboxGuild.channels.get(inboxGuild.id);
|
logChannel = inboxGuild.channels.get(inboxGuild.id);
|
||||||
|
} else if (! logChannel) {
|
||||||
|
logChannel = inboxGuild.channels.get(config.logChannelId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! logChannel) {
|
if (! logChannel) {
|
||||||
logChannel = inboxGuild.channels.get(config.logChannelId);
|
throw new BotError('Log channel not found!');
|
||||||
}
|
}
|
||||||
|
|
||||||
return logChannel;
|
return logChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
const userMentionRegex = /^<@\!?([0-9]+?)>$/;
|
function postError(str) {
|
||||||
|
getLogChannel().createMessage({
|
||||||
|
content: `@here **Error:** ${str.trim()}`,
|
||||||
|
disableEveryone: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given member has permission to use modmail commands
|
||||||
|
* @param member
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function isStaff(member) {
|
||||||
|
if (! config.inboxServerPermission) return true;
|
||||||
|
return member.permission.has(config.inboxServerPermission);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given message is on the inbox server
|
||||||
|
* @param msg
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function messageIsOnInboxServer(msg) {
|
||||||
|
if (! msg.channel.guild) return false;
|
||||||
|
if (msg.channel.guild.id !== getInboxGuild().id) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given message is on the main server
|
||||||
|
* @param msg
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function messageIsOnMainServer(msg) {
|
||||||
|
if (! msg.channel.guild) return false;
|
||||||
|
if (msg.channel.guild.id !== getMainGuild().id) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a command that can only be triggered on the inbox server.
|
||||||
|
* Command handlers added with this function also get the thread the message was posted in as a third argument, if any.
|
||||||
|
* @param cmd
|
||||||
|
* @param fn
|
||||||
|
* @param opts
|
||||||
|
*/
|
||||||
|
function addInboxCommand(cmd, fn, opts) {
|
||||||
|
bot.registerCommand(cmd, async (msg, args) => {
|
||||||
|
if (! messageIsOnInboxServer(msg)) return;
|
||||||
|
if (! isStaff(msg.member)) return;
|
||||||
|
|
||||||
|
const thread = await threads.getByChannelId(msg.channel.id);
|
||||||
|
fn(msg, args, thread);
|
||||||
|
}, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param attachment
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
async function formatAttachment(attachment) {
|
||||||
|
let filesize = attachment.size || 0;
|
||||||
|
filesize /= 1024;
|
||||||
|
|
||||||
|
const attachmentUrl = await attachments.getUrl(attachment.id, attachment.filename);
|
||||||
|
return `**Attachment:** ${attachment.filename} (${filesize.toFixed(1)}KB)\n${attachmentUrl}`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the user ID of the user mentioned in str, if any
|
* Returns the user ID of the user mentioned in str, if any
|
||||||
|
@ -115,9 +196,20 @@ function chunk(items, chunkSize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
BotError,
|
||||||
|
|
||||||
getInboxGuild,
|
getInboxGuild,
|
||||||
getMainGuild,
|
getMainGuild,
|
||||||
getLogChannel,
|
getLogChannel,
|
||||||
|
postError,
|
||||||
|
|
||||||
|
isStaff,
|
||||||
|
messageIsOnInboxServer,
|
||||||
|
messageIsOnMainServer,
|
||||||
|
addInboxCommand,
|
||||||
|
|
||||||
|
formatAttachment,
|
||||||
|
|
||||||
getUserMention,
|
getUserMention,
|
||||||
getTimestamp,
|
getTimestamp,
|
||||||
disableLinkPreviews,
|
disableLinkPreviews,
|
||||||
|
|
|
@ -2,7 +2,7 @@ const http = require('http');
|
||||||
const mime = require('mime');
|
const mime = require('mime');
|
||||||
const url = require('url');
|
const url = require('url');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const config = require('../config');
|
const config = require('./config');
|
||||||
const logs = require('./logs');
|
const logs = require('./logs');
|
||||||
const attachments = require('./attachments');
|
const attachments = require('./attachments');
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue