ramirez/src/formatters.js

260 lines
9.2 KiB
JavaScript

const Eris = require("eris");
const utils = require("./utils");
const config = require("./cfg");
const ThreadMessage = require("./data/ThreadMessage");
const {THREAD_MESSAGE_TYPE} = require("./data/constants");
const moment = require("moment");
/**
* Function to format the DM that is sent to the user when a staff member replies to them via !reply
* @callback FormatStaffReplyDM
* @param {ThreadMessage} threadMessage
* @return {Eris.MessageContent} Message content to send as a DM
*/
/**
* Function to format a staff reply in a thread channel
* @callback FormatStaffReplyThreadMessage
* @param {ThreadMessage} threadMessage
* @return {Eris.MessageContent} Message content to post in the thread channel
*/
/**
* Function to format a user reply in a thread channel
* @callback FormatUserReplyThreadMessage
* @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 edit
* @callback FormatStaffReplyEditNotificationThreadMessage
* @param {ThreadMessage} threadMessage
* @param {string} newText
* @param {Eris.Member} moderator Moderator that edited the message
* @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
* @param {Eris.Member} moderator Moderator that deleted the message
* @return {Eris.MessageContent} Message content to post in the thread channel
*/
/**
* @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}
*/
/**
* @typedef MessageFormatters
* @property {FormatStaffReplyDM} formatStaffReplyDM
* @property {FormatStaffReplyThreadMessage} formatStaffReplyThreadMessage
* @property {FormatUserReplyThreadMessage} formatUserReplyThreadMessage
* @property {FormatStaffReplyEditNotificationThreadMessage} formatStaffReplyEditNotificationThreadMessage
* @property {FormatStaffReplyDeletionNotificationThreadMessage} formatStaffReplyDeletionNotificationThreadMessage
* @property {FormatLog} formatLog
*/
/**
* @type {MessageFormatters}
*/
const defaultFormatters = {
formatStaffReplyDM(threadMessage) {
const modInfo = threadMessage.is_anonymous
? (threadMessage.role_name ? threadMessage.role_name : "Moderator")
: (threadMessage.role_name ? `(${threadMessage.role_name}) ${threadMessage.user_name}` : threadMessage.user_name);
return `**${modInfo}:** ${threadMessage.body}`;
},
formatStaffReplyThreadMessage(threadMessage) {
const modInfo = threadMessage.is_anonymous
? `(Anonymous) (${threadMessage.user_name}) ${threadMessage.role_name || "Moderator"}`
: (threadMessage.role_name ? `(${threadMessage.role_name}) ${threadMessage.user_name}` : threadMessage.user_name);
let result = `**${modInfo}:** ${threadMessage.body}`;
if (config.threadTimestamps) {
const formattedTimestamp = utils.getTimestamp(threadMessage.created_at);
result = `[${formattedTimestamp}] ${result}`;
}
result = `\`[${threadMessage.message_number}]\` ${result}`;
return result;
},
formatUserReplyThreadMessage(threadMessage) {
let result = `**${threadMessage.user_name}:** ${threadMessage.body}`;
for (const link of threadMessage.attachments) {
result += `\n\n${link}`;
}
if (config.threadTimestamps) {
const formattedTimestamp = utils.getTimestamp(threadMessage.created_at);
result = `[${formattedTimestamp}] ${result}`;
}
return result;
},
formatStaffReplyEditNotificationThreadMessage(threadMessage, newText, moderator) {
let content = `**${moderator.user.username}#${moderator.user.discriminator}** (\`${moderator.id}\`) edited reply \`[${threadMessage.message_number}]\`:`;
content += `\n\nBefore:\n\`\`\`${utils.disableCodeBlocks(threadMessage.body)}\`\`\``;
content += `\nAfter:\n\`\`\`${utils.disableCodeBlocks(newText)}\`\`\``;
return content;
},
formatStaffReplyDeletionNotificationThreadMessage(threadMessage, moderator) {
let content = `**${moderator.user.username}#${moderator.user.discriminator}** (\`${moderator.id}\`) deleted reply \`[${threadMessage.message_number}]\`:`;
content += "```" + utils.disableCodeBlocks(threadMessage.body) + "```";
return content;
},
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) {
line += ` [TO USER] [${message.message_number || "0"}] [${message.user_name}]`;
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}`;
} 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,
};
},
};
/**
* @type {MessageFormatters}
*/
const formatters = { ...defaultFormatters };
/**
* @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
* @property {function(FormatLog): void} setLogFormatter
*/
/**
* @type {FormattersExport}
*/
module.exports = {
formatters: new Proxy(formatters, {
set() {
throw new Error("Please use the formatter setter functions instead of modifying the formatters directly");
},
}),
setStaffReplyDMFormatter(fn) {
formatters.formatStaffReplyDM = fn;
},
setStaffReplyThreadMessageFormatter(fn) {
formatters.formatStaffReplyThreadMessage = fn;
},
setUserReplyThreadMessageFormatter(fn) {
formatters.formatUserReplyThreadMessage = fn;
},
setStaffReplyEditNotificationThreadMessageFormatter(fn) {
formatters.formatStaffReplyEditNotificationThreadMessage = fn;
},
setStaffReplyDeletionNotificationThreadMessageFormatter(fn) {
formatters.formatStaffReplyDeletionNotificationThreadMessage = fn;
},
setLogFormatter(fn) {
formatters.formatLog = fn;
},
};