aboutsummaryrefslogtreecommitdiff
path: root/src/modules/snippets.js
diff options
context:
space:
mode:
authorSin-MacBook <[email protected]>2020-08-10 23:44:20 +0200
committerSin-MacBook <[email protected]>2020-08-10 23:44:20 +0200
commit2a53887abba882bf7b63aeda8dfa55fdb3ab8792 (patch)
treead7a95eb41faa6ff13c3142285cdc0eb3ca92183 /src/modules/snippets.js
downloadmodmail-2a53887abba882bf7b63aeda8dfa55fdb3ab8792.tar.xz
modmail-2a53887abba882bf7b63aeda8dfa55fdb3ab8792.zip
clean this up when home
Diffstat (limited to 'src/modules/snippets.js')
-rw-r--r--src/modules/snippets.js138
1 files changed, 138 insertions, 0 deletions
diff --git a/src/modules/snippets.js b/src/modules/snippets.js
new file mode 100644
index 0000000..e9c1271
--- /dev/null
+++ b/src/modules/snippets.js
@@ -0,0 +1,138 @@
+const threads = require('../data/threads');
+const snippets = require('../data/snippets');
+const config = require('../config');
+const utils = require('../utils');
+const { parseArguments } = require('knub-command-manager');
+
+const whitespaceRegex = /\s/;
+const quoteChars = ["'", '"'];
+
+module.exports = ({ bot, knex, config, commands }) => {
+ /**
+ * "Renders" a snippet by replacing all argument placeholders e.g. {1} {2} with their corresponding arguments.
+ * The number in the placeholder is the argument's order in the argument list, i.e. {1} is the first argument (= index 0)
+ * @param {String} body
+ * @param {String[]} args
+ * @returns {String}
+ */
+ function renderSnippet(body, args) {
+ return body
+ .replace(/(?<!\\){\d+}/g, match => {
+ const index = parseInt(match.slice(1, -1), 10) - 1;
+ return (args[index] != null ? args[index] : match);
+ })
+ .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 => {
+ if (! utils.messageIsOnInboxServer(msg)) return;
+ if (! utils.isStaff(msg.member)) return;
+
+ if (msg.author.bot) return;
+ if (! msg.content) return;
+ if (! msg.content.startsWith(config.snippetPrefix) && ! msg.content.startsWith(config.snippetPrefixAnon)) return;
+
+ let snippetPrefix, isAnonymous;
+
+ if (config.snippetPrefixAnon.length > config.snippetPrefix.length) {
+ // Anonymous prefix is longer -> check it first
+ if (msg.content.startsWith(config.snippetPrefixAnon)) {
+ snippetPrefix = config.snippetPrefixAnon;
+ isAnonymous = true;
+ } else {
+ snippetPrefix = config.snippetPrefix;
+ isAnonymous = false;
+ }
+ } else {
+ // Regular prefix is longer -> check it first
+ if (msg.content.startsWith(config.snippetPrefix)) {
+ snippetPrefix = config.snippetPrefix;
+ isAnonymous = false;
+ } else {
+ snippetPrefix = config.snippetPrefixAnon;
+ isAnonymous = true;
+ }
+ }
+
+ const thread = await threads.findByChannelId(msg.channel.id);
+ if (! thread) return;
+
+ let [, trigger, rawArgs] = msg.content.slice(snippetPrefix.length).match(/(\S+)(?:\s+(.*))?/s);
+ trigger = trigger.toLowerCase();
+
+ const snippet = await snippets.get(trigger);
+ if (! snippet) return;
+
+ let args = rawArgs ? parseArguments(rawArgs) : [];
+ args = args.map(arg => arg.value);
+ const rendered = renderSnippet(snippet.body, args);
+
+ const replied = await thread.replyToUser(msg.member, rendered, [], isAnonymous);
+ if (replied) msg.delete();
+ });
+
+ // Show or add a snippet
+ commands.addInboxServerCommand('snippet', '<trigger> [text$]', async (msg, args, thread) => {
+ const snippet = await snippets.get(args.trigger);
+
+ if (snippet) {
+ if (args.text) {
+ // If the snippet exists and we're trying to create a new one, inform the user the snippet already exists
+ utils.postSystemMessageWithFallback(msg.channel, thread, `Snippet "${args.trigger}" already exists! You can edit or delete it with ${config.prefix}edit_snippet and ${config.prefix}delete_snippet respectively.`);
+ } else {
+ // If the snippet exists and we're NOT trying to create a new one, show info about the existing snippet
+ utils.postSystemMessageWithFallback(msg.channel, thread, `\`${config.snippetPrefix}${args.trigger}\` replies with: \`\`\`${utils.disableCodeBlocks(snippet.body)}\`\`\``);
+ }
+ } else {
+ if (args.text) {
+ // If the snippet doesn't exist and the user wants to create it, create it
+ await snippets.add(args.trigger, args.text, msg.author.id);
+ utils.postSystemMessageWithFallback(msg.channel, thread, `Snippet "${args.trigger}" created!`);
+ } else {
+ // If the snippet doesn't exist and the user isn't trying to create it, inform them how to create it
+ utils.postSystemMessageWithFallback(msg.channel, thread, `Snippet "${args.trigger}" doesn't exist! You can create it with \`${config.prefix}snippet ${args.trigger} text\``);
+ }
+ }
+ }, {
+ aliases: ['s']
+ });
+
+ commands.addInboxServerCommand('delete_snippet', '<trigger>', async (msg, args, thread) => {
+ const snippet = await snippets.get(args.trigger);
+ if (! snippet) {
+ utils.postSystemMessageWithFallback(msg.channel, thread, `Snippet "${args.trigger}" doesn't exist!`);
+ return;
+ }
+
+ await snippets.del(args.trigger);
+ utils.postSystemMessageWithFallback(msg.channel, thread, `Snippet "${args.trigger}" deleted!`);
+ }, {
+ aliases: ['ds']
+ });
+
+ commands.addInboxServerCommand('edit_snippet', '<trigger> [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!`);
+ return;
+ }
+
+ await snippets.del(args.trigger);
+ await snippets.add(args.trigger, args.text, msg.author.id);
+
+ utils.postSystemMessageWithFallback(msg.channel, thread, `Snippet "${args.trigger}" edited!`);
+ }, {
+ aliases: ['es']
+ });
+
+ 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(', ')}`);
+ });
+};