diff --git a/src/bot.js b/src/bot.js index 41a37d9..1f52c16 100644 --- a/src/bot.js +++ b/src/bot.js @@ -1,5 +1,5 @@ -const Eris = require('eris'); -const config = require('./cfg'); +const Eris = require("eris"); +const config = require("./cfg"); const bot = new Eris.Client(config.token, { getAllUsers: true, diff --git a/src/cfg.js b/src/cfg.js index 0d86065..dc18bd0 100644 --- a/src/cfg.js +++ b/src/cfg.js @@ -1,29 +1,29 @@ -const fs = require('fs'); -const path = require('path'); -const Ajv = require('ajv'); -const schema = require('./data/cfg.schema.json'); +const fs = require("fs"); +const path = require("path"); +const Ajv = require("ajv"); +const schema = require("./data/cfg.schema.json"); /** @type {ModmailConfig} */ let config = {}; // Config files to search for, in priority order const configFiles = [ - 'config.ini', - 'config.json', - 'config.json5', - 'config.js', + "config.ini", + "config.json", + "config.json5", + "config.js", // Possible config files when file extensions are hidden - 'config.ini.ini', - 'config.ini.txt', - 'config.json.json', - 'config.json.txt', + "config.ini.ini", + "config.ini.txt", + "config.json.json", + "config.json.txt", ]; let foundConfigFile; for (const configFile of configFiles) { try { - fs.accessSync(__dirname + '/../' + configFile); + fs.accessSync(__dirname + "/../" + configFile); foundConfigFile = configFile; break; } catch (e) {} @@ -33,14 +33,14 @@ for (const configFile of configFiles) { if (foundConfigFile) { console.log(`Loading configuration from ${foundConfigFile}...`); try { - if (foundConfigFile.endsWith('.js')) { + if (foundConfigFile.endsWith(".js")) { config = require(`../${foundConfigFile}`); } else { - const raw = fs.readFileSync(__dirname + '/../' + foundConfigFile, {encoding: "utf8"}); - if (foundConfigFile.endsWith('.ini') || foundConfigFile.endsWith('.ini.txt')) { - config = require('ini').decode(raw); + const raw = fs.readFileSync(__dirname + "/../" + foundConfigFile, {encoding: "utf8"}); + if (foundConfigFile.endsWith(".ini") || foundConfigFile.endsWith(".ini.txt")) { + config = require("ini").decode(raw); } else { - config = require('json5').parse(raw); + config = require("json5").parse(raw); } } } catch (e) { @@ -49,11 +49,11 @@ if (foundConfigFile) { } // Set dynamic default values which can't be set in the schema directly -config.dbDir = path.join(__dirname, '..', 'db'); -config.logDir = path.join(__dirname, '..', 'logs'); // Only used for migrating data from older Modmail versions +config.dbDir = path.join(__dirname, "..", "db"); +config.logDir = path.join(__dirname, "..", "logs"); // Only used for migrating data from older Modmail versions // Load config values from environment variables -const envKeyPrefix = 'MM_'; +const envKeyPrefix = "MM_"; let loadedEnvValues = 0; for (const [key, value] of Object.entries(process.env)) { @@ -64,10 +64,10 @@ for (const [key, value] of Object.entries(process.env)) { const configKey = key.slice(envKeyPrefix.length) .toLowerCase() .replace(/([a-z])_([a-z])/g, (m, m1, m2) => `${m1}${m2.toUpperCase()}`) - .replace('__', '.'); + .replace("__", "."); - config[configKey] = value.includes('||') - ? value.split('||') + config[configKey] = value.includes("||") + ? value.split("||") : value; loadedEnvValues++; @@ -80,15 +80,15 @@ if (process.env.PORT && ! process.env.MM_PORT) { } if (loadedEnvValues > 0) { - console.log(`Loaded ${loadedEnvValues} ${loadedEnvValues === 1 ? 'value' : 'values'} from environment variables`); + console.log(`Loaded ${loadedEnvValues} ${loadedEnvValues === 1 ? "value" : "values"} from environment variables`); } // Convert config keys with periods to objects // E.g. commandAliases.mv -> commandAliases: { mv: ... } for (const [key, value] of Object.entries(config)) { - if (! key.includes('.')) continue; + if (! key.includes(".")) continue; - const keys = key.split('.'); + const keys = key.split("."); let cursor = config; for (let i = 0; i < keys.length; i++) { if (i === keys.length - 1) { @@ -102,20 +102,20 @@ for (const [key, value] of Object.entries(config)) { delete config[key]; } -if (! config['knex']) { +if (! config["knex"]) { config.knex = { - client: 'sqlite', + client: "sqlite", connection: { - filename: path.join(config.dbDir, 'data.sqlite') + filename: path.join(config.dbDir, "data.sqlite") }, useNullAsDefault: true }; } // Make sure migration settings are always present in knex config -Object.assign(config['knex'], { +Object.assign(config["knex"], { migrations: { - directory: path.join(config.dbDir, 'migrations') + directory: path.join(config.dbDir, "migrations") } }); @@ -141,7 +141,7 @@ if (config.newThreadCategoryId) { // Delete empty string options (i.e. "option=" without a value in config.ini) for (const [key, value] of Object.entries(config)) { - if (value === '') { + if (value === "") { delete config[key]; } } @@ -152,7 +152,7 @@ const ajv = new Ajv({ useDefaults: true, coerceTypes: "array" }); // https://github.com/ajv-validator/ajv/issues/141#issuecomment-270692820 const truthyValues = ["1", "true", "on"]; const falsyValues = ["0", "false", "off"]; -ajv.addKeyword('coerceBoolean', { +ajv.addKeyword("coerceBoolean", { compile(value) { return (data, dataPath, parentData, parentKey) => { if (! value) { @@ -183,7 +183,7 @@ ajv.addKeyword('coerceBoolean', { }, }); -ajv.addKeyword('multilineString', { +ajv.addKeyword("multilineString", { compile(value) { return (data, dataPath, parentData, parentKey) => { if (! value) { @@ -208,12 +208,12 @@ const validate = ajv.compile(schema); const configIsValid = validate(config); if (! configIsValid) { - console.error('Issues with configuration options:'); + console.error("Issues with configuration options:"); for (const error of validate.errors) { console.error(`The "${error.dataPath.slice(1)}" option ${error.message}`); } - console.error(''); - console.error('Please restart the bot after fixing the issues mentioned above.'); + console.error(""); + console.error("Please restart the bot after fixing the issues mentioned above."); process.exit(1); } diff --git a/src/commands.js b/src/commands.js index 3135ee9..cdc95de 100644 --- a/src/commands.js +++ b/src/commands.js @@ -1,9 +1,9 @@ -const { CommandManager, defaultParameterTypes, TypeConversionError, IParameter, ICommandConfig } = require('knub-command-manager'); -const Eris = require('eris'); -const config = require('./cfg'); -const utils = require('./utils'); -const threads = require('./data/threads'); -const Thread = require('./data/Thread'); +const { CommandManager, defaultParameterTypes, TypeConversionError, IParameter, ICommandConfig } = require("knub-command-manager"); +const Eris = require("eris"); +const config = require("./cfg"); +const utils = require("./utils"); +const threads = require("./data/threads"); +const Thread = require("./data/Thread"); module.exports = { createCommandManager(bot) { @@ -27,7 +27,7 @@ module.exports = { const handlers = {}; const aliasMap = new Map(); - bot.on('messageCreate', async msg => { + bot.on("messageCreate", async msg => { if (msg.author.bot) return; if (msg.author.id === bot.user.id) return; if (! msg.content) return; diff --git a/src/data/Thread.js b/src/data/Thread.js index 465d25f..747c449 100644 --- a/src/data/Thread.js +++ b/src/data/Thread.js @@ -1,16 +1,16 @@ -const moment = require('moment'); -const Eris = require('eris'); +const moment = require("moment"); +const Eris = require("eris"); -const bot = require('../bot'); -const knex = require('../knex'); -const utils = require('../utils'); -const config = require('../cfg'); -const attachments = require('./attachments'); -const { formatters } = require('../formatters'); +const bot = require("../bot"); +const knex = require("../knex"); +const utils = require("../utils"); +const config = require("../cfg"); +const attachments = require("./attachments"); +const { formatters } = require("../formatters"); -const ThreadMessage = require('./ThreadMessage'); +const ThreadMessage = require("./ThreadMessage"); -const {THREAD_MESSAGE_TYPE, THREAD_STATUS} = require('./constants'); +const {THREAD_MESSAGE_TYPE, THREAD_STATUS} = require("./constants"); /** * @property {String} id @@ -41,12 +41,12 @@ class Thread { // Try to open a DM channel with the user const dmChannel = await this.getDMChannel(); if (! dmChannel) { - throw new Error('Could not open DMs with the user. They may have blocked the bot or set their privacy settings higher.'); + throw new Error("Could not open DMs with the user. They may have blocked the bot or set their privacy settings higher."); } let firstMessage; - if (typeof content === 'string') { + if (typeof content === "string") { // Content is a string, chunk it and send it as individual messages. // Files (attachments) are only sent with the last message. const chunks = utils.chunk(content, 2000); @@ -79,7 +79,7 @@ class Thread { try { let firstMessage; - if (typeof content === 'string') { + if (typeof content === "string") { // Content is a string, chunk it and send it as individual messages. // Files (attachments) are only sent with the last message. const chunks = utils.chunk(content, 2000); @@ -120,16 +120,16 @@ class Thread { } const dmChannel = await this.getDMChannel(); - const insertedIds = await knex('thread_messages').insert({ + const insertedIds = await knex("thread_messages").insert({ thread_id: this.id, - created_at: moment.utc().format('YYYY-MM-DD HH:mm:ss'), + created_at: moment.utc().format("YYYY-MM-DD HH:mm:ss"), is_anonymous: 0, dm_channel_id: dmChannel.id, ...data }); - const threadMessage = await knex('thread_messages') - .where('id', insertedIds[0]) + const threadMessage = await knex("thread_messages") + .where("id", insertedIds[0]) .select(); return new ThreadMessage(threadMessage[0]); @@ -142,8 +142,8 @@ class Thread { * @private */ async _updateThreadMessage(id, data) { - await knex('thread_messages') - .where('id', id) + await knex("thread_messages") + .where("id", id) .update(data); } @@ -153,8 +153,8 @@ class Thread { * @private */ async _deleteThreadMessage(id) { - await knex('thread_messages') - .where('id', id) + await knex("thread_messages") + .where("id", id) .delete(); } @@ -163,8 +163,8 @@ class Thread { * @private */ _lastMessageNumberInThreadSQL() { - return knex('thread_messages AS tm_msg_num_ref') - .select(knex.raw('MAX(tm_msg_num_ref.message_number)')) + return knex("thread_messages AS tm_msg_num_ref") + .select(knex.raw("MAX(tm_msg_num_ref.message_number)")) .whereRaw(`tm_msg_num_ref.thread_id = '${this.id}'`) .toSQL() .sql; @@ -212,7 +212,7 @@ class Thread { message_type: THREAD_MESSAGE_TYPE.TO_USER, user_id: moderator.id, user_name: fullModeratorName, - body: '', + body: "", is_anonymous: (isAnonymous ? 1 : 0), dm_message_id: dmMessage.id }); @@ -227,7 +227,7 @@ class Thread { // Interrupt scheduled closing, if in progress if (this.scheduled_close_at) { await this.cancelScheduledClose(); - await this.postSystemMessage(`Cancelling scheduled closing of this thread due to new reply`); + await this.postSystemMessage("Cancelling scheduled closing of this thread due to new reply"); } return true; @@ -306,11 +306,11 @@ class Thread { async postSystemMessage(content, file = null, opts = {}) { const msg = await this._postToThreadChannel(content, file); if (msg && opts.saveToLog !== false) { - const finalLogBody = opts.logBody || msg.content || ''; + const finalLogBody = opts.logBody || msg.content || ""; await this._addThreadMessageToDB({ message_type: THREAD_MESSAGE_TYPE.SYSTEM, user_id: null, - user_name: '', + user_name: "", body: finalLogBody, is_anonymous: 0, inbox_message_id: msg.id, @@ -329,11 +329,11 @@ class Thread { async sendSystemMessageToUser(content, file = null, opts = {}) { const msg = await this._sendDMToUser(content, file); if (opts.saveToLog !== false) { - const finalLogBody = opts.logBody || msg.content || ''; + const finalLogBody = opts.logBody || msg.content || ""; await this._addThreadMessageToDB({ message_type: THREAD_MESSAGE_TYPE.SYSTEM_TO_USER, user_id: null, - user_name: '', + user_name: "", body: finalLogBody, is_anonymous: 0, dm_message_id: msg.id, @@ -381,9 +381,9 @@ class Thread { * @returns {Promise} */ async updateChatMessageInLogs(msg) { - await knex('thread_messages') - .where('thread_id', this.id) - .where('dm_message_id', msg.id) + await knex("thread_messages") + .where("thread_id", this.id) + .where("dm_message_id", msg.id) .update({ body: msg.content }); @@ -394,9 +394,9 @@ class Thread { * @returns {Promise} */ async deleteChatMessageFromLogs(messageId) { - await knex('thread_messages') - .where('thread_id', this.id) - .where('dm_message_id', messageId) + await knex("thread_messages") + .where("thread_id", this.id) + .where("dm_message_id", messageId) .delete(); } @@ -404,10 +404,10 @@ class Thread { * @returns {Promise} */ async getThreadMessages() { - const threadMessages = await knex('thread_messages') - .where('thread_id', this.id) - .orderBy('created_at', 'ASC') - .orderBy('id', 'ASC') + const threadMessages = await knex("thread_messages") + .where("thread_id", this.id) + .orderBy("created_at", "ASC") + .orderBy("id", "ASC") .select(); return threadMessages.map(row => new ThreadMessage(row)); @@ -418,9 +418,9 @@ class Thread { * @returns {Promise} */ async findThreadMessageByMessageNumber(messageNumber) { - const data = await knex('thread_messages') - .where('thread_id', this.id) - .where('message_number', messageNumber) + const data = await knex("thread_messages") + .where("thread_id", this.id) + .where("message_number", messageNumber) .select(); return data ? new ThreadMessage(data) : null; @@ -434,15 +434,15 @@ class Thread { console.log(`Closing thread ${this.id}`); if (silent) { - await this.postSystemMessage('Closing thread silently...'); + await this.postSystemMessage("Closing thread silently..."); } else { - await this.postSystemMessage('Closing thread...'); + await this.postSystemMessage("Closing thread..."); } } // Update DB status - await knex('threads') - .where('id', this.id) + await knex("threads") + .where("id", this.id) .update({ status: THREAD_STATUS.CLOSED }); @@ -451,7 +451,7 @@ class Thread { const channel = bot.getChannel(this.channel_id); if (channel) { console.log(`Deleting channel ${this.channel_id}`); - await channel.delete('Thread closed'); + await channel.delete("Thread closed"); } } @@ -462,8 +462,8 @@ class Thread { * @returns {Promise} */ async scheduleClose(time, user, silent) { - await knex('threads') - .where('id', this.id) + await knex("threads") + .where("id", this.id) .update({ scheduled_close_at: time, scheduled_close_id: user.id, @@ -476,8 +476,8 @@ class Thread { * @returns {Promise} */ async cancelScheduledClose() { - await knex('threads') - .where('id', this.id) + await knex("threads") + .where("id", this.id) .update({ scheduled_close_at: null, scheduled_close_id: null, @@ -490,8 +490,8 @@ class Thread { * @returns {Promise} */ async suspend() { - await knex('threads') - .where('id', this.id) + await knex("threads") + .where("id", this.id) .update({ status: THREAD_STATUS.SUSPENDED, scheduled_suspend_at: null, @@ -504,8 +504,8 @@ class Thread { * @returns {Promise} */ async unsuspend() { - await knex('threads') - .where('id', this.id) + await knex("threads") + .where("id", this.id) .update({ status: THREAD_STATUS.OPEN }); @@ -517,8 +517,8 @@ class Thread { * @returns {Promise} */ async scheduleSuspend(time, user) { - await knex('threads') - .where('id', this.id) + await knex("threads") + .where("id", this.id) .update({ scheduled_suspend_at: time, scheduled_suspend_id: user.id, @@ -530,8 +530,8 @@ class Thread { * @returns {Promise} */ async cancelScheduledSuspend() { - await knex('threads') - .where('id', this.id) + await knex("threads") + .where("id", this.id) .update({ scheduled_suspend_at: null, scheduled_suspend_id: null, @@ -544,8 +544,8 @@ class Thread { * @returns {Promise} */ async setAlert(userId) { - await knex('threads') - .where('id', this.id) + await knex("threads") + .where("id", this.id) .update({ alert_id: userId }); diff --git a/src/data/attachments.js b/src/data/attachments.js index cf8cc6d..3d3ae16 100644 --- a/src/data/attachments.js +++ b/src/data/attachments.js @@ -1,13 +1,13 @@ -const Eris = require('eris'); -const fs = require('fs'); -const https = require('https'); -const {promisify} = require('util'); -const tmp = require('tmp'); -const config = require('../cfg'); -const utils = require('../utils'); -const mv = promisify(require('mv')); +const Eris = require("eris"); +const fs = require("fs"); +const https = require("https"); +const {promisify} = require("util"); +const tmp = require("tmp"); +const config = require("../cfg"); +const utils = require("../utils"); +const mv = promisify(require("mv")); -const getUtils = () => require('../utils'); +const getUtils = () => require("../utils"); const access = promisify(fs.access); const readFile = promisify(fs.readFile); @@ -20,7 +20,7 @@ const attachmentStorageTypes = {}; function getErrorResult(msg = null) { return { - url: `Attachment could not be saved${msg ? ': ' + msg : ''}`, + url: `Attachment could not be saved${msg ? ": " + msg : ""}`, failed: true }; } @@ -61,8 +61,8 @@ async function saveLocalAttachment(attachment) { function downloadAttachment(attachment, tries = 0) { return new Promise((resolve, reject) => { if (tries > 3) { - console.error('Attachment download failed after 3 tries:', attachment); - reject('Attachment download failed after 3 tries'); + console.error("Attachment download failed after 3 tries:", attachment); + reject("Attachment download failed after 3 tries"); return; } @@ -71,16 +71,16 @@ function downloadAttachment(attachment, tries = 0) { https.get(attachment.url, (res) => { res.pipe(writeStream); - writeStream.on('finish', () => { + writeStream.on("finish", () => { writeStream.end(); resolve({ path: filepath, cleanup: cleanupCallback }); }); - }).on('error', (err) => { + }).on("error", (err) => { fs.unlink(filepath); - console.error('Error downloading attachment, retrying'); + console.error("Error downloading attachment, retrying"); resolve(downloadAttachment(attachment, tries++)); }); }); @@ -103,7 +103,7 @@ function getLocalAttachmentPath(attachmentId) { * @returns {Promise} */ function getLocalAttachmentUrl(attachmentId, desiredName = null) { - if (desiredName == null) desiredName = 'file.bin'; + if (desiredName == null) desiredName = "file.bin"; return getUtils().getSelfUrl(`attachments/${attachmentId}/${desiredName}`); } @@ -113,19 +113,19 @@ function getLocalAttachmentUrl(attachmentId, desiredName = null) { */ async function saveDiscordAttachment(attachment) { if (attachment.size > 1024 * 1024 * 8) { - return getErrorResult('attachment too large (max 8MB)'); + return getErrorResult("attachment too large (max 8MB)"); } const attachmentChannelId = config.attachmentStorageChannelId; const inboxGuild = utils.getInboxGuild(); if (! inboxGuild.channels.has(attachmentChannelId)) { - throw new Error('Attachment storage channel not found!'); + throw new Error("Attachment storage channel not found!"); } const attachmentChannel = inboxGuild.channels.get(attachmentChannelId); if (! (attachmentChannel instanceof Eris.TextChannel)) { - throw new Error('Attachment storage channel must be a text channel!'); + throw new Error("Attachment storage channel must be a text channel!"); } const file = await attachmentToDiscordFileObject(attachment); diff --git a/src/data/blocked.js b/src/data/blocked.js index 81c1214..533baf1 100644 --- a/src/data/blocked.js +++ b/src/data/blocked.js @@ -1,13 +1,13 @@ -const moment = require('moment'); -const knex = require('../knex'); +const moment = require("moment"); +const knex = require("../knex"); /** * @param {String} userId * @returns {Promise<{ isBlocked: boolean, expiresAt: string }>} */ async function getBlockStatus(userId) { - const row = await knex('blocked_users') - .where('user_id', userId) + const row = await knex("blocked_users") + .where("user_id", userId) .first(); return { @@ -32,15 +32,15 @@ async function isBlocked(userId) { * @param {String} blockedBy * @returns {Promise} */ -async function block(userId, userName = '', blockedBy = null, expiresAt = null) { +async function block(userId, userName = "", blockedBy = null, expiresAt = null) { if (await isBlocked(userId)) return; - return knex('blocked_users') + return knex("blocked_users") .insert({ user_id: userId, user_name: userName, blocked_by: blockedBy, - blocked_at: moment.utc().format('YYYY-MM-DD HH:mm:ss'), + blocked_at: moment.utc().format("YYYY-MM-DD HH:mm:ss"), expires_at: expiresAt }); } @@ -51,8 +51,8 @@ async function block(userId, userName = '', blockedBy = null, expiresAt = null) * @returns {Promise} */ async function unblock(userId) { - return knex('blocked_users') - .where('user_id', userId) + return knex("blocked_users") + .where("user_id", userId) .delete(); } @@ -63,8 +63,8 @@ async function unblock(userId) { * @returns {Promise} */ async function updateExpiryTime(userId, expiresAt) { - return knex('blocked_users') - .where('user_id', userId) + return knex("blocked_users") + .where("user_id", userId) .update({ expires_at: expiresAt }); @@ -74,11 +74,11 @@ async function updateExpiryTime(userId, expiresAt) { * @returns {String[]} */ async function getExpiredBlocks() { - const now = moment.utc().format('YYYY-MM-DD HH:mm:ss'); + const now = moment.utc().format("YYYY-MM-DD HH:mm:ss"); - const blocks = await knex('blocked_users') - .whereNotNull('expires_at') - .where('expires_at', '<=', now) + const blocks = await knex("blocked_users") + .whereNotNull("expires_at") + .where("expires_at", "<=", now) .select(); return blocks.map(block => block.user_id); diff --git a/src/data/constants.js b/src/data/constants.js index 40388db..067cb06 100644 --- a/src/data/constants.js +++ b/src/data/constants.js @@ -27,41 +27,41 @@ module.exports = { }, ACCIDENTAL_THREAD_MESSAGES: [ - 'ok', - 'okay', - 'thanks', - 'ty', - 'k', - 'kk', - 'thank you', - 'thanx', - 'thnx', - 'thx', - 'tnx', - 'ok thank you', - 'ok thanks', - 'ok ty', - 'ok thanx', - 'ok thnx', - 'ok thx', - 'ok no problem', - 'ok np', - 'okay thank you', - 'okay thanks', - 'okay ty', - 'okay thanx', - 'okay thnx', - 'okay thx', - 'okay no problem', - 'okay np', - 'okey thank you', - 'okey thanks', - 'okey ty', - 'okey thanx', - 'okey thnx', - 'okey thx', - 'okey no problem', - 'okey np', - 'cheers' + "ok", + "okay", + "thanks", + "ty", + "k", + "kk", + "thank you", + "thanx", + "thnx", + "thx", + "tnx", + "ok thank you", + "ok thanks", + "ok ty", + "ok thanx", + "ok thnx", + "ok thx", + "ok no problem", + "ok np", + "okay thank you", + "okay thanks", + "okay ty", + "okay thanx", + "okay thnx", + "okay thx", + "okay no problem", + "okay np", + "okey thank you", + "okey thanks", + "okey ty", + "okey thanx", + "okey thnx", + "okey thx", + "okey no problem", + "okey np", + "cheers" ], }; diff --git a/src/data/generateCfgJsdoc.js b/src/data/generateCfgJsdoc.js index 096ddb8..4470dd4 100644 --- a/src/data/generateCfgJsdoc.js +++ b/src/data/generateCfgJsdoc.js @@ -1,8 +1,8 @@ -const path = require('path'); -const fs = require('fs'); -const toJsdoc = require('json-schema-to-jsdoc'); -const schema = require('./cfg.schema.json'); -const target = path.join(__dirname, 'cfg.jsdoc.js'); +const path = require("path"); +const fs = require("fs"); +const toJsdoc = require("json-schema-to-jsdoc"); +const schema = require("./cfg.schema.json"); +const target = path.join(__dirname, "cfg.jsdoc.js"); // Fix up some custom types for the JSDoc conversion const schemaCopy = JSON.parse(JSON.stringify(schema)); @@ -20,4 +20,4 @@ for (const propertyDef of Object.values(schemaCopy.properties)) { } const result = toJsdoc(schemaCopy); -fs.writeFileSync(target, result, { encoding: 'utf8' }); +fs.writeFileSync(target, result, { encoding: "utf8" }); diff --git a/src/data/snippets.js b/src/data/snippets.js index a95b2b4..0e4aebc 100644 --- a/src/data/snippets.js +++ b/src/data/snippets.js @@ -1,14 +1,14 @@ -const moment = require('moment'); -const knex = require('../knex'); -const Snippet = require('./Snippet'); +const moment = require("moment"); +const knex = require("../knex"); +const Snippet = require("./Snippet"); /** * @param {String} trigger * @returns {Promise} */ async function getSnippet(trigger) { - const snippet = await knex('snippets') - .where('trigger', trigger) + const snippet = await knex("snippets") + .where("trigger", trigger) .first(); return (snippet ? new Snippet(snippet) : null); @@ -22,11 +22,11 @@ async function getSnippet(trigger) { async function addSnippet(trigger, body, createdBy = 0) { if (await getSnippet(trigger)) return; - return knex('snippets').insert({ + return knex("snippets").insert({ trigger, body, created_by: createdBy, - created_at: moment.utc().format('YYYY-MM-DD HH:mm:ss') + created_at: moment.utc().format("YYYY-MM-DD HH:mm:ss") }); } @@ -35,8 +35,8 @@ async function addSnippet(trigger, body, createdBy = 0) { * @returns {Promise} */ async function deleteSnippet(trigger) { - return knex('snippets') - .where('trigger', trigger) + return knex("snippets") + .where("trigger", trigger) .delete(); } @@ -44,7 +44,7 @@ async function deleteSnippet(trigger) { * @returns {Promise} */ async function getAllSnippets() { - const snippets = await knex('snippets') + const snippets = await knex("snippets") .select(); return snippets.map(s => new Snippet(s)); diff --git a/src/data/threads.js b/src/data/threads.js index 4120489..58692f0 100644 --- a/src/data/threads.js +++ b/src/data/threads.js @@ -1,19 +1,19 @@ -const {User, Member} = require('eris'); +const {User, Member} = require("eris"); -const transliterate = require('transliteration'); -const moment = require('moment'); -const uuid = require('uuid'); -const humanizeDuration = require('humanize-duration'); +const transliterate = require("transliteration"); +const moment = require("moment"); +const uuid = require("uuid"); +const humanizeDuration = require("humanize-duration"); -const bot = require('../bot'); -const knex = require('../knex'); -const config = require('../cfg'); -const utils = require('../utils'); -const updates = require('./updates'); +const bot = require("../bot"); +const knex = require("../knex"); +const config = require("../cfg"); +const utils = require("../utils"); +const updates = require("./updates"); -const Thread = require('./Thread'); +const Thread = require("./Thread"); const {callBeforeNewThreadHooks} = require("../hooks/beforeNewThread"); -const {THREAD_STATUS, DISOCRD_CHANNEL_TYPES} = require('./constants'); +const {THREAD_STATUS, DISOCRD_CHANNEL_TYPES} = require("./constants"); const MINUTES = 60 * 1000; const HOURS = 60 * MINUTES; @@ -23,8 +23,8 @@ const HOURS = 60 * MINUTES; * @returns {Promise} */ async function findById(id) { - const thread = await knex('threads') - .where('id', id) + const thread = await knex("threads") + .where("id", id) .first(); return (thread ? new Thread(thread) : null); @@ -35,9 +35,9 @@ async function findById(id) { * @returns {Promise} */ async function findOpenThreadByUserId(userId) { - const thread = await knex('threads') - .where('user_id', userId) - .where('status', THREAD_STATUS.OPEN) + const thread = await knex("threads") + .where("user_id", userId) + .where("status", THREAD_STATUS.OPEN) .first(); return (thread ? new Thread(thread) : null); @@ -70,7 +70,7 @@ async function createNewThreadForUser(user, opts = {}) { const existingThread = await findOpenThreadByUserId(user.id); if (existingThread) { - throw new Error('Attempted to create a new thread for a user with an existing open thread!'); + throw new Error("Attempted to create a new thread for a user with an existing open thread!"); } // If set in config, check that the user's account is old enough (time since they registered on Discord) @@ -132,7 +132,7 @@ async function createNewThreadForUser(user, opts = {}) { // Use the user's name+discrim for the thread channel's name // Channel names are particularly picky about what characters they allow, so we gotta do some clean-up let cleanName = transliterate.slugify(user.username); - if (cleanName === '') cleanName = 'unknown'; + if (cleanName === "") cleanName = "unknown"; cleanName = cleanName.slice(0, 95); // Make sure the discrim fits const channelName = `${cleanName}-${user.discriminator}`; @@ -161,7 +161,7 @@ async function createNewThreadForUser(user, opts = {}) { let createdChannel; try { createdChannel = await utils.getInboxGuild().createChannel(channelName, DISOCRD_CHANNEL_TYPES.GUILD_TEXT, { - reason: 'New Modmail thread', + reason: "New Modmail thread", parentID: newThreadCategoryId, }); } catch (err) { @@ -175,7 +175,7 @@ async function createNewThreadForUser(user, opts = {}) { user_id: user.id, user_name: `${user.username}#${user.discriminator}`, channel_id: createdChannel.id, - created_at: moment.utc().format('YYYY-MM-DD HH:mm:ss') + created_at: moment.utc().format("YYYY-MM-DD HH:mm:ss") }); const newThread = await findById(newThreadId); @@ -216,7 +216,7 @@ async function createNewThreadForUser(user, opts = {}) { infoHeaderItems.push(`ID **${user.id}**`); } - let infoHeader = infoHeaderItems.join(', '); + let infoHeader = infoHeaderItems.join(", "); // Guild member info for (const [guildId, guildData] of userGuildData.entries()) { @@ -235,10 +235,10 @@ async function createNewThreadForUser(user, opts = {}) { if (config.rolesInThreadHeader && guildData.member.roles.length) { const roles = guildData.member.roles.map(roleId => guildData.guild.roles.get(roleId)).filter(Boolean); - headerItems.push(`ROLES **${roles.map(r => r.name).join(', ')}**`); + headerItems.push(`ROLES **${roles.map(r => r.name).join(", ")}**`); } - const headerStr = headerItems.join(', '); + const headerStr = headerItems.join(", "); if (mainGuilds.length === 1) { infoHeader += `\n${headerStr}`; @@ -253,7 +253,7 @@ async function createNewThreadForUser(user, opts = {}) { infoHeader += `\n\nThis user has **${userLogCount}** previous modmail threads. Use \`${config.prefix}logs\` to see them.`; } - infoHeader += '\n────────────────'; + infoHeader += "\n────────────────"; await newThread.postSystemMessage(infoHeader); @@ -280,10 +280,10 @@ async function createNewThreadForUser(user, opts = {}) { */ async function createThreadInDB(data) { const threadId = uuid.v4(); - const now = moment.utc().format('YYYY-MM-DD HH:mm:ss'); + const now = moment.utc().format("YYYY-MM-DD HH:mm:ss"); const finalData = Object.assign({created_at: now, is_legacy: 0}, data, {id: threadId}); - await knex('threads').insert(finalData); + await knex("threads").insert(finalData); return threadId; } @@ -293,8 +293,8 @@ async function createThreadInDB(data) { * @returns {Promise} */ async function findByChannelId(channelId) { - const thread = await knex('threads') - .where('channel_id', channelId) + const thread = await knex("threads") + .where("channel_id", channelId) .first(); return (thread ? new Thread(thread) : null); @@ -305,9 +305,9 @@ async function findByChannelId(channelId) { * @returns {Promise} */ async function findOpenThreadByChannelId(channelId) { - const thread = await knex('threads') - .where('channel_id', channelId) - .where('status', THREAD_STATUS.OPEN) + const thread = await knex("threads") + .where("channel_id", channelId) + .where("status", THREAD_STATUS.OPEN) .first(); return (thread ? new Thread(thread) : null); @@ -318,9 +318,9 @@ async function findOpenThreadByChannelId(channelId) { * @returns {Promise} */ async function findSuspendedThreadByChannelId(channelId) { - const thread = await knex('threads') - .where('channel_id', channelId) - .where('status', THREAD_STATUS.SUSPENDED) + const thread = await knex("threads") + .where("channel_id", channelId) + .where("status", THREAD_STATUS.SUSPENDED) .first(); return (thread ? new Thread(thread) : null); @@ -331,9 +331,9 @@ async function findSuspendedThreadByChannelId(channelId) { * @returns {Promise} */ async function getClosedThreadsByUserId(userId) { - const threads = await knex('threads') - .where('status', THREAD_STATUS.CLOSED) - .where('user_id', userId) + const threads = await knex("threads") + .where("status", THREAD_STATUS.CLOSED) + .where("user_id", userId) .select(); return threads.map(thread => new Thread(thread)); @@ -344,10 +344,10 @@ async function getClosedThreadsByUserId(userId) { * @returns {Promise} */ async function getClosedThreadCountByUserId(userId) { - const row = await knex('threads') - .where('status', THREAD_STATUS.CLOSED) - .where('user_id', userId) - .first(knex.raw('COUNT(id) AS thread_count')); + const row = await knex("threads") + .where("status", THREAD_STATUS.CLOSED) + .where("user_id", userId) + .first(knex.raw("COUNT(id) AS thread_count")); return parseInt(row.thread_count, 10); } @@ -365,24 +365,24 @@ async function findOrCreateThreadForUser(user, opts = {}) { } async function getThreadsThatShouldBeClosed() { - const now = moment.utc().format('YYYY-MM-DD HH:mm:ss'); - const threads = await knex('threads') - .where('status', THREAD_STATUS.OPEN) - .whereNotNull('scheduled_close_at') - .where('scheduled_close_at', '<=', now) - .whereNotNull('scheduled_close_at') + const now = moment.utc().format("YYYY-MM-DD HH:mm:ss"); + const threads = await knex("threads") + .where("status", THREAD_STATUS.OPEN) + .whereNotNull("scheduled_close_at") + .where("scheduled_close_at", "<=", now) + .whereNotNull("scheduled_close_at") .select(); return threads.map(thread => new Thread(thread)); } async function getThreadsThatShouldBeSuspended() { - const now = moment.utc().format('YYYY-MM-DD HH:mm:ss'); - const threads = await knex('threads') - .where('status', THREAD_STATUS.OPEN) - .whereNotNull('scheduled_suspend_at') - .where('scheduled_suspend_at', '<=', now) - .whereNotNull('scheduled_suspend_at') + const now = moment.utc().format("YYYY-MM-DD HH:mm:ss"); + const threads = await knex("threads") + .where("status", THREAD_STATUS.OPEN) + .whereNotNull("scheduled_suspend_at") + .where("scheduled_suspend_at", "<=", now) + .whereNotNull("scheduled_suspend_at") .select(); return threads.map(thread => new Thread(thread)); diff --git a/src/data/updates.js b/src/data/updates.js index b0e7285..ac83ad8 100644 --- a/src/data/updates.js +++ b/src/data/updates.js @@ -1,16 +1,16 @@ -const url = require('url'); -const https = require('https'); -const moment = require('moment'); -const knex = require('../knex'); -const config = require('../cfg'); +const url = require("url"); +const https = require("https"); +const moment = require("moment"); +const knex = require("../knex"); +const config = require("../cfg"); const UPDATE_CHECK_FREQUENCY = 12; // In hours let updateCheckPromise = null; async function initUpdatesTable() { - const row = await knex('updates').first(); + const row = await knex("updates").first(); if (! row) { - await knex('updates').insert({ + await knex("updates").insert({ available_version: null, last_checked: null, }); @@ -24,48 +24,48 @@ async function initUpdatesTable() { */ async function refreshVersions() { await initUpdatesTable(); - const { last_checked } = await knex('updates').first(); + const { last_checked } = await knex("updates").first(); // Only refresh available version if it's been more than UPDATE_CHECK_FREQUENCY since our last check - if (last_checked != null && last_checked > moment.utc().subtract(UPDATE_CHECK_FREQUENCY, 'hours').format('YYYY-MM-DD HH:mm:ss')) return; + if (last_checked != null && last_checked > moment.utc().subtract(UPDATE_CHECK_FREQUENCY, "hours").format("YYYY-MM-DD HH:mm:ss")) return; - const packageJson = require('../../package.json'); + const packageJson = require("../../package.json"); const repositoryUrl = packageJson.repository && packageJson.repository.url; if (! repositoryUrl) return; const parsedUrl = url.parse(repositoryUrl); - if (parsedUrl.hostname !== 'github.com') return; + if (parsedUrl.hostname !== "github.com") return; - const [, owner, repo] = parsedUrl.pathname.split('/'); + const [, owner, repo] = parsedUrl.pathname.split("/"); if (! owner || ! repo) return; https.get( { - hostname: 'api.github.com', + hostname: "api.github.com", path: `/repos/${owner}/${repo}/tags`, headers: { - 'User-Agent': `Modmail Bot (https://github.com/${owner}/${repo}) (${packageJson.version})` + "User-Agent": `Modmail Bot (https://github.com/${owner}/${repo}) (${packageJson.version})` } }, async res => { if (res.statusCode !== 200) { - await knex('updates').update({ - last_checked: moment.utc().format('YYYY-MM-DD HH:mm:ss') + await knex("updates").update({ + last_checked: moment.utc().format("YYYY-MM-DD HH:mm:ss") }); console.warn(`[WARN] Got status code ${res.statusCode} when checking for available updates`); return; } - let data = ''; - res.on('data', chunk => data += chunk); - res.on('end', async () => { + let data = ""; + res.on("data", chunk => data += chunk); + res.on("end", async () => { const parsed = JSON.parse(data); if (! Array.isArray(parsed) || parsed.length === 0) return; const latestVersion = parsed[0].name; - await knex('updates').update({ + await knex("updates").update({ available_version: latestVersion, - last_checked: moment.utc().format('YYYY-MM-DD HH:mm:ss') + last_checked: moment.utc().format("YYYY-MM-DD HH:mm:ss") }); }); } @@ -78,11 +78,11 @@ async function refreshVersions() { * @returns {Number} 1 if version a is larger than b, -1 is version a is smaller than b, 0 if they are equal */ function compareVersions(a, b) { - const aParts = a.split('.'); - const bParts = b.split('.'); + const aParts = a.split("."); + const bParts = b.split("."); for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) { - let aPart = parseInt((aParts[i] || '0').match(/\d+/)[0] || '0', 10); - let bPart = parseInt((bParts[i] || '0').match(/\d+/)[0] || '0', 10); + let aPart = parseInt((aParts[i] || "0").match(/\d+/)[0] || "0", 10); + let bPart = parseInt((bParts[i] || "0").match(/\d+/)[0] || "0", 10); if (aPart > bPart) return 1; if (aPart < bPart) return -1; } @@ -92,9 +92,9 @@ function compareVersions(a, b) { async function getAvailableUpdate() { await initUpdatesTable(); - const packageJson = require('../../package.json'); + const packageJson = require("../../package.json"); const currentVersion = packageJson.version; - const { available_version: availableVersion } = await knex('updates').first(); + const { available_version: availableVersion } = await knex("updates").first(); if (availableVersion == null) return null; if (currentVersion == null) return availableVersion; diff --git a/src/formatters.js b/src/formatters.js index 8733019..ea18eae 100644 --- a/src/formatters.js +++ b/src/formatters.js @@ -1,7 +1,7 @@ -const Eris = require('eris'); -const utils = require('./utils'); -const config = require('./cfg'); -const ThreadMessage = require('./data/ThreadMessage'); +const Eris = require("eris"); +const utils = require("./utils"); +const config = require("./cfg"); +const ThreadMessage = require("./data/ThreadMessage"); /** * Function to format the DM that is sent to the user when a staff member replies to them via !reply @@ -116,7 +116,7 @@ const defaultFormatters = { const mainRole = utils.getMainRole(moderator); const modName = (config.useNicknames ? moderator.nick || moderator.user.username : moderator.user.username); const modInfo = opts.isAnonymous - ? (mainRole ? mainRole.name : 'Moderator') + ? (mainRole ? mainRole.name : "Moderator") : (mainRole ? `(${mainRole.name}) ${modName}` : modName); return `**${modInfo}:** ${text}`; @@ -126,7 +126,7 @@ const defaultFormatters = { const mainRole = utils.getMainRole(moderator); const modName = (config.useNicknames ? moderator.nick || moderator.user.username : moderator.user.username); const modInfo = opts.isAnonymous - ? `(Anonymous) (${modName}) ${mainRole ? mainRole.name : 'Moderator'}` + ? `(Anonymous) (${modName}) ${mainRole ? mainRole.name : "Moderator"}` : (mainRole ? `(${mainRole.name}) ${modName}` : modName); let result = `**${modInfo}:** ${text}`; @@ -147,13 +147,13 @@ const defaultFormatters = { // Mirroring the DM formatting here... const modInfo = opts.isAnonymous - ? (mainRole ? mainRole.name : 'Moderator') + ? (mainRole ? mainRole.name : "Moderator") : (mainRole ? `(${mainRole.name}) ${modName}` : modName); let result = `**${modInfo}:** ${text}`; if (opts.attachmentLinks && opts.attachmentLinks.length) { - result += '\n'; + result += "\n"; for (const link of opts.attachmentLinks) { result += `\n**Attachment:** ${link}`; } @@ -165,8 +165,8 @@ const defaultFormatters = { }, formatUserReplyThreadMessage(user, msg, opts = {}) { - const content = (msg.content.trim() === '' && msg.embeds.length) - ? '' + const content = (msg.content.trim() === "" && msg.embeds.length) + ? "" : msg.content; let result = `**${user.username}#${user.discriminator}:** ${content}`; @@ -178,7 +178,7 @@ const defaultFormatters = { } if (config.threadTimestamps) { - const formattedTimestamp = utils.getTimestamp(msg.timestamp, 'x'); + const formattedTimestamp = utils.getTimestamp(msg.timestamp, "x"); result = `[${formattedTimestamp}] ${result}`; } @@ -186,8 +186,8 @@ const defaultFormatters = { }, formatUserReplyLogMessage(user, msg, opts = {}) { - const content = (msg.content.trim() === '' && msg.embeds.length) - ? '' + const content = (msg.content.trim() === "" && msg.embeds.length) + ? "" : msg.content; let result = content; diff --git a/src/hooks/beforeNewThread.js b/src/hooks/beforeNewThread.js index 801d84d..9805843 100644 --- a/src/hooks/beforeNewThread.js +++ b/src/hooks/beforeNewThread.js @@ -1,4 +1,4 @@ -const Eris = require('eris'); +const Eris = require("eris"); /** * @callback BeforeNewThreadHook_SetCategoryId diff --git a/src/index.js b/src/index.js index 7c5152e..588111a 100644 --- a/src/index.js +++ b/src/index.js @@ -1,25 +1,25 @@ // Verify NodeJS version -const nodeMajorVersion = parseInt(process.versions.node.split('.')[0], 10); +const nodeMajorVersion = parseInt(process.versions.node.split(".")[0], 10); if (nodeMajorVersion < 11) { - console.error('Unsupported NodeJS version! Please install Node.js 11, 12, 13, or 14.'); + console.error("Unsupported NodeJS version! Please install Node.js 11, 12, 13, or 14."); process.exit(1); } // Verify node modules have been installed -const fs = require('fs'); -const path = require('path'); +const fs = require("fs"); +const path = require("path"); try { - fs.accessSync(path.join(__dirname, '..', 'node_modules')); + fs.accessSync(path.join(__dirname, "..", "node_modules")); } catch (e) { - console.error('Please run "npm ci" before starting the bot'); + console.error("Please run \"npm ci\" before starting the bot"); process.exit(1); } // Error handling -process.on('uncaughtException', err => { +process.on("uncaughtException", err => { // Unknown message types (nitro boosting messages at the time) should be safe to ignore - if (err && err.message && err.message.startsWith('Unhandled MESSAGE_CREATE type')) { + if (err && err.message && err.message.startsWith("Unhandled MESSAGE_CREATE type")) { return; } @@ -28,27 +28,27 @@ process.on('uncaughtException', err => { process.exit(1); }); -let testedPackage = ''; +let testedPackage = ""; try { - const packageJson = require('../package.json'); + const packageJson = require("../package.json"); const modules = Object.keys(packageJson.dependencies); modules.forEach(mod => { testedPackage = mod; - fs.accessSync(path.join(__dirname, '..', 'node_modules', mod)) + fs.accessSync(path.join(__dirname, "..", "node_modules", mod)) }); } catch (e) { console.error(`Please run "npm ci" again! Package "${testedPackage}" is missing.`); process.exit(1); } -const config = require('./cfg'); -const utils = require('./utils'); -const main = require('./main'); -const knex = require('./knex'); -const legacyMigrator = require('./legacy/legacyMigrator'); +const config = require("./cfg"); +const utils = require("./utils"); +const main = require("./main"); +const knex = require("./knex"); +const legacyMigrator = require("./legacy/legacyMigrator"); // Force crash on unhandled rejections (use something like forever/pm2 to restart) -process.on('unhandledRejection', err => { +process.on("unhandledRejection", err => { if (err instanceof utils.BotError || (err && err.code)) { // We ignore stack traces for BotErrors (the message has enough info) and network errors from Eris (their stack traces are unreadably long) console.error(`Error: ${err.message}`); @@ -63,34 +63,34 @@ process.on('unhandledRejection', err => { // Make sure the database is up to date const [completed, newMigrations] = await knex.migrate.list(); if (newMigrations.length > 0) { - console.log('Updating database. This can take a while. Don\'t close the bot!'); + console.log("Updating database. This can take a while. Don't close the bot!"); await knex.migrate.latest(); - console.log('Done!'); + console.log("Done!"); } // Migrate legacy data if we need to if (await legacyMigrator.shouldMigrate()) { - console.log('=== MIGRATING LEGACY DATA ==='); - console.log('Do not close the bot!'); - console.log(''); + console.log("=== MIGRATING LEGACY DATA ==="); + console.log("Do not close the bot!"); + console.log(""); await legacyMigrator.migrate(); const relativeDbDir = (path.isAbsolute(config.dbDir) ? config.dbDir : path.resolve(process.cwd(), config.dbDir)); const relativeLogDir = (path.isAbsolute(config.logDir) ? config.logDir : path.resolve(process.cwd(), config.logDir)); - console.log(''); - console.log('=== LEGACY DATA MIGRATION FINISHED ==='); - console.log(''); - console.log('IMPORTANT: After the bot starts, please verify that all logs, threads, blocked users, and snippets are still working correctly.'); - console.log('Once you\'ve done that, the following files/directories are no longer needed. I would recommend keeping a backup of them, however.'); - console.log(''); - console.log('FILE: ' + path.resolve(relativeDbDir, 'threads.json')); - console.log('FILE: ' + path.resolve(relativeDbDir, 'blocked.json')); - console.log('FILE: ' + path.resolve(relativeDbDir, 'snippets.json')); - console.log('DIRECTORY: ' + relativeLogDir); - console.log(''); - console.log('Starting the bot...'); + console.log(""); + console.log("=== LEGACY DATA MIGRATION FINISHED ==="); + console.log(""); + console.log("IMPORTANT: After the bot starts, please verify that all logs, threads, blocked users, and snippets are still working correctly."); + console.log("Once you've done that, the following files/directories are no longer needed. I would recommend keeping a backup of them, however."); + console.log(""); + console.log("FILE: " + path.resolve(relativeDbDir, "threads.json")); + console.log("FILE: " + path.resolve(relativeDbDir, "blocked.json")); + console.log("FILE: " + path.resolve(relativeDbDir, "snippets.json")); + console.log("DIRECTORY: " + relativeLogDir); + console.log(""); + console.log("Starting the bot..."); } // Start the bot diff --git a/src/knex.js b/src/knex.js index ce33d98..35c8cb5 100644 --- a/src/knex.js +++ b/src/knex.js @@ -1,2 +1,2 @@ -const config = require('./cfg'); -module.exports = require('knex')(config.knex); +const config = require("./cfg"); +module.exports = require("knex")(config.knex); diff --git a/src/legacy/jsonDb.js b/src/legacy/jsonDb.js index 9b97c05..1c534e9 100644 --- a/src/legacy/jsonDb.js +++ b/src/legacy/jsonDb.js @@ -1,6 +1,6 @@ -const fs = require('fs'); -const path = require('path'); -const config = require('../cfg'); +const fs = require("fs"); +const path = require("path"); +const config = require("../cfg"); const dbDir = config.dbDir; @@ -15,7 +15,7 @@ class JSONDB { this.useCloneByDefault = useCloneByDefault; this.load = new Promise(resolve => { - fs.readFile(path, {encoding: 'utf8'}, (err, data) => { + fs.readFile(path, {encoding: "utf8"}, (err, data) => { if (err) return resolve(def); let unserialized; @@ -39,7 +39,7 @@ class JSONDB { save(newData) { const serialized = JSON.stringify(newData); this.load = new Promise((resolve, reject) => { - fs.writeFile(this.path, serialized, {encoding: 'utf8'}, () => { + fs.writeFile(this.path, serialized, {encoding: "utf8"}, () => { resolve(newData); }); }); diff --git a/src/legacy/legacyMigrator.js b/src/legacy/legacyMigrator.js index bf3f947..18720ec 100644 --- a/src/legacy/legacyMigrator.js +++ b/src/legacy/legacyMigrator.js @@ -1,15 +1,15 @@ -const fs = require('fs'); -const path = require('path'); -const promisify = require('util').promisify; -const moment = require('moment'); -const Eris = require('eris'); +const fs = require("fs"); +const path = require("path"); +const promisify = require("util").promisify; +const moment = require("moment"); +const Eris = require("eris"); -const knex = require('../knex'); -const config = require('../cfg'); -const jsonDb = require('./jsonDb'); -const threads = require('../data/threads'); +const knex = require("../knex"); +const config = require("../cfg"); +const jsonDb = require("./jsonDb"); +const threads = require("../data/threads"); -const {THREAD_STATUS, THREAD_MESSAGE_TYPE} = require('../data/constants'); +const {THREAD_STATUS, THREAD_MESSAGE_TYPE} = require("../data/constants"); const readDir = promisify(fs.readdir); const readFile = promisify(fs.readFile); @@ -17,43 +17,43 @@ const access = promisify(fs.access); const writeFile = promisify(fs.writeFile); async function migrate() { - console.log('Migrating open threads...'); + console.log("Migrating open threads..."); await migrateOpenThreads(); - console.log('Migrating logs...'); + console.log("Migrating logs..."); await migrateLogs(); - console.log('Migrating blocked users...'); + console.log("Migrating blocked users..."); await migrateBlockedUsers(); - console.log('Migrating snippets...'); + console.log("Migrating snippets..."); await migrateSnippets(); - await writeFile(path.join(config.dbDir, '.migrated_legacy'), ''); + await writeFile(path.join(config.dbDir, ".migrated_legacy"), ""); } async function shouldMigrate() { // If there is a file marking a finished migration, assume we don't need to migrate - const migrationFile = path.join(config.dbDir, '.migrated_legacy'); + const migrationFile = path.join(config.dbDir, ".migrated_legacy"); try { await access(migrationFile); return false; } catch (e) {} // If there are any old threads, we need to migrate - const oldThreads = await jsonDb.get('threads', []); + const oldThreads = await jsonDb.get("threads", []); if (oldThreads.length) { return true; } // If there are any old blocked users, we need to migrate - const blockedUsers = await jsonDb.get('blocked', []); + const blockedUsers = await jsonDb.get("blocked", []); if (blockedUsers.length) { return true; } // If there are any old snippets, we need to migrate - const snippets = await jsonDb.get('snippets', {}); + const snippets = await jsonDb.get("snippets", {}); if (Object.keys(snippets).length) { return true; } @@ -71,12 +71,12 @@ async function migrateOpenThreads() { const bot = new Eris.Client(config.token); const toReturn = new Promise(resolve => { - bot.on('ready', async () => { - const oldThreads = await jsonDb.get('threads', []); + bot.on("ready", async () => { + const oldThreads = await jsonDb.get("threads", []); const promises = oldThreads.map(async oldThread => { - const existingOpenThread = await knex('threads') - .where('channel_id', oldThread.channelId) + const existingOpenThread = await knex("threads") + .where("channel_id", oldThread.channelId) .first(); if (existingOpenThread) return; @@ -86,9 +86,9 @@ async function migrateOpenThreads() { const threadMessages = await oldChannel.getMessages(1000); const log = threadMessages.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}`; - }).join('\n') + '\n'; + }).join("\n") + "\n"; const newThread = { status: THREAD_STATUS.OPEN, @@ -100,14 +100,14 @@ async function migrateOpenThreads() { const threadId = await threads.createThreadInDB(newThread); - await knex('thread_messages').insert({ + await knex("thread_messages").insert({ thread_id: threadId, message_type: THREAD_MESSAGE_TYPE.LEGACY, user_id: oldThread.userId, - user_name: '', + user_name: "", body: log, is_anonymous: 0, - created_at: moment.utc().format('YYYY-MM-DD HH:mm:ss') + created_at: moment.utc().format("YYYY-MM-DD HH:mm:ss") }); }); @@ -128,38 +128,38 @@ async function migrateLogs() { for (let i = 0; i < logFiles.length; i++) { const logFile = logFiles[i]; - if (! logFile.endsWith('.txt')) continue; + if (! logFile.endsWith(".txt")) continue; - const [rawDate, userId, threadId] = logFile.slice(0, -4).split('__'); - const date = `${rawDate.slice(0, 10)} ${rawDate.slice(11).replace('-', ':')}`; + const [rawDate, userId, threadId] = logFile.slice(0, -4).split("__"); + const date = `${rawDate.slice(0, 10)} ${rawDate.slice(11).replace("-", ":")}`; const fullPath = path.join(logDir, logFile); - const contents = await readFile(fullPath, {encoding: 'utf8'}); + const contents = await readFile(fullPath, {encoding: "utf8"}); const newThread = { id: threadId, status: THREAD_STATUS.CLOSED, user_id: userId, - user_name: '', + user_name: "", channel_id: null, is_legacy: 1, created_at: date }; await knex.transaction(async trx => { - const existingThread = await trx('threads') - .where('id', newThread.id) + const existingThread = await trx("threads") + .where("id", newThread.id) .first(); if (existingThread) return; - await trx('threads').insert(newThread); + await trx("threads").insert(newThread); - await trx('thread_messages').insert({ + await trx("thread_messages").insert({ thread_id: newThread.id, message_type: THREAD_MESSAGE_TYPE.LEGACY, user_id: userId, - user_name: '', + user_name: "", body: contents, is_anonymous: 0, created_at: date @@ -174,19 +174,19 @@ async function migrateLogs() { } async function migrateBlockedUsers() { - const now = moment.utc().format('YYYY-MM-DD HH:mm:ss'); - const blockedUsers = await jsonDb.get('blocked', []); + const now = moment.utc().format("YYYY-MM-DD HH:mm:ss"); + const blockedUsers = await jsonDb.get("blocked", []); for (const userId of blockedUsers) { - const existingBlockedUser = await knex('blocked_users') - .where('user_id', userId) + const existingBlockedUser = await knex("blocked_users") + .where("user_id", userId) .first(); if (existingBlockedUser) return; - await knex('blocked_users').insert({ + await knex("blocked_users").insert({ user_id: userId, - user_name: '', + user_name: "", blocked_by: null, blocked_at: now }); @@ -194,17 +194,17 @@ async function migrateBlockedUsers() { } async function migrateSnippets() { - const now = moment.utc().format('YYYY-MM-DD HH:mm:ss'); - const snippets = await jsonDb.get('snippets', {}); + const now = moment.utc().format("YYYY-MM-DD HH:mm:ss"); + const snippets = await jsonDb.get("snippets", {}); const promises = Object.entries(snippets).map(async ([trigger, data]) => { - const existingSnippet = await knex('snippets') - .where('trigger', trigger) + const existingSnippet = await knex("snippets") + .where("trigger", trigger) .first(); if (existingSnippet) return; - return knex('snippets').insert({ + return knex("snippets").insert({ trigger, body: data.text, is_anonymous: data.isAnonymous ? 1 : 0, diff --git a/src/main.js b/src/main.js index b9a319c..9f0ff4c 100644 --- a/src/main.js +++ b/src/main.js @@ -1,58 +1,58 @@ -const Eris = require('eris'); -const path = require('path'); +const Eris = require("eris"); +const path = require("path"); -const config = require('./cfg'); -const bot = require('./bot'); -const knex = require('./knex'); -const {messageQueue} = require('./queue'); -const utils = require('./utils'); -const { createCommandManager } = require('./commands'); -const { getPluginAPI, loadPlugin } = require('./plugins'); -const { callBeforeNewThreadHooks } = require('./hooks/beforeNewThread'); +const config = require("./cfg"); +const bot = require("./bot"); +const knex = require("./knex"); +const {messageQueue} = require("./queue"); +const utils = require("./utils"); +const { createCommandManager } = require("./commands"); +const { getPluginAPI, loadPlugin } = require("./plugins"); +const { callBeforeNewThreadHooks } = require("./hooks/beforeNewThread"); -const blocked = require('./data/blocked'); -const threads = require('./data/threads'); -const updates = require('./data/updates'); +const blocked = require("./data/blocked"); +const threads = require("./data/threads"); +const updates = require("./data/updates"); -const reply = require('./modules/reply'); -const close = require('./modules/close'); -const snippets = require('./modules/snippets'); -const logs = require('./modules/logs'); -const move = require('./modules/move'); -const block = require('./modules/block'); -const suspend = require('./modules/suspend'); -const webserver = require('./modules/webserver'); -const greeting = require('./modules/greeting'); -const typingProxy = require('./modules/typingProxy'); -const version = require('./modules/version'); -const newthread = require('./modules/newthread'); -const idModule = require('./modules/id'); -const alert = require('./modules/alert'); +const reply = require("./modules/reply"); +const close = require("./modules/close"); +const snippets = require("./modules/snippets"); +const logs = require("./modules/logs"); +const move = require("./modules/move"); +const block = require("./modules/block"); +const suspend = require("./modules/suspend"); +const webserver = require("./modules/webserver"); +const greeting = require("./modules/greeting"); +const typingProxy = require("./modules/typingProxy"); +const version = require("./modules/version"); +const newthread = require("./modules/newthread"); +const idModule = require("./modules/id"); +const alert = require("./modules/alert"); -const {ACCIDENTAL_THREAD_MESSAGES} = require('./data/constants'); +const {ACCIDENTAL_THREAD_MESSAGES} = require("./data/constants"); module.exports = { async start() { - console.log('Connecting to Discord...'); + console.log("Connecting to Discord..."); - bot.once('ready', async () => { - console.log('Connected! Waiting for guilds to become available...'); + bot.once("ready", async () => { + console.log("Connected! Waiting for guilds to become available..."); await Promise.all([ ...config.mainGuildId.map(id => waitForGuild(id)), waitForGuild(config.mailGuildId) ]); - console.log('Initializing...'); + console.log("Initializing..."); initStatus(); initBaseMessageHandlers(); - console.log('Loading plugins...'); + console.log("Loading plugins..."); const pluginResult = await initPlugins(); console.log(`Loaded ${pluginResult.loadedCount} plugins (${pluginResult.builtInCount} built-in plugins, ${pluginResult.externalCount} external plugins)`); - console.log(''); - console.log('Done! Now listening to DMs.'); - console.log(''); + console.log(""); + console.log("Done! Now listening to DMs."); + console.log(""); }); bot.connect(); @@ -65,7 +65,7 @@ function waitForGuild(guildId) { } return new Promise(resolve => { - bot.on('guildAvailable', guild => { + bot.on("guildAvailable", guild => { if (guild.id === guildId) { resolve(); } @@ -89,7 +89,7 @@ function initBaseMessageHandlers() { * 1) If alwaysReply is enabled, reply to the user * 2) If alwaysReply is disabled, save that message as a chat message in the thread */ - bot.on('messageCreate', async msg => { + bot.on("messageCreate", async msg => { if (! utils.messageIsOnInboxServer(msg)) return; if (msg.author.bot) return; @@ -116,7 +116,7 @@ function initBaseMessageHandlers() { * 1) Find the open modmail thread for this user, or create a new one * 2) Post the message as a user reply in the thread */ - bot.on('messageCreate', async msg => { + bot.on("messageCreate", async msg => { if (! (msg.channel instanceof Eris.PrivateChannel)) return; if (msg.author.bot) return; if (msg.type !== 0) return; // Ignore pins etc. @@ -133,7 +133,7 @@ function initBaseMessageHandlers() { if (config.ignoreAccidentalThreads && msg.content && ACCIDENTAL_THREAD_MESSAGES.includes(msg.content.trim().toLowerCase())) return; thread = await threads.createNewThreadForUser(msg.author, { - source: 'dm', + source: "dm", }); } @@ -148,13 +148,13 @@ function initBaseMessageHandlers() { * 1) If that message was in DMs, and we have a thread open with that user, post the edit as a system message in the thread * 2) If that message was moderator chatter in the thread, update the corresponding chat message in the DB */ - bot.on('messageUpdate', async (msg, oldMessage) => { + bot.on("messageUpdate", async (msg, oldMessage) => { if (! msg || ! msg.author) return; if (msg.author.bot) return; if (await blocked.isBlocked(msg.author.id)) return; // Old message content doesn't persist between bot restarts - const oldContent = oldMessage && oldMessage.content || '*Unavailable due to bot restart*'; + const oldContent = oldMessage && oldMessage.content || "*Unavailable due to bot restart*"; const newContent = msg.content; // Ignore edit events with changes only in embeds etc. @@ -181,7 +181,7 @@ function initBaseMessageHandlers() { /** * When a staff message is deleted in a modmail thread, delete it from the database as well */ - bot.on('messageDelete', async msg => { + bot.on("messageDelete", async msg => { if (! msg.author) return; if (msg.author.bot) return; if (! utils.messageIsOnInboxServer(msg)) return; @@ -196,7 +196,7 @@ function initBaseMessageHandlers() { /** * When the bot is mentioned on the main server, ping staff in the log channel about it */ - bot.on('messageCreate', async msg => { + bot.on("messageCreate", async msg => { if (! utils.messageIsOnMainServer(msg)) return; if (! msg.mentions.some(user => user.id === bot.user.id)) return; if (msg.author.bot) return; @@ -215,7 +215,7 @@ function initBaseMessageHandlers() { let content; const mainGuilds = utils.getMainGuilds(); - const staffMention = (config.pingOnBotMention ? utils.getInboxMention() : ''); + const staffMention = (config.pingOnBotMention ? utils.getInboxMention() : ""); if (mainGuilds.length === 1) { content = `${staffMention}Bot mentioned in ${msg.channel.mention} by **${msg.author.username}#${msg.author.discriminator}(${msg.author.id})**: "${msg.cleanContent}"\n\n`; diff --git a/src/modules/alert.js b/src/modules/alert.js index a844230..6f8f1ba 100644 --- a/src/modules/alert.js +++ b/src/modules/alert.js @@ -1,8 +1,8 @@ module.exports = ({ bot, knex, config, commands }) => { - commands.addInboxThreadCommand('alert', '[opt:string]', async (msg, args, thread) => { - if (args.opt && args.opt.startsWith('c')) { + commands.addInboxThreadCommand("alert", "[opt:string]", async (msg, args, thread) => { + if (args.opt && args.opt.startsWith("c")) { await thread.setAlert(null); - await thread.postSystemMessage(`Cancelled new message alert`); + await thread.postSystemMessage("Cancelled new message alert"); } else { await thread.setAlert(msg.author.id); await thread.postSystemMessage(`Pinging ${msg.author.username}#${msg.author.discriminator} when this thread gets a new reply`); diff --git a/src/modules/block.js b/src/modules/block.js index 94913c4..5eea8fd 100644 --- a/src/modules/block.js +++ b/src/modules/block.js @@ -1,5 +1,5 @@ -const humanizeDuration = require('humanize-duration'); -const moment = require('moment'); +const humanizeDuration = require("humanize-duration"); +const moment = require("moment"); const blocked = require("../data/blocked"); const utils = require("../utils"); @@ -23,7 +23,7 @@ module.exports = ({ bot, knex, config, commands }) => { setTimeout(expiredBlockLoop, 2000); } - bot.on('ready', expiredBlockLoop); + bot.on("ready", expiredBlockLoop); const blockCmd = async (msg, args, thread) => { const userIdToBlock = args.userId || (thread && thread.user_id); @@ -31,16 +31,16 @@ module.exports = ({ bot, knex, config, commands }) => { const isBlocked = await blocked.isBlocked(userIdToBlock); if (isBlocked) { - msg.channel.createMessage('User is already blocked'); + msg.channel.createMessage("User is already blocked"); return; } const expiresAt = args.blockTime - ? moment.utc().add(args.blockTime, 'ms').format('YYYY-MM-DD HH:mm:ss') + ? moment.utc().add(args.blockTime, "ms").format("YYYY-MM-DD HH:mm:ss") : null; const user = bot.users.get(userIdToBlock); - await blocked.block(userIdToBlock, (user ? `${user.username}#${user.discriminator}` : ''), msg.author.id, expiresAt); + await blocked.block(userIdToBlock, (user ? `${user.username}#${user.discriminator}` : ""), msg.author.id, expiresAt); if (expiresAt) { const humanized = humanizeDuration(args.blockTime, { largest: 2, round: true }); @@ -50,8 +50,8 @@ module.exports = ({ bot, knex, config, commands }) => { } }; - commands.addInboxServerCommand('block', ' [blockTime:delay]', blockCmd); - commands.addInboxServerCommand('block', '[blockTime:delay]', blockCmd); + commands.addInboxServerCommand("block", " [blockTime:delay]", blockCmd); + commands.addInboxServerCommand("block", "[blockTime:delay]", blockCmd); const unblockCmd = async (msg, args, thread) => { const userIdToUnblock = args.userId || (thread && thread.user_id); @@ -59,12 +59,12 @@ module.exports = ({ bot, knex, config, commands }) => { const isBlocked = await blocked.isBlocked(userIdToUnblock); if (! isBlocked) { - msg.channel.createMessage('User is not blocked'); + msg.channel.createMessage("User is not blocked"); return; } const unblockAt = args.unblockDelay - ? moment.utc().add(args.unblockDelay, 'ms').format('YYYY-MM-DD HH:mm:ss') + ? moment.utc().add(args.unblockDelay, "ms").format("YYYY-MM-DD HH:mm:ss") : null; const user = bot.users.get(userIdToUnblock); @@ -78,10 +78,10 @@ module.exports = ({ bot, knex, config, commands }) => { } }; - commands.addInboxServerCommand('unblock', ' [unblockDelay:delay]', unblockCmd); - commands.addInboxServerCommand('unblock', '[unblockDelay:delay]', unblockCmd); + commands.addInboxServerCommand("unblock", " [unblockDelay:delay]", unblockCmd); + commands.addInboxServerCommand("unblock", "[unblockDelay:delay]", unblockCmd); - commands.addInboxServerCommand('is_blocked', '[userId:userId]',async (msg, args, thread) => { + commands.addInboxServerCommand("is_blocked", "[userId:userId]",async (msg, args, thread) => { const userIdToCheck = args.userId || (thread && thread.user_id); if (! userIdToCheck) return; diff --git a/src/modules/close.js b/src/modules/close.js index b3173ac..19679aa 100644 --- a/src/modules/close.js +++ b/src/modules/close.js @@ -1,10 +1,10 @@ -const moment = require('moment'); -const Eris = require('eris'); -const config = require('../cfg'); -const utils = require('../utils'); -const threads = require('../data/threads'); -const blocked = require('../data/blocked'); -const {messageQueue} = require('../queue'); +const moment = require("moment"); +const Eris = require("eris"); +const config = require("../cfg"); +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 @@ -39,7 +39,7 @@ module.exports = ({ bot, knex, config, commands }) => { 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) => { + commands.addGlobalCommand("close", "[opts...]", async (msg, args) => { let thread, closedBy; let hasCloseMessage = !! config.closeMessage; @@ -56,11 +56,11 @@ module.exports = ({ bot, knex, config, commands }) => { // 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...'); + thread.postSystemMessage("Thread closed by user, closing..."); await thread.close(true); }); - closedBy = 'the user'; + closedBy = "the user"; } else { // A staff member is closing the thread if (! utils.messageIsOnInboxServer(msg)) return; @@ -70,18 +70,18 @@ module.exports = ({ bot, knex, config, commands }) => { if (! thread) return; if (args.opts && args.opts.length) { - if (args.opts.includes('cancel') || args.opts.includes('c')) { + 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`); + thread.postSystemMessage("Cancelled scheduled closing"); } return; } // Silent close (= no close message) - if (args.opts.includes('silent') || args.opts.includes('s')) { + if (args.opts.includes("silent") || args.opts.includes("s")) { silentClose = true; } @@ -90,12 +90,12 @@ module.exports = ({ bot, knex, config, commands }) => { if (delayStringArg) { const delay = utils.convertDelayStringToMS(delayStringArg); if (delay === 0 || delay === null) { - thread.postSystemMessage(`Invalid delay specified. Format: "1h30m"`); + 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); + 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) { @@ -129,7 +129,7 @@ module.exports = ({ bot, knex, config, commands }) => { }); // Auto-close threads if their channel is deleted - bot.on('channelDelete', async (channel) => { + bot.on("channelDelete", async (channel) => { if (! (channel instanceof Eris.TextChannel)) return; if (channel.guild.id !== utils.getInboxGuild().id) return; diff --git a/src/modules/greeting.js b/src/modules/greeting.js index 1d1d7cb..37a2620 100644 --- a/src/modules/greeting.js +++ b/src/modules/greeting.js @@ -1,12 +1,12 @@ -const path = require('path'); -const fs = require('fs'); -const config = require('../cfg'); -const utils = require('../utils'); +const path = require("path"); +const fs = require("fs"); +const config = require("../cfg"); +const utils = require("../utils"); module.exports = ({ bot }) => { if (! config.enableGreeting) return; - bot.on('guildMemberAdd', (guild, member) => { + bot.on("guildMemberAdd", (guild, member) => { const guildGreeting = config.guildGreetings[guild.id]; if (! guildGreeting || (! guildGreeting.message && ! guildGreeting.attachment)) return; @@ -14,7 +14,7 @@ module.exports = ({ bot }) => { bot.getDMChannel(member.id).then(channel => { if (! channel) return; - channel.createMessage(message || '', file) + channel.createMessage(message || "", file) .catch(e => { if (e.code === 50007) return; throw e; diff --git a/src/modules/id.js b/src/modules/id.js index 18fecdf..ceced1b 100644 --- a/src/modules/id.js +++ b/src/modules/id.js @@ -1,9 +1,9 @@ module.exports = ({ bot, knex, config, commands }) => { - commands.addInboxThreadCommand('id', [], async (msg, args, thread) => { + commands.addInboxThreadCommand("id", [], async (msg, args, thread) => { thread.postSystemMessage(thread.user_id); }); - commands.addInboxThreadCommand('dm_channel_id', [], async (msg, args, thread) => { + commands.addInboxThreadCommand("dm_channel_id", [], async (msg, args, thread) => { const dmChannel = await thread.getDMChannel(); thread.postSystemMessage(dmChannel.id); }); diff --git a/src/modules/logs.js b/src/modules/logs.js index 1559d49..61b7729 100644 --- a/src/modules/logs.js +++ b/src/modules/logs.js @@ -1,5 +1,5 @@ const threads = require("../data/threads"); -const moment = require('moment'); +const moment = require("moment"); const utils = require("../utils"); const LOG_LINES_PER_PAGE = 10; @@ -30,7 +30,7 @@ module.exports = ({ bot, knex, config, commands }) => { const threadLines = await Promise.all(userThreads.map(async thread => { const logUrl = await thread.getLogUrl(); - const formattedDate = moment.utc(thread.created_at).format('MMM Do [at] HH:mm [UTC]'); + const formattedDate = moment.utc(thread.created_at).format("MMM Do [at] HH:mm [UTC]"); return `\`${formattedDate}\`: <${logUrl}>`; })); @@ -38,26 +38,26 @@ module.exports = ({ bot, knex, config, commands }) => { ? `**Log files for <@${userId}>** (page **${page}/${maxPage}**, showing logs **${start + 1}-${end}/${totalUserThreads}**):` : `**Log files for <@${userId}>:**`; - message += `\n${threadLines.join('\n')}`; + message += `\n${threadLines.join("\n")}`; if (isPaginated) { - message += `\nTo view more, add a page number to the end of the command`; + message += "\nTo view more, add a page number to the end of the command"; } // Send the list of logs in chunks of 15 lines per message - const lines = message.split('\n'); + const lines = message.split("\n"); const chunks = utils.chunk(lines, 15); let root = Promise.resolve(); chunks.forEach(lines => { - root = root.then(() => msg.channel.createMessage(lines.join('\n'))); + root = root.then(() => msg.channel.createMessage(lines.join("\n"))); }); }; - commands.addInboxServerCommand('logs', ' [page:number]', logsCmd); - commands.addInboxServerCommand('logs', '[page:number]', logsCmd); + commands.addInboxServerCommand("logs", " [page:number]", logsCmd); + commands.addInboxServerCommand("logs", "[page:number]", logsCmd); - commands.addInboxServerCommand('loglink', [], async (msg, args, thread) => { + commands.addInboxServerCommand("loglink", [], async (msg, args, thread) => { if (! thread) { thread = await threads.findSuspendedThreadByChannelId(msg.channel.id); if (! thread) return; @@ -65,21 +65,21 @@ module.exports = ({ bot, knex, config, commands }) => { const logUrl = await thread.getLogUrl(); const query = []; - if (args.verbose) query.push('verbose=1'); - if (args.simple) query.push('simple=1'); - let qs = query.length ? `?${query.join('&')}` : ''; + if (args.verbose) query.push("verbose=1"); + if (args.simple) query.push("simple=1"); + let qs = query.length ? `?${query.join("&")}` : ""; thread.postSystemMessage(`Log URL: ${logUrl}${qs}`); }, { options: [ { - name: 'verbose', - shortcut: 'v', + name: "verbose", + shortcut: "v", isSwitch: true, }, { - name: 'simple', - shortcut: 's', + name: "simple", + shortcut: "s", isSwitch: true, }, ], diff --git a/src/modules/move.js b/src/modules/move.js index 44eefd3..0d4adcc 100644 --- a/src/modules/move.js +++ b/src/modules/move.js @@ -1,12 +1,12 @@ -const config = require('../cfg'); -const Eris = require('eris'); +const config = require("../cfg"); +const Eris = require("eris"); const transliterate = require("transliteration"); -const erisEndpoints = require('eris/lib/rest/Endpoints'); +const erisEndpoints = require("eris/lib/rest/Endpoints"); module.exports = ({ bot, knex, config, commands }) => { if (! config.allowMove) return; - commands.addInboxThreadCommand('move', '', async (msg, args, thread) => { + commands.addInboxThreadCommand("move", "", async (msg, args, thread) => { const searchStr = args.category; const normalizedSearchStr = transliterate.slugify(searchStr); @@ -41,7 +41,7 @@ module.exports = ({ bot, knex, config, commands }) => { }); if (containsRankings[0][1] === 0) { - thread.postSystemMessage('No matching category'); + thread.postSystemMessage("No matching category"); return; } diff --git a/src/modules/newthread.js b/src/modules/newthread.js index 834ba55..22bae22 100644 --- a/src/modules/newthread.js +++ b/src/modules/newthread.js @@ -2,10 +2,10 @@ const utils = require("../utils"); const threads = require("../data/threads"); module.exports = ({ bot, knex, config, commands }) => { - commands.addInboxServerCommand('newthread', '', async (msg, args, thread) => { + commands.addInboxServerCommand("newthread", "", async (msg, args, thread) => { const user = bot.users.get(args.userId); if (! user) { - utils.postSystemMessageWithFallback(msg.channel, thread, 'User not found!'); + utils.postSystemMessageWithFallback(msg.channel, thread, "User not found!"); return; } @@ -18,7 +18,7 @@ module.exports = ({ bot, knex, config, commands }) => { const createdThread = await threads.createNewThreadForUser(user, { quiet: true, ignoreRequirements: true, - source: 'command', + source: "command", }); createdThread.postSystemMessage(`Thread was opened by ${msg.author.username}#${msg.author.discriminator}`); diff --git a/src/modules/reply.js b/src/modules/reply.js index cd0bef2..146469e 100644 --- a/src/modules/reply.js +++ b/src/modules/reply.js @@ -1,71 +1,71 @@ const attachments = require("../data/attachments"); -const utils = require('../utils'); -const config = require('../cfg'); -const Thread = require('../data/Thread'); +const utils = require("../utils"); +const config = require("../cfg"); +const Thread = require("../data/Thread"); module.exports = ({ bot, knex, config, commands }) => { // 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 - commands.addInboxThreadCommand('reply', '[text$]', async (msg, args, thread) => { + commands.addInboxThreadCommand("reply", "[text$]", async (msg, args, thread) => { if (! args.text && msg.attachments.length === 0) { - utils.postError(msg.channel, 'Text or attachment required'); + utils.postError(msg.channel, "Text or attachment required"); return; } - const replied = await thread.replyToUser(msg.member, args.text || '', msg.attachments, false); + const replied = await thread.replyToUser(msg.member, args.text || "", msg.attachments, false); if (replied) msg.delete(); }, { - aliases: ['r'] + aliases: ["r"] }); // Anonymous replies only show the role, not the username - commands.addInboxThreadCommand('anonreply', '[text$]', async (msg, args, thread) => { + commands.addInboxThreadCommand("anonreply", "[text$]", async (msg, args, thread) => { if (! args.text && msg.attachments.length === 0) { - utils.postError(msg.channel, 'Text or attachment required'); + utils.postError(msg.channel, "Text or attachment required"); return; } - const replied = await thread.replyToUser(msg.member, args.text || '', msg.attachments, true); + const replied = await thread.replyToUser(msg.member, args.text || "", msg.attachments, true); if (replied) msg.delete(); }, { - aliases: ['ar'] + aliases: ["ar"] }); if (config.allowStaffEdit) { - commands.addInboxThreadCommand('edit', ' ', async (msg, args, thread) => { + commands.addInboxThreadCommand("edit", " ", async (msg, args, thread) => { const threadMessage = await thread.findThreadMessageByMessageNumber(args.messageNumber); if (! threadMessage) { - utils.postError(msg.channel, 'Unknown message number'); + utils.postError(msg.channel, "Unknown message number"); return; } if (threadMessage.user_id !== msg.author.id) { - utils.postError(msg.channel, 'You can only edit your own replies'); + utils.postError(msg.channel, "You can only edit your own replies"); return; } await thread.editStaffReply(msg.member, threadMessage, args.text) }, { - aliases: ['e'] + aliases: ["e"] }); } if (config.allowStaffDelete) { - commands.addInboxThreadCommand('delete', '', async (msg, args, thread) => { + commands.addInboxThreadCommand("delete", "", async (msg, args, thread) => { const threadMessage = await thread.findThreadMessageByMessageNumber(args.messageNumber); if (! threadMessage) { - utils.postError(msg.channel, 'Unknown message number'); + utils.postError(msg.channel, "Unknown message number"); return; } if (threadMessage.user_id !== msg.author.id) { - utils.postError(msg.channel, 'You can only delete your own replies'); + utils.postError(msg.channel, "You can only delete your own replies"); return; } await thread.deleteStaffReply(msg.member, threadMessage); }, { - aliases: ['d'] + aliases: ["d"] }); } }; diff --git a/src/modules/snippets.js b/src/modules/snippets.js index 2948849..2a5ae73 100644 --- a/src/modules/snippets.js +++ b/src/modules/snippets.js @@ -1,11 +1,11 @@ -const threads = require('../data/threads'); -const snippets = require('../data/snippets'); -const config = require('../cfg'); -const utils = require('../utils'); -const { parseArguments } = require('knub-command-manager'); +const threads = require("../data/threads"); +const snippets = require("../data/snippets"); +const config = require("../cfg"); +const utils = require("../utils"); +const { parseArguments } = require("knub-command-manager"); const whitespaceRegex = /\s/; -const quoteChars = ["'", '"']; +const quoteChars = ["'", "\""]; module.exports = ({ bot, knex, config, commands }) => { /** @@ -21,13 +21,13 @@ module.exports = ({ bot, knex, config, commands }) => { const index = parseInt(match.slice(1, -1), 10) - 1; return (args[index] != null ? args[index] : match); }) - .replace(/\\{/g, '{'); + .replace(/\\{/g, "{"); } /** * When a staff member uses a snippet (snippet prefix + trigger word), find the snippet and post it as a reply in the thread */ - bot.on('messageCreate', async msg => { + bot.on("messageCreate", async msg => { if (! utils.messageIsOnInboxServer(msg)) return; if (! utils.isStaff(msg.member)) return; @@ -75,7 +75,7 @@ module.exports = ({ bot, knex, config, commands }) => { }); // Show or add a snippet - commands.addInboxServerCommand('snippet', ' [text$]', async (msg, args, thread) => { + commands.addInboxServerCommand("snippet", " [text$]", async (msg, args, thread) => { const snippet = await snippets.get(args.trigger); if (snippet) { @@ -97,10 +97,10 @@ module.exports = ({ bot, knex, config, commands }) => { } } }, { - aliases: ['s'] + aliases: ["s"] }); - commands.addInboxServerCommand('delete_snippet', '', async (msg, args, thread) => { + commands.addInboxServerCommand("delete_snippet", "", async (msg, args, thread) => { const snippet = await snippets.get(args.trigger); if (! snippet) { utils.postSystemMessageWithFallback(msg.channel, thread, `Snippet "${args.trigger}" doesn't exist!`); @@ -110,10 +110,10 @@ module.exports = ({ bot, knex, config, commands }) => { await snippets.del(args.trigger); utils.postSystemMessageWithFallback(msg.channel, thread, `Snippet "${args.trigger}" deleted!`); }, { - aliases: ['ds'] + aliases: ["ds"] }); - commands.addInboxServerCommand('edit_snippet', ' [text$]', async (msg, args, thread) => { + commands.addInboxServerCommand("edit_snippet", " [text$]", async (msg, args, thread) => { const snippet = await snippets.get(args.trigger); if (! snippet) { utils.postSystemMessageWithFallback(msg.channel, thread, `Snippet "${args.trigger}" doesn't exist!`); @@ -125,14 +125,14 @@ module.exports = ({ bot, knex, config, commands }) => { utils.postSystemMessageWithFallback(msg.channel, thread, `Snippet "${args.trigger}" edited!`); }, { - aliases: ['es'] + aliases: ["es"] }); - commands.addInboxServerCommand('snippets', [], async (msg, args, thread) => { + commands.addInboxServerCommand("snippets", [], async (msg, args, thread) => { const allSnippets = await snippets.all(); const triggers = allSnippets.map(s => s.trigger); triggers.sort(); - utils.postSystemMessageWithFallback(msg.channel, thread, `Available snippets (prefix ${config.snippetPrefix}):\n${triggers.join(', ')}`); + utils.postSystemMessageWithFallback(msg.channel, thread, `Available snippets (prefix ${config.snippetPrefix}):\n${triggers.join(", ")}`); }); }; diff --git a/src/modules/suspend.js b/src/modules/suspend.js index 61d3708..bddb756 100644 --- a/src/modules/suspend.js +++ b/src/modules/suspend.js @@ -1,9 +1,9 @@ -const moment = require('moment'); +const moment = require("moment"); const threads = require("../data/threads"); -const utils = require('../utils'); -const config = require('../cfg'); +const utils = require("../utils"); +const config = require("../cfg"); -const {THREAD_STATUS} = require('../data/constants'); +const {THREAD_STATUS} = require("../data/constants"); module.exports = ({ bot, knex, config, commands }) => { // Check for threads that are scheduled to be suspended and suspend them @@ -29,20 +29,20 @@ module.exports = ({ bot, knex, config, commands }) => { scheduledSuspendLoop(); - commands.addInboxThreadCommand('suspend cancel', [], async (msg, args, thread) => { + commands.addInboxThreadCommand("suspend cancel", [], async (msg, args, thread) => { // Cancel timed suspend if (thread.scheduled_suspend_at) { await thread.cancelScheduledSuspend(); - thread.postSystemMessage(`Cancelled scheduled suspension`); + thread.postSystemMessage("Cancelled scheduled suspension"); } else { - thread.postSystemMessage(`Thread is not scheduled to be suspended`); + thread.postSystemMessage("Thread is not scheduled to be suspended"); } }); - commands.addInboxThreadCommand('suspend', '[delay:delay]', async (msg, args, thread) => { + commands.addInboxThreadCommand("suspend", "[delay:delay]", async (msg, args, thread) => { if (args.delay) { - const suspendAt = moment.utc().add(args.delay, 'ms'); - await thread.scheduleSuspend(suspendAt.format('YYYY-MM-DD HH:mm:ss'), msg.author); + const suspendAt = moment.utc().add(args.delay, "ms"); + await thread.scheduleSuspend(suspendAt.format("YYYY-MM-DD HH:mm:ss"), msg.author); thread.postSystemMessage(`Thread will be suspended in ${utils.humanizeDelay(args.delay)}. Use \`${config.prefix}suspend cancel\` to cancel.`); @@ -50,18 +50,18 @@ module.exports = ({ bot, knex, config, commands }) => { } await thread.suspend(); - thread.postSystemMessage(`**Thread suspended!** This thread will act as closed until unsuspended with \`!unsuspend\``); + thread.postSystemMessage("**Thread suspended!** This thread will act as closed until unsuspended with `!unsuspend`"); }); - commands.addInboxServerCommand('unsuspend', [], async (msg, args, thread) => { + commands.addInboxServerCommand("unsuspend", [], async (msg, args, thread) => { if (thread) { - thread.postSystemMessage(`Thread is not suspended`); + thread.postSystemMessage("Thread is not suspended"); return; } thread = await threads.findSuspendedThreadByChannelId(msg.channel.id); if (! thread) { - msg.channel.createMessage(`Not in a thread`); + msg.channel.createMessage("Not in a thread"); return; } @@ -72,6 +72,6 @@ module.exports = ({ bot, knex, config, commands }) => { } await thread.unsuspend(); - thread.postSystemMessage(`**Thread unsuspended!**`); + thread.postSystemMessage("**Thread unsuspended!**"); }); }; diff --git a/src/modules/typingProxy.js b/src/modules/typingProxy.js index d2e585e..cdcc97a 100644 --- a/src/modules/typingProxy.js +++ b/src/modules/typingProxy.js @@ -1,4 +1,4 @@ -const config = require('../cfg'); +const config = require("../cfg"); const threads = require("../data/threads"); const Eris = require("eris"); diff --git a/src/modules/version.js b/src/modules/version.js index 0411cf0..b198297 100644 --- a/src/modules/version.js +++ b/src/modules/version.js @@ -1,18 +1,18 @@ -const path = require('path'); -const fs = require('fs'); -const {promisify} = require('util'); +const path = require("path"); +const fs = require("fs"); +const {promisify} = require("util"); const utils = require("../utils"); -const updates = require('../data/updates'); -const config = require('../cfg'); +const updates = require("../data/updates"); +const config = require("../cfg"); const access = promisify(fs.access); const readFile = promisify(fs.readFile); -const GIT_DIR = path.join(__dirname, '..', '..', '.git'); +const GIT_DIR = path.join(__dirname, "..", "..", ".git"); module.exports = ({ bot, knex, config, commands }) => { - commands.addInboxServerCommand('version', [], async (msg, args, thread) => { - const packageJson = require('../../package.json'); + commands.addInboxServerCommand("version", [], async (msg, args, thread) => { + const packageJson = require("../../package.json"); const packageVersion = packageJson.version; let response = `Modmail v${packageVersion}`; @@ -27,12 +27,12 @@ module.exports = ({ bot, knex, config, commands }) => { if (isGit) { let commitHash; - const HEAD = await readFile(path.join(GIT_DIR, 'HEAD'), {encoding: 'utf8'}); + const HEAD = await readFile(path.join(GIT_DIR, "HEAD"), {encoding: "utf8"}); - if (HEAD.startsWith('ref:')) { + if (HEAD.startsWith("ref:")) { // Branch const ref = HEAD.match(/^ref: (.*)$/m)[1]; - commitHash = (await readFile(path.join(GIT_DIR, ref), {encoding: 'utf8'})).trim(); + commitHash = (await readFile(path.join(GIT_DIR, ref), {encoding: "utf8"})).trim(); } else { // Detached head commitHash = HEAD.trim(); diff --git a/src/modules/webserver.js b/src/modules/webserver.js index 22dff34..9f22f47 100644 --- a/src/modules/webserver.js +++ b/src/modules/webserver.js @@ -1,18 +1,18 @@ -const http = require('http'); -const mime = require('mime'); -const url = require('url'); -const fs = require('fs'); -const qs = require('querystring'); -const moment = require('moment'); -const config = require('../cfg'); -const threads = require('../data/threads'); -const attachments = require('../data/attachments'); +const http = require("http"); +const mime = require("mime"); +const url = require("url"); +const fs = require("fs"); +const qs = require("querystring"); +const moment = require("moment"); +const config = require("../cfg"); +const threads = require("../data/threads"); +const attachments = require("../data/attachments"); -const {THREAD_MESSAGE_TYPE} = require('../data/constants'); +const {THREAD_MESSAGE_TYPE} = require("../data/constants"); function notfound(res) { res.statusCode = 404; - res.end('Page Not Found'); + res.end("Page Not Found"); } async function serveLogs(req, res, pathParts, query) { @@ -41,7 +41,7 @@ async function serveLogs(req, res, pathParts, query) { return message.body; } - let line = `[${moment.utc(message.created_at).format('YYYY-MM-DD HH:mm:ss')}]`; + let line = `[${moment.utc(message.created_at).format("YYYY-MM-DD HH:mm:ss")}]`; if (query.verbose) { if (message.dm_channel_id) { @@ -72,12 +72,12 @@ async function serveLogs(req, res, pathParts, query) { return line; }); - const openedAt = moment(thread.created_at).format('YYYY-MM-DD HH:mm:ss'); + const openedAt = moment(thread.created_at).format("YYYY-MM-DD HH:mm:ss"); const header = `# Modmail thread with ${thread.user_name} (${thread.user_id}) started at ${openedAt}. All times are in UTC+0.`; - const fullResponse = header + '\n\n' + lines.join('\n'); + const fullResponse = header + "\n\n" + lines.join("\n"); - res.setHeader('Content-Type', 'text/plain; charset=UTF-8'); + res.setHeader("Content-Type", "text/plain; charset=UTF-8"); res.end(fullResponse); } @@ -92,11 +92,11 @@ function serveAttachments(req, res, pathParts) { fs.access(attachmentPath, (err) => { if (err) return notfound(res); - const filenameParts = desiredFilename.split('.'); - const ext = (filenameParts.length > 1 ? filenameParts[filenameParts.length - 1] : 'bin'); + const filenameParts = desiredFilename.split("."); + const ext = (filenameParts.length > 1 ? filenameParts[filenameParts.length - 1] : "bin"); const fileMime = mime.getType(ext); - res.setHeader('Content-Type', fileMime); + res.setHeader("Content-Type", fileMime); const read = fs.createReadStream(attachmentPath); read.pipe(res); @@ -106,20 +106,20 @@ function serveAttachments(req, res, pathParts) { module.exports = () => { const server = http.createServer((req, res) => { const parsedUrl = url.parse(`http://${req.url}`); - const pathParts = parsedUrl.pathname.split('/').filter(v => v !== ''); + const pathParts = parsedUrl.pathname.split("/").filter(v => v !== ""); const query = qs.parse(parsedUrl.query); - if (parsedUrl.pathname.startsWith('/logs/')) { + if (parsedUrl.pathname.startsWith("/logs/")) { serveLogs(req, res, pathParts, query); - } else if (parsedUrl.pathname.startsWith('/attachments/')) { + } else if (parsedUrl.pathname.startsWith("/attachments/")) { serveAttachments(req, res, pathParts, query); } else { notfound(res); } }); - server.on('error', err => { - console.log('[WARN] Web server error:', err.message); + server.on("error", err => { + console.log("[WARN] Web server error:", err.message); }); server.listen(config.port); diff --git a/src/plugins.js b/src/plugins.js index b078dbb..d03c2ae 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -1,6 +1,6 @@ -const attachments = require('./data/attachments'); -const { beforeNewThread } = require('./hooks/beforeNewThread'); -const formats = require('./formatters'); +const attachments = require("./data/attachments"); +const { beforeNewThread } = require("./hooks/beforeNewThread"); +const formats = require("./formatters"); module.exports = { getPluginAPI({ bot, knex, config, commands }) { diff --git a/src/utils.js b/src/utils.js index 4c5d08d..791ca67 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,9 +1,9 @@ -const Eris = require('eris'); -const bot = require('./bot'); -const moment = require('moment'); -const humanizeDuration = require('humanize-duration'); -const publicIp = require('public-ip'); -const config = require('./cfg'); +const Eris = require("eris"); +const bot = require("./bot"); +const moment = require("moment"); +const humanizeDuration = require("humanize-duration"); +const publicIp = require("public-ip"); +const config = require("./cfg"); class BotError extends Error {} @@ -18,7 +18,7 @@ let logChannel = null; */ function getInboxGuild() { 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!'); + if (! inboxGuild) throw new BotError("The bot is not on the modmail (inbox) server!"); return inboxGuild; } @@ -32,9 +32,9 @@ function getMainGuilds() { if (mainGuilds.length !== config.mainGuildId.length) { if (config.mainGuildId.length === 1) { - console.warn(`[WARN] The bot hasn't joined the main guild!`); + console.warn("[WARN] The bot hasn't joined the main guild!"); } else { - console.warn(`[WARN] The bot hasn't joined one or more main guilds!`); + console.warn("[WARN] The bot hasn't joined one or more main guilds!"); } } @@ -50,11 +50,11 @@ function getLogChannel() { const logChannel = inboxGuild.channels.get(config.logChannelId); if (! logChannel) { - throw new BotError('Log channel (logChannelId) not found!'); + throw new BotError("Log channel (logChannelId) not found!"); } if (! (logChannel instanceof Eris.TextChannel)) { - throw new BotError('Make sure the logChannelId option is set to a text channel!'); + throw new BotError("Make sure the logChannelId option is set to a text channel!"); } return logChannel; @@ -155,7 +155,7 @@ function getUserMention(str) { * @returns {String} */ function getTimestamp(...momentArgs) { - return moment.utc(...momentArgs).format('HH:mm'); + return moment.utc(...momentArgs).format("HH:mm"); } /** @@ -164,7 +164,7 @@ function getTimestamp(...momentArgs) { * @returns {String} */ function disableLinkPreviews(str) { - return str.replace(/(^|[^<])(https?:\/\/\S+)/ig, '$1<$2>'); + return str.replace(/(^|[^<])(https?:\/\/\S+)/ig, "$1<$2>"); } /** @@ -172,7 +172,7 @@ function disableLinkPreviews(str) { * @param {String} path * @returns {Promise} */ -async function getSelfUrl(path = '') { +async function getSelfUrl(path = "") { if (config.url) { return `${config.url}/${path}`; } else { @@ -216,9 +216,9 @@ function chunk(items, chunkSize) { */ function trimAll(str) { return str - .split('\n') + .split("\n") .map(str => str.trim()) - .join('\n'); + .join("\n"); } const delayStringRegex = /^([0-9]+)(?:([dhms])[a-z]*)?/i; @@ -234,17 +234,17 @@ function convertDelayStringToMS(str) { str = str.trim(); - while (str !== '' && (match = str.match(delayStringRegex)) !== null) { - if (match[2] === 'd') ms += match[1] * 1000 * 60 * 60 * 24; - else if (match[2] === 'h') ms += match[1] * 1000 * 60 * 60; - else if (match[2] === 's') ms += match[1] * 1000; - else if (match[2] === 'm' || ! match[2]) ms += match[1] * 1000 * 60; + while (str !== "" && (match = str.match(delayStringRegex)) !== null) { + if (match[2] === "d") ms += match[1] * 1000 * 60 * 60 * 24; + else if (match[2] === "h") ms += match[1] * 1000 * 60 * 60; + else if (match[2] === "s") ms += match[1] * 1000; + else if (match[2] === "m" || ! match[2]) ms += match[1] * 1000 * 60; str = str.slice(match[0].length); } // Invalid delay string - if (str !== '') { + if (str !== "") { return null; } @@ -256,11 +256,11 @@ function getInboxMention() { const mentions = []; for (const role of mentionRoles) { if (role == null) continue; - else if (role === 'here') mentions.push('@here'); - else if (role === 'everyone') mentions.push('@everyone'); + else if (role === "here") mentions.push("@here"); + else if (role === "everyone") mentions.push("@everyone"); else mentions.push(`<@&${role}>`); } - return mentions.join(' ') + ' '; + return mentions.join(" ") + " "; } function postSystemMessageWithFallback(channel, thread, text) { @@ -286,7 +286,7 @@ function setDataModelProps(target, props) { target[prop] = null; } else { // Set the value as a string in the same format it's returned in SQLite - target[prop] = moment.utc(props[prop]).format('YYYY-MM-DD HH:mm:ss'); + target[prop] = moment.utc(props[prop]).format("YYYY-MM-DD HH:mm:ss"); } } else { target[prop] = props[prop]; @@ -299,11 +299,11 @@ function isSnowflake(str) { return str && snowflakeRegex.test(str); } -const humanizeDelay = (delay, opts = {}) => humanizeDuration(delay, Object.assign({conjunction: ' and '}, opts)); +const humanizeDelay = (delay, opts = {}) => humanizeDuration(delay, Object.assign({conjunction: " and "}, opts)); const markdownCharsRegex = /([\\_*|`~])/g; function escapeMarkdown(str) { - return str.replace(markdownCharsRegex, '\\$1'); + return str.replace(markdownCharsRegex, "\\$1"); } function disableCodeBlocks(str) { @@ -314,7 +314,7 @@ function disableCodeBlocks(str) { * */ function readMultilineConfigValue(str) { - return Array.isArray(str) ? str.join('\n') : str; + return Array.isArray(str) ? str.join("\n") : str; } module.exports = {