2020-08-12 17:08:37 -04:00
|
|
|
const Eris = require("eris");
|
|
|
|
const utils = require("./utils");
|
|
|
|
const config = require("./cfg");
|
|
|
|
const ThreadMessage = require("./data/ThreadMessage");
|
2020-08-13 17:42:32 -04:00
|
|
|
const {THREAD_MESSAGE_TYPE} = require("./data/constants");
|
|
|
|
const moment = require("moment");
|
2020-07-13 18:11:48 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to format the DM that is sent to the user when a staff member replies to them via !reply
|
|
|
|
* @callback FormatStaffReplyDM
|
2020-08-13 17:42:32 -04:00
|
|
|
* @param {ThreadMessage} threadMessage
|
2020-07-13 18:31:50 -04:00
|
|
|
* @return {Eris.MessageContent} Message content to send as a DM
|
2020-07-13 18:11:48 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to format a staff reply in a thread channel
|
|
|
|
* @callback FormatStaffReplyThreadMessage
|
2020-08-13 17:42:32 -04:00
|
|
|
* @param {ThreadMessage} threadMessage
|
2020-07-13 18:31:50 -04:00
|
|
|
* @return {Eris.MessageContent} Message content to post in the thread channel
|
2020-07-13 18:11:48 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to format a user reply in a thread channel
|
|
|
|
* @callback FormatUserReplyThreadMessage
|
2020-08-13 17:42:32 -04:00
|
|
|
* @param {ThreadMessage} threadMessage
|
2020-07-13 18:31:50 -04:00
|
|
|
* @return {Eris.MessageContent} Message content to post in the thread channel
|
2020-07-13 18:11:48 -04:00
|
|
|
*/
|
|
|
|
|
2020-07-19 07:11:38 -04:00
|
|
|
/**
|
|
|
|
* Function to format the inbox channel notification for a staff reply edit
|
|
|
|
* @callback FormatStaffReplyEditNotificationThreadMessage
|
|
|
|
* @param {ThreadMessage} threadMessage
|
|
|
|
* @return {Eris.MessageContent} Message content to post in the thread channel
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to format the inbox channel notification for a staff reply deletion
|
|
|
|
* @callback FormatStaffReplyDeletionNotificationThreadMessage
|
|
|
|
* @param {ThreadMessage} threadMessage
|
|
|
|
* @return {Eris.MessageContent} Message content to post in the thread channel
|
|
|
|
*/
|
|
|
|
|
2020-10-12 13:33:43 -04:00
|
|
|
/**
|
|
|
|
* Function to format a system message in a thread channel
|
|
|
|
* @callback FormatSystemThreadMessage
|
|
|
|
* @param {ThreadMessage} threadMessage
|
|
|
|
* @return {Eris.MessageContent} Message content to post in the thread channel
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to format a system message sent to the user in a thread channel
|
|
|
|
* @callback FormatSystemToUserThreadMessage
|
|
|
|
* @param {ThreadMessage} threadMessage
|
|
|
|
* @return {Eris.MessageContent} Message content to post in the thread channel
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to format the DM that is sent to the user when the bot sends a system message to the user
|
|
|
|
* @callback FormatSystemToUserDM
|
|
|
|
* @param {ThreadMessage} threadMessage
|
|
|
|
* @return {Eris.MessageContent} Message content to send as a DM
|
|
|
|
*/
|
|
|
|
|
2020-07-19 07:11:38 -04:00
|
|
|
/**
|
2020-08-13 17:42:32 -04:00
|
|
|
* @typedef {Object} FormatLogOptions
|
|
|
|
* @property {Boolean?} simple
|
|
|
|
* @property {Boolean?} verbose
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef {Object} FormatLogResult
|
|
|
|
* @property {String} content Contents of the entire log
|
|
|
|
* @property {*?} extra
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to format the inbox channel notification for a staff reply deletion
|
|
|
|
* @callback FormatLog
|
|
|
|
* @param {Thread} thread
|
|
|
|
* @param {ThreadMessage[]} threadMessages
|
|
|
|
* @param {FormatLogOptions={}} opts
|
|
|
|
* @return {FormatLogResult}
|
2020-07-19 07:11:38 -04:00
|
|
|
*/
|
|
|
|
|
2020-07-13 18:11:48 -04:00
|
|
|
/**
|
|
|
|
* @typedef MessageFormatters
|
|
|
|
* @property {FormatStaffReplyDM} formatStaffReplyDM
|
|
|
|
* @property {FormatStaffReplyThreadMessage} formatStaffReplyThreadMessage
|
|
|
|
* @property {FormatUserReplyThreadMessage} formatUserReplyThreadMessage
|
2020-07-19 07:11:38 -04:00
|
|
|
* @property {FormatStaffReplyEditNotificationThreadMessage} formatStaffReplyEditNotificationThreadMessage
|
|
|
|
* @property {FormatStaffReplyDeletionNotificationThreadMessage} formatStaffReplyDeletionNotificationThreadMessage
|
2020-10-12 13:33:43 -04:00
|
|
|
* @property {FormatSystemThreadMessage} formatSystemThreadMessage
|
|
|
|
* @property {FormatSystemToUserThreadMessage} formatSystemToUserThreadMessage
|
|
|
|
* @property {FormatSystemToUserDM} formatSystemToUserDM
|
2020-08-13 17:42:32 -04:00
|
|
|
* @property {FormatLog} formatLog
|
2020-07-13 18:11:48 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @type {MessageFormatters}
|
|
|
|
*/
|
|
|
|
const defaultFormatters = {
|
2020-08-13 17:42:32 -04:00
|
|
|
formatStaffReplyDM(threadMessage) {
|
2020-10-21 16:24:45 -04:00
|
|
|
const roleName = threadMessage.role_name || config.fallbackRoleName;
|
2020-08-13 17:42:32 -04:00
|
|
|
const modInfo = threadMessage.is_anonymous
|
2020-10-21 16:24:45 -04:00
|
|
|
? roleName
|
|
|
|
: (roleName ? `(${roleName}) ${threadMessage.user_name}` : threadMessage.user_name);
|
2020-08-13 17:42:32 -04:00
|
|
|
|
2020-10-21 16:24:45 -04:00
|
|
|
return modInfo
|
|
|
|
? `**${modInfo}:** ${threadMessage.body}`
|
|
|
|
: threadMessage.body;
|
2020-07-13 18:11:48 -04:00
|
|
|
},
|
|
|
|
|
2020-08-13 17:42:32 -04:00
|
|
|
formatStaffReplyThreadMessage(threadMessage) {
|
2020-10-21 16:24:45 -04:00
|
|
|
const roleName = threadMessage.role_name || config.fallbackRoleName;
|
2020-08-13 17:42:32 -04:00
|
|
|
const modInfo = threadMessage.is_anonymous
|
2020-10-21 16:24:45 -04:00
|
|
|
? (roleName ? `(Anonymous) (${threadMessage.user_name}) ${roleName}` : `(Anonymous) (${threadMessage.user_name})`)
|
|
|
|
: (roleName ? `(${roleName}) ${threadMessage.user_name}` : threadMessage.user_name);
|
2020-07-13 18:11:48 -04:00
|
|
|
|
2020-10-21 16:24:45 -04:00
|
|
|
let result = modInfo
|
|
|
|
? `**${modInfo}:** ${threadMessage.body}`
|
|
|
|
: threadMessage.body;
|
2020-07-13 18:11:48 -04:00
|
|
|
|
|
|
|
if (config.threadTimestamps) {
|
2020-08-13 17:42:32 -04:00
|
|
|
const formattedTimestamp = utils.getTimestamp(threadMessage.created_at);
|
2020-07-13 18:11:48 -04:00
|
|
|
result = `[${formattedTimestamp}] ${result}`;
|
|
|
|
}
|
|
|
|
|
2020-08-16 18:46:51 -04:00
|
|
|
result = `\`${threadMessage.message_number}\` ${result}`;
|
2020-07-19 07:24:17 -04:00
|
|
|
|
2020-07-13 18:11:48 -04:00
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
2020-08-13 17:42:32 -04:00
|
|
|
formatUserReplyThreadMessage(threadMessage) {
|
|
|
|
let result = `**${threadMessage.user_name}:** ${threadMessage.body}`;
|
2020-07-13 18:11:48 -04:00
|
|
|
|
2020-08-13 17:42:32 -04:00
|
|
|
for (const link of threadMessage.attachments) {
|
|
|
|
result += `\n\n${link}`;
|
2020-07-13 18:11:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (config.threadTimestamps) {
|
2020-08-13 17:42:32 -04:00
|
|
|
const formattedTimestamp = utils.getTimestamp(threadMessage.created_at);
|
2020-07-13 18:11:48 -04:00
|
|
|
result = `[${formattedTimestamp}] ${result}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
2020-10-12 13:54:42 -04:00
|
|
|
formatStaffReplyEditNotificationThreadMessage(threadMessage) {
|
|
|
|
const originalThreadMessage = threadMessage.getMetadataValue("originalThreadMessage");
|
|
|
|
const newBody = threadMessage.getMetadataValue("newBody");
|
2020-08-16 18:53:21 -04:00
|
|
|
|
2020-10-12 13:54:42 -04:00
|
|
|
let content = `**${originalThreadMessage.user_name}** (\`${originalThreadMessage.user_id}\`) edited reply \`${originalThreadMessage.message_number}\``;
|
|
|
|
|
|
|
|
if (originalThreadMessage.body.length < 200 && newBody.length < 200) {
|
2020-08-16 18:53:21 -04:00
|
|
|
// Show edits of small messages inline
|
2020-10-12 13:54:42 -04:00
|
|
|
content += ` from \`${utils.disableInlineCode(originalThreadMessage.body)}\` to \`${newBody}\``;
|
2020-08-16 18:53:21 -04:00
|
|
|
} else {
|
|
|
|
// Show edits of long messages in two code blocks
|
|
|
|
content += ":";
|
2020-10-12 13:54:42 -04:00
|
|
|
content += `\n\nBefore:\n\`\`\`${utils.disableCodeBlocks(originalThreadMessage.body)}\`\`\``;
|
|
|
|
content += `\nAfter:\n\`\`\`${utils.disableCodeBlocks(newBody)}\`\`\``;
|
2020-08-16 18:53:21 -04:00
|
|
|
}
|
|
|
|
|
2020-08-13 18:19:01 -04:00
|
|
|
return content;
|
2020-07-19 07:11:38 -04:00
|
|
|
},
|
|
|
|
|
2020-10-12 13:54:42 -04:00
|
|
|
formatStaffReplyDeletionNotificationThreadMessage(threadMessage) {
|
|
|
|
const originalThreadMessage = threadMessage.getMetadataValue("originalThreadMessage");
|
|
|
|
let content = `**${originalThreadMessage.user_name}** (\`${originalThreadMessage.user_id}\`) deleted reply \`${originalThreadMessage.message_number}\``;
|
2020-08-16 18:53:21 -04:00
|
|
|
|
2020-10-12 13:54:42 -04:00
|
|
|
if (originalThreadMessage.body.length < 200) {
|
2020-08-16 18:53:21 -04:00
|
|
|
// Show the original content of deleted small messages inline
|
2020-10-12 13:54:42 -04:00
|
|
|
content += ` (message content: \`${utils.disableInlineCode(originalThreadMessage.body)}\`)`;
|
2020-08-16 18:53:21 -04:00
|
|
|
} else {
|
|
|
|
// Show the original content of deleted large messages in a code block
|
2020-10-12 13:54:42 -04:00
|
|
|
content += ":\n```" + utils.disableCodeBlocks(originalThreadMessage.body) + "```";
|
2020-08-16 18:53:21 -04:00
|
|
|
}
|
|
|
|
|
2020-08-13 18:19:01 -04:00
|
|
|
return content;
|
2020-07-19 07:11:38 -04:00
|
|
|
},
|
|
|
|
|
2020-10-12 13:33:43 -04:00
|
|
|
formatSystemThreadMessage(threadMessage) {
|
|
|
|
let result = threadMessage.body;
|
|
|
|
|
|
|
|
for (const link of threadMessage.attachments) {
|
|
|
|
result += `\n\n${link}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
|
|
|
formatSystemToUserThreadMessage(threadMessage) {
|
2020-10-12 13:54:42 -04:00
|
|
|
let result = `**[SYSTEM TO USER]** ${threadMessage.body}`;
|
2020-10-12 13:33:43 -04:00
|
|
|
|
|
|
|
for (const link of threadMessage.attachments) {
|
|
|
|
result += `\n\n${link}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
|
|
|
formatSystemToUserDM(threadMessage) {
|
|
|
|
let result = threadMessage.body;
|
|
|
|
|
|
|
|
for (const link of threadMessage.attachments) {
|
|
|
|
result += `\n\n${link}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
2020-08-13 17:42:32 -04:00
|
|
|
formatLog(thread, threadMessages, opts = {}) {
|
|
|
|
if (opts.simple) {
|
|
|
|
threadMessages = threadMessages.filter(message => {
|
|
|
|
return (
|
|
|
|
message.message_type !== THREAD_MESSAGE_TYPE.SYSTEM
|
|
|
|
&& message.message_type !== THREAD_MESSAGE_TYPE.SYSTEM_TO_USER
|
|
|
|
&& message.message_type !== THREAD_MESSAGE_TYPE.CHAT
|
|
|
|
&& message.message_type !== THREAD_MESSAGE_TYPE.COMMAND
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
const lines = threadMessages.map(message => {
|
|
|
|
// Legacy messages (from 2018) are the entire log in one message, so just serve them as they are
|
|
|
|
if (message.message_type === THREAD_MESSAGE_TYPE.LEGACY) {
|
|
|
|
return message.body;
|
|
|
|
}
|
|
|
|
|
|
|
|
let line = `[${moment.utc(message.created_at).format("YYYY-MM-DD HH:mm:ss")}]`;
|
|
|
|
|
|
|
|
if (opts.verbose) {
|
|
|
|
if (message.dm_channel_id) {
|
|
|
|
line += ` [DM CHA ${message.dm_channel_id}]`;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (message.dm_message_id) {
|
|
|
|
line += ` [DM MSG ${message.dm_message_id}]`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (message.message_type === THREAD_MESSAGE_TYPE.FROM_USER) {
|
|
|
|
line += ` [FROM USER] [${message.user_name}] ${message.body}`;
|
|
|
|
} else if (message.message_type === THREAD_MESSAGE_TYPE.TO_USER) {
|
2020-08-16 18:45:45 -04:00
|
|
|
if (opts.verbose) {
|
|
|
|
line += ` [TO USER] [${message.message_number || "0"}] [${message.user_name}]`;
|
|
|
|
} else {
|
|
|
|
line += ` [TO USER] [${message.user_name}]`;
|
|
|
|
}
|
|
|
|
|
2020-08-13 17:42:32 -04:00
|
|
|
if (message.use_legacy_format) {
|
|
|
|
// Legacy format (from pre-2.31.0) includes the role and username in the message body, so serve that as is
|
|
|
|
line += ` ${message.body}`;
|
|
|
|
} else if (message.is_anonymous) {
|
|
|
|
if (message.role_name) {
|
|
|
|
line += ` (Anonymous) ${message.role_name}: ${message.body}`;
|
|
|
|
} else {
|
|
|
|
line += ` (Anonymous) Moderator: ${message.body}`;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (message.role_name) {
|
|
|
|
line += ` (${message.role_name}) ${message.user_name}: ${message.body}`;
|
|
|
|
} else {
|
|
|
|
line += ` ${message.user_name}: ${message.body}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (message.message_type === THREAD_MESSAGE_TYPE.SYSTEM) {
|
|
|
|
line += ` [SYSTEM] ${message.body}`;
|
|
|
|
} else if (message.message_type === THREAD_MESSAGE_TYPE.SYSTEM_TO_USER) {
|
|
|
|
line += ` [SYSTEM TO USER] ${message.body}`;
|
|
|
|
} else if (message.message_type === THREAD_MESSAGE_TYPE.CHAT) {
|
|
|
|
line += ` [CHAT] [${message.user_name}] ${message.body}`;
|
|
|
|
} else if (message.message_type === THREAD_MESSAGE_TYPE.COMMAND) {
|
|
|
|
line += ` [COMMAND] [${message.user_name}] ${message.body}`;
|
2020-10-12 13:54:42 -04:00
|
|
|
} else if (message.message_type === THREAD_MESSAGE_TYPE.REPLY_EDITED) {
|
|
|
|
const originalThreadMessage = message.getMetadataValue("originalThreadMessage");
|
|
|
|
line += ` [REPLY EDITED] ${originalThreadMessage.user_name} edited reply ${originalThreadMessage.message_number}:`;
|
|
|
|
line += `\n\nBefore:\n${originalThreadMessage.body}`;
|
|
|
|
line += `\n\nAfter:\n${message.getMetadataValue("newBody")}`;
|
|
|
|
} else if (message.message_type === THREAD_MESSAGE_TYPE.REPLY_DELETED) {
|
|
|
|
const originalThreadMessage = message.getMetadataValue("originalThreadMessage");
|
|
|
|
line += ` [REPLY DELETED] ${originalThreadMessage.user_name} deleted reply ${originalThreadMessage.message_number}:`;
|
|
|
|
line += `\n\n${originalThreadMessage.body}`;
|
2020-08-13 17:42:32 -04:00
|
|
|
} else {
|
|
|
|
line += ` [${message.user_name}] ${message.body}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return line;
|
|
|
|
});
|
|
|
|
|
|
|
|
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 fullResult = header + "\n\n" + lines.join("\n");
|
|
|
|
|
|
|
|
return {
|
|
|
|
content: fullResult,
|
|
|
|
};
|
2020-07-19 07:11:38 -04:00
|
|
|
},
|
2020-07-13 18:11:48 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @type {MessageFormatters}
|
|
|
|
*/
|
|
|
|
const formatters = { ...defaultFormatters };
|
|
|
|
|
2020-08-16 16:26:04 -04:00
|
|
|
/**
|
|
|
|
* @typedef {object} FormattersExport
|
|
|
|
* @property {MessageFormatters} formatters Read only
|
|
|
|
* @property {function(FormatStaffReplyDM): void} setStaffReplyDMFormatter
|
|
|
|
* @property {function(FormatStaffReplyThreadMessage): void} setStaffReplyThreadMessageFormatter
|
|
|
|
* @property {function(FormatUserReplyThreadMessage): void} setUserReplyThreadMessageFormatter
|
|
|
|
* @property {function(FormatStaffReplyEditNotificationThreadMessage): void} setStaffReplyEditNotificationThreadMessageFormatter
|
|
|
|
* @property {function(FormatStaffReplyDeletionNotificationThreadMessage): void} setStaffReplyDeletionNotificationThreadMessageFormatter
|
2020-10-12 13:33:43 -04:00
|
|
|
* @property {function(FormatSystemThreadMessage): void} setSystemThreadMessageFormatter
|
|
|
|
* @property {function(FormatSystemToUserThreadMessage): void} setSystemToUserThreadMessageFormatter
|
|
|
|
* @property {function(FormatSystemToUserDM): void} setSystemToUserDMFormatter
|
2020-08-16 16:26:04 -04:00
|
|
|
* @property {function(FormatLog): void} setLogFormatter
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @type {FormattersExport}
|
|
|
|
*/
|
2020-07-13 18:11:48 -04:00
|
|
|
module.exports = {
|
2020-08-13 17:42:32 -04:00
|
|
|
formatters: new Proxy(formatters, {
|
|
|
|
set() {
|
|
|
|
throw new Error("Please use the formatter setter functions instead of modifying the formatters directly");
|
|
|
|
},
|
|
|
|
}),
|
2020-07-13 18:11:48 -04:00
|
|
|
|
|
|
|
setStaffReplyDMFormatter(fn) {
|
|
|
|
formatters.formatStaffReplyDM = fn;
|
|
|
|
},
|
|
|
|
|
|
|
|
setStaffReplyThreadMessageFormatter(fn) {
|
|
|
|
formatters.formatStaffReplyThreadMessage = fn;
|
|
|
|
},
|
|
|
|
|
|
|
|
setUserReplyThreadMessageFormatter(fn) {
|
|
|
|
formatters.formatUserReplyThreadMessage = fn;
|
|
|
|
},
|
|
|
|
|
2020-07-19 07:11:38 -04:00
|
|
|
setStaffReplyEditNotificationThreadMessageFormatter(fn) {
|
|
|
|
formatters.formatStaffReplyEditNotificationThreadMessage = fn;
|
|
|
|
},
|
|
|
|
|
|
|
|
setStaffReplyDeletionNotificationThreadMessageFormatter(fn) {
|
|
|
|
formatters.formatStaffReplyDeletionNotificationThreadMessage = fn;
|
|
|
|
},
|
|
|
|
|
2020-10-12 13:33:43 -04:00
|
|
|
setSystemThreadMessageFormatter(fn) {
|
|
|
|
formatters.formatSystemThreadMessage = fn;
|
|
|
|
},
|
|
|
|
|
|
|
|
setSystemToUserThreadMessageFormatter(fn) {
|
|
|
|
formatters.formatSystemToUserThreadMessage = fn;
|
|
|
|
},
|
|
|
|
|
|
|
|
setSystemToUserDMFormatter(fn) {
|
|
|
|
formatters.formatSystemToUserDM = fn;
|
|
|
|
},
|
|
|
|
|
2020-08-13 17:42:32 -04:00
|
|
|
setLogFormatter(fn) {
|
|
|
|
formatters.formatLog = fn;
|
2020-07-19 07:11:38 -04:00
|
|
|
},
|
2020-07-13 18:11:48 -04:00
|
|
|
};
|