Jelajahi Sumber

Move all features into separate script files

denikson 6 tahun lalu
induk
melakukan
b5256ecda6
9 mengubah file dengan 462 tambahan dan 291 penghapusan
  1. 3 0
      client.js
  2. 82 0
      commands/facemorph.js
  3. 83 0
      commands/guide.js
  4. 18 0
      commands/help.js
  5. 22 0
      commands/image_statistics.js
  6. 88 0
      commands/react.js
  7. 60 0
      commands/rss_checker.js
  8. 74 291
      main.js
  9. 32 0
      util.js

+ 3 - 0
client.js

@@ -0,0 +1,3 @@
+const Discord = require("discord.js");
+
+module.exports = new Discord.Client();

+ 82 - 0
commands/facemorph.js

@@ -0,0 +1,82 @@
+const Clarifai = require("clarifai");
+const ClarifaiTOKEN = require("../clarifai_keys.js");
+const db = require("../db.js");
+const util = require("../util.js");
+const Jimp = require("jimp");
+const client = require("../client.js");
+
+
+const EMOTE_GUILD = "505333548694241281";
+const clarifaiApp = new Clarifai.App({
+    apiKey: ClarifaiTOKEN
+});
+
+async function processFaceSwap(message, attachmentUrl) {
+    let result = await clarifaiApp.models.predict(Clarifai.FACE_DETECT_MODEL, attachmentUrl);
+
+    if(result.outputs[0].data.regions === undefined || result.outputs[0].data.regions.length == 0)
+        return;
+    
+    let image = await Jimp.read(attachmentUrl);
+    let w = image.getWidth();
+    let h = image.getHeight();
+
+    let emojiKeys = [...client.guilds.get(EMOTE_GUILD).emojis.filter(e => !e.animated).keys()];
+
+    for(let region of result.outputs[0].data.regions) {
+        let bb = region.region_info.bounding_box;
+        let bw = (bb.right_col - bb.left_col) * w;
+        let bh = (bb.bottom_row - bb.top_row) * h;
+        
+        let dx = (bb.right_col + bb.left_col) * w / 2;
+        let dy = (bb.bottom_row + bb.top_row) * h / 2;
+
+        let emojiKey = emojiKeys[Math.floor(Math.random() * emojiKeys.length)];
+        let emoji = client.emojis.get(emojiKey);
+        
+        let emojiImage = await Jimp.read(emoji.url);
+        let ew = emojiImage.getWidth();
+        let eh = emojiImage.getHeight();
+
+        const CONSTANT_SCALE = 1.5;
+        let scaleFactor = Math.max(bw, bh) / Math.min(ew, eh) * CONSTANT_SCALE;
+        ew *= scaleFactor;
+        eh *= scaleFactor;
+
+        emojiImage = emojiImage.scale(scaleFactor);
+        image = image.composite(emojiImage, dx - ew / 2, dy - eh / 2);
+    }
+
+    image.quality(90);
+    let buffer = await image.getBufferAsync(Jimp.MIME_JPEG);
+
+    message.channel.send(`I noticed a face in the image. I think this looks better ${client.emojis.get("505076258753740810").toString()}`, {
+        files: [ buffer ]
+    });
+}
+
+const onMessage = (msg, contents, actionsDone) => {
+    if (actionsDone)
+        return false;
+
+    let imagesCount = msg.attachments.filter(v => util.isValidImage(v.filename)).size;
+
+    if (imagesCount > 0) {
+        let probValue = db.get("faceEditChannels").get(msg.channel.id);
+        if (probValue.isUndefined().value() || probValue.isNull().value())
+            return false;
+
+        if (Math.random() > probValue.value())
+            return false;
+
+        let imageAttachment = msg.attachments.find(v => util.isValidImage(v.filename));
+        processFaceSwap(msg, imageAttachment.url).catch(err => console.log(`Failed to run faceapp because ${err}`));
+        return true;
+    }
+
+    return false;
+};
+
+module.exports = {
+    onMessage: onMessage
+};

+ 83 - 0
commands/guide.js

@@ -0,0 +1,83 @@
+const db = require("../db.js");
+const util = require("../util.js");
+
+const commands = {
+    "make guide": msg => {
+        if (!util.isAuthorised(msg.member)) return;
+        let content = msg.content.substring(msg.content.indexOf("make guide") + "make guide ".length);
+        let guideName = content.substring(0, content.indexOf("\n")).trim();
+        let guideContent = content.substring(content.indexOf("\n")).trim();
+
+        let guide = db.get("guides").find({
+            name: guideName
+        });
+
+        if (!guide.isUndefined().value()) {
+            guide.assign({
+                content: guideContent
+            }).write();
+        } else {
+            db.get("guides")
+                .push({
+                    name: guideName,
+                    content: guideContent
+                })
+                .write();
+        }
+
+        msg.channel.send(
+            `${msg.author.toString()} Added/updated "${guideName}"!`
+        );
+    },
+    "delete guide": (msg, s) => {
+        if (!util.isAuthorised(msg.member)) return;
+        let guideName = s.substring("delete guide ".length).trim();
+        let val = db.get("guides").find({
+            name: guideName
+        });
+
+        if (val.isUndefined().value()) {
+            msg.channel.send(`${msg.author.toString()} No guide "${guideName}"!`);
+            return;
+        }
+
+        db.get("guides")
+            .remove({
+                name: guideName
+            })
+            .write();
+        msg.channel.send(
+            `${msg.author.toString()} Removed guide "${guideName}"!`
+        );
+    }
+};
+
+const onDirectMention = (msg, content, actionsDone) => {
+    if (actionsDone)
+        return false;
+
+    let parts = content.trim().split(" ");
+    let guide = db
+        .get("guides")
+        .map(g => Object.assign({
+            parts: g.name.toLowerCase().split(" ")
+        }, g))
+        .sortBy(g => g.parts.length)
+        .maxBy(k => db._.intersection(parts, k.parts).length)
+        .value();
+    
+    let hits =
+        guide !== undefined &&
+        db._.intersection(guide.name.toLowerCase().split(" "), parts).length > 0;
+    
+    if (hits) {
+        msg.channel.send(guide.content);
+        return true;
+    }
+    return false;
+};
+
+module.exports = {
+    commands: commands,
+    onDirectMention: onDirectMention
+};

+ 18 - 0
commands/help.js

@@ -0,0 +1,18 @@
+const db = require("../db.js");
+
+const commands = {
+    "help": msg => {
+        let guides = db
+            .get("guides")
+            .map(g => g.name)
+            .reduce((p, c) => `${p}\n${c}`, "")
+            .value();
+        msg.channel.send(
+            `Hello! I am NoctBot! I have answers for the most common C(O)M-related questions.\nJust ping me with one of the following keywords:\n\`\`\`${guides}\`\`\``
+        );
+    }
+};
+
+module.exports = {
+    commands: commands
+};

+ 22 - 0
commands/image_statistics.js

@@ -0,0 +1,22 @@
+const fs = require("fs");
+const util = require("../util.js");
+const path = require("path");
+
+const statsFilePath = path.resolve(path.dirname(module.filename), "../imagestats.csv");
+const statsFile = fs.openSync(statsFilePath, "a");
+
+const onMessage = msg => {
+    let imagesCount = msg.attachments.filter(v => util.isValidImage(v.filename)).size;
+
+    if(imagesCount > 0) {
+        let now = new Date();
+        fs.writeSync(statsFile, `${now.getUTCFullYear()}-${now.getUTCMonth()+1}-${now.getUTCDate()} ${now.getUTCHours()}:${now.getUTCMinutes()};${imagesCount};${msg.channel.name}\n`);
+    }
+
+    return false;
+};
+
+module.exports = {
+    onMessage: onMessage
+};
+

+ 88 - 0
commands/react.js

@@ -0,0 +1,88 @@
+const db = require("../db.js");
+const util = require("../util.js");
+const client = require("../client.js");
+
+const commands = {
+    "react to": (msg, s) => {
+        if (!util.isAuthorised(msg.member)) return;
+        const pattern = /^react to\s+"([^"]+)"\s+with\s+\<:[^:]+:([^\>]+)\>$/i;
+        let contents = pattern.exec(s);
+        if (contents != null) {
+            let reactable = contents[1].trim().toLowerCase();
+            let reactionEmoji = contents[2];
+            if (!client.emojis.has(reactionEmoji)) {
+                msg.channel.send(`${msg.author.toString()} I cannot react with this emoji :(`);
+                return;
+            }
+            db.get("messageReactions").set(reactable, reactionEmoji).write();
+            msg.channel.send(`${msg.author.toString()} Added reaction!`);
+        }
+    },
+    "remove reaction to": (msg, s) => {
+        if (!util.isAuthorised(msg.member)) return;
+        let content = s.substring("remove reaction to ".length).trim().toLowerCase();
+        if (!db.get("messageReactions").has(content).value()) {
+            msg.channel.send(`${msg.author.toString()} No such reaction available!`);
+            return;
+        }
+        db.get("messageReactions").unset(content).write();
+        msg.channel.send(`${msg.author.toString()} Removed reaction!`);
+    },
+    "reactions": msg => {
+        let reactions = db.get("messageReactions").keys().value().reduce((p, c) => `${p}\n${c}`, "");
+        msg.channel.send(`I'll react to the following messages:\n\`\`\`${reactions}\`\`\``);
+    }
+};
+
+const onMessage = (msg, content, actionsDone) => {
+    if (actionsDone)
+        return false;
+
+    let lowerContent = content.toLowerCase();
+    if (db.get("messageReactions").has(lowerContent).value()) {
+        msg.react(client.emojis.get(db.get("messageReactions").get(lowerContent).value()));
+        return true;
+    }
+
+    if (msg.mentions.users.size == 0)
+        return false;
+
+    if (!db.get("reactableMentionedUsers").intersectionWith(msg.mentions.users.map(u => u.id)).isEmpty().value()) {
+        const emoteId = db
+            .get("emotes")
+            .get("angery")
+            .randomElement()
+            .value();
+        msg.react(client.emojis.find(e => e.id == emoteId));
+        return true;
+    }
+
+    return false;
+};
+
+const onIndirectMention = (msg, actionsDone) => {
+    if(actionsDone)
+        return false;
+    let emoteType = "angery";
+    if (db.get("specialUsers").includes(msg.author.id).value())
+        emoteType = "hug";
+    else if (db.get("bigUsers").includes(msg.author.id).value())
+        emoteType = "big";
+    else if (db.get("dedUsers").includes(msg.author.id).value())
+        emoteType = "ded";
+
+    const id = db
+        .get("emotes")
+        .get(emoteType)
+        .randomElement()
+        .value();
+    console.log(id);
+    msg.channel.send(client.emojis.find(e => e.id == id).toString());
+    return true;
+};
+
+module.exports = {
+    commands: commands,
+    onMessage: onMessage,
+    onIndirectMention: onIndirectMention
+};

+ 60 - 0
commands/rss_checker.js

@@ -0,0 +1,60 @@
+const TurndownService = require("turndown");
+const RSSParser = require("rss-parser");
+const db = require("../db.js");
+const interval = require("interval-promise");
+const client = require("../client.js");
+
+const turndown = new TurndownService();
+turndown.addRule("image", {
+    filter: "img",
+    replacement: () => ""
+});
+turndown.addRule("link", {
+    filter: node => node.nodeName === "A" &&node.getAttribute("href"),
+    replacement: (content, node) => node.getAttribute("href")
+});
+
+const parser = new RSSParser();
+const RSS_UPDATE_INTERVAL_MIN = 5;
+
+function getThreadId(url) {
+    let result = url.substring(url.lastIndexOf(".") + 1);
+    if(result.endsWith("/"))
+        result = result.substring(0, result.length - 1);
+    return result;
+}
+
+async function checkFeeds() {
+    console.log(`Checking feeds on ${new Date().toISOString()}`);
+    let feeds = db.get("rssFeeds").value();
+    let outlets = db.get("feedOutputs").value();
+    let oldNews = db.get("postedNewsGuids");
+    for(let feedEntry of feeds) {
+        let feed = await parser.parseURL(feedEntry.url);
+        if(feed.items.length == 0)
+            continue;
+        let printableItems = feed.items.filter(i => i.isoDate > feedEntry.lastUpdate).sort((a, b) => a.isoDate.localeCompare(b.isoDate));
+        if(printableItems.length > 0) {
+            printableItems.forEach(item => {
+                let itemID = getThreadId(item.guid);
+                if(oldNews.has(itemID).value())
+                    return;
+                outlets.forEach(ch => {
+                    client.channels.get(ch).send(`**${item.title}**\nPosted by ${item.creator}\n${item.link}\n\n${turndown.turndown(item[feedEntry.contentElement]).replace(/( {2}\n|\n\n){2,}/gm, "\n").replace(item.link, "")}`);
+                });
+                oldNews.set(itemID, true).write();
+            });
+            let lastUpdateDate = printableItems[printableItems.length - 1].isoDate;
+            console.log(`Setting last update marker on ${feedEntry.url} to ${lastUpdateDate}`);
+            db.get("rssFeeds").find({ url: feedEntry.url}).assign({lastUpdate: lastUpdateDate}).write();
+        }
+    }
+}
+
+const onStart = () => {
+    interval(checkFeeds, RSS_UPDATE_INTERVAL_MIN * 60 * 1000);
+};
+
+module.exports = {
+    onStart: onStart
+};

+ 74 - 291
main.js

@@ -1,278 +1,53 @@
-const Discord = require("discord.js");
 const TOKEN = require("./token.js");
-const db = require("./db.js");
-const RSSParser = require("rss-parser");
-const interval = require("interval-promise");
-const TurndownService = require("turndown");
 const fs = require("fs");
-const Jimp = require("jimp");
-const Clarifai = require("clarifai");
-const ClarifaiTOKEN = require("./clarifai_keys.js");
-
-const EMOTE_GUILD = "505333548694241281";
-
-const VALID_EXTENSIONS = new Set([
-    "png",
-    "jpg",
-    "jpeg",
-    "tiff",
-    "tif",
-    "bmp",
-    "webp"
-]);
-
-const clarifaiApp = new Clarifai.App({
-    apiKey: ClarifaiTOKEN
-});
-
-const turndown = new TurndownService();
-turndown.addRule("image", {
-    filter: "img",
-    replacement: () => ""
-});
-turndown.addRule("link", {
-    filter: node => node.nodeName === "A" &&node.getAttribute("href"),
-    replacement: (content, node) => node.getAttribute("href")
-});
-
-const statsFile = fs.openSync(__dirname + "/imagestats.csv", "a");
-
-const client = new Discord.Client();
-const parser = new RSSParser();
-const RSS_UPDATE_INTERVAL_MIN = 5;
+const path = require("path");
+const client = require("./client.js");
 
 const REACT_PROBABILITY = 0.6;
 
-function isValidImage(fileName) {
-    let extPosition = fileName.lastIndexOf(".");
-    if(extPosition < 0)
-        return false;
-    let ext = fileName.substring(extPosition + 1).toLowerCase();
-    return VALID_EXTENSIONS.has(ext);
-}
-
-function getThreadId(url) {
-    let result = url.substring(url.lastIndexOf(".") + 1);
-    if(result.endsWith("/"))
-        result = result.substring(0, result.length - 1);
-    return result;
-}
-
-async function checkFeeds() {
-    console.log(`Checking feeds on ${new Date().toISOString()}`);
-    let feeds = db.get("rssFeeds").value();
-    let outlets = db.get("feedOutputs").value();
-    let oldNews = db.get("postedNewsGuids");
-    for(let feedEntry of feeds) {
-        let feed = await parser.parseURL(feedEntry.url);
-        if(feed.items.length == 0)
-            continue;
-        let printableItems = feed.items.filter(i => i.isoDate > feedEntry.lastUpdate).sort((a, b) => a.isoDate.localeCompare(b.isoDate));
-        if(printableItems.length > 0) {
-            printableItems.forEach(item => {
-                let itemID = getThreadId(item.guid);
-                if(oldNews.has(itemID).value())
-                    return;
-                outlets.forEach(ch => {
-                    client.channels.get(ch).send(`**${item.title}**\nPosted by ${item.creator}\n${item.link}\n\n${turndown.turndown(item[feedEntry.contentElement]).replace(/( {2}\n|\n\n){2,}/gm, "\n").replace(item.link, "")}`);
-                });
-                oldNews.set(itemID, true).write();
-            });
-            let lastUpdateDate = printableItems[printableItems.length - 1].isoDate;
-            console.log(`Setting last update marker on ${feedEntry.url} to ${lastUpdateDate}`);
-            db.get("rssFeeds").find({ url: feedEntry.url}).assign({lastUpdate: lastUpdateDate}).write();
-        }
+function trigger(actions, ...params) {
+    let actionDone = false;
+    for (let i = 0; i < actions.length; i++) {
+        const action = actions[i];
+        actionDone |= action(...params, actionDone);
     }
+    return actionDone;
 }
 
-function isAuthorised(member) {
-    if (db.get("editors.users").includes(member.id).value())
-        return true;
-    if (db.get("editors.roles").intersectionWith(member.roles.keyArray()).isEmpty().value())
-        return false;
-    return true;
-}
-
-async function processFaceSwap(message, attachmentUrl) {
-    let result = await clarifaiApp.models.predict(Clarifai.FACE_DETECT_MODEL, attachmentUrl);
-
-    if(result.outputs[0].data.regions === undefined || result.outputs[0].data.regions.length == 0)
-        return;
-    
-    let image = await Jimp.read(attachmentUrl);
-    let w = image.getWidth();
-    let h = image.getHeight();
-
-    let emojiKeys = [...client.guilds.get(EMOTE_GUILD).emojis.filter(e => !e.animated).keys()];
-
-    for(let region of result.outputs[0].data.regions) {
-        let bb = region.region_info.bounding_box;
-        let bw = (bb.right_col - bb.left_col) * w;
-        let bh = (bb.bottom_row - bb.top_row) * h;
-        
-        let dx = (bb.right_col + bb.left_col) * w / 2;
-        let dy = (bb.bottom_row + bb.top_row) * h / 2;
-
-        let emojiKey = emojiKeys[Math.floor(Math.random() * emojiKeys.length)];
-        let emoji = client.emojis.get(emojiKey);
-        
-        let emojiImage = await Jimp.read(emoji.url);
-        let ew = emojiImage.getWidth();
-        let eh = emojiImage.getHeight();
-
-        const CONSTANT_SCALE = 1.5;
-        let scaleFactor = Math.max(bw, bh) / Math.min(ew, eh) * CONSTANT_SCALE;
-        ew *= scaleFactor;
-        eh *= scaleFactor;
-
-        emojiImage = emojiImage.scale(scaleFactor);
-        image = image.composite(emojiImage, dx - ew / 2, dy - eh / 2);
-    }
-
-    image.quality(90);
-    let buffer = await image.getBufferAsync(Jimp.MIME_JPEG);
+let commands = {};
+let msgActions = [];
+let indirectMentionActions = [];
+let startActions = [];
+let directMessageActions = [];
+let postActions = [];
 
-    message.channel.send(`I noticed a face in the image. I think this looks better ${client.emojis.get("505076258753740810").toString()}`, {
-        files: [ buffer ]
+client.on("ready", () => {
+    console.log("Starting up NoctBot!");
+    client.user.setActivity("@NoctBot help", {
+        type: "PLAYING"
     });
-}
-
-function faceMorph(message) {
-    let probValue = db.get("faceEditChannels").get(message.channel.id);
-    if(probValue.isUndefined().value() || probValue.isNull().value())
-        return;
-
-    if(Math.random() > probValue.value())
-        return;
-
-    let imageAttachment = message.attachments.find(v => isValidImage(v.filename));
-    processFaceSwap(message, imageAttachment.url).catch(err => console.log(`Failed to run faceapp because ${err}`));
-}
-
-const commands = {
-    "make guide": msg => {
-        if (!isAuthorised(msg.member)) return;
-        let content = msg.content.substring(msg.content.indexOf("make guide") + "make guide ".length);
-        let guideName = content.substring(0, content.indexOf("\n")).trim();
-        let guideContent = content.substring(content.indexOf("\n")).trim();
-
-        let guide = db.get("guides").find({ name: guideName });
-
-        if (!guide.isUndefined().value()) {
-            guide.assign({ content: guideContent }).write();
-        } else {
-            db.get("guides")
-                .push({
-                    name: guideName,
-                    content: guideContent
-                })
-                .write();
-        }
-
-        msg.channel.send(
-            `${msg.author.toString()} Added/updated "${guideName}"!`
-        );
-    },
-    "delete guide": (msg, s) => {
-        if (!isAuthorised(msg.member)) return;
-        let guideName = s.substring("delete guide ".length).trim();
-        let val = db.get("guides").find({ name: guideName });
-
-        if (val.isUndefined().value()) {
-            msg.channel.send(`${msg.author.toString()} No guide "${guideName}"!`);
-            return;
-        }
-
-        db.get("guides")
-            .remove({ name: guideName })
-            .write();
-        msg.channel.send(
-            `${msg.author.toString()} Removed guide "${guideName}"!`
-        );
-    },
-    "react to": (msg, s) => {
-        if (!isAuthorised(msg.member)) return;
-        const pattern = /^react to\s+"([^"]+)"\s+with\s+\<:[^:]+:([^\>]+)\>$/i;
-        let contents = pattern.exec(s);
-        if(contents != null) {
-            let reactable = contents[1].trim().toLowerCase();
-            let reactionEmoji = contents[2];
-            if(!client.emojis.has(reactionEmoji)){
-                msg.channel.send(`${msg.author.toString()} I cannot react with this emoji :(`);
-                return;
-            }
-            db.get("messageReactions").set(reactable, reactionEmoji).write();
-            msg.channel.send(`${msg.author.toString()} Added reaction!`);
-        }
-    },
-    "remove reaction to": (msg, s) => {
-        if (!isAuthorised(msg.member)) return;
-        let content = s.substring("remove reaction to ".length).trim().toLowerCase();
-        if(!db.get("messageReactions").has(content).value()) {
-            msg.channel.send(`${msg.author.toString()} No such reaction available!`);
-            return;
-        }
-        db.get("messageReactions").unset(content).write();
-        msg.channel.send(`${msg.author.toString()} Removed reaction!`);
-    },
-    "reactions": msg => {
-        let reactions = db.get("messageReactions").keys().value().reduce((p, c) => `${p}\n${c}`, "");
-        msg.channel.send(`I'll react to the following messages:\n\`\`\`${reactions}\`\`\``);
-    },
-    "help": msg => {
-        let guides = db
-            .get("guides")
-            .map(g => g.name)
-            .reduce((p, c) => `${p}\n${c}`, "")
-            .value();
-        msg.channel.send(
-            `Hello! I am NoctBot! I have answers for the most common C(O)M-related questions.\nJust ping me with one of the following keywords:\n\`\`\`${guides}\`\`\``
-        );
+    for (let i = 0; i < startActions.length; i++) {
+        const action = startActions[i];
+        action();
     }
-};
-
-client.on("ready", () => {
-    console.log("Ready!");
-    client.user.setActivity("@NoctBot help", { type: "PLAYING" });
-    interval(checkFeeds, RSS_UPDATE_INTERVAL_MIN * 60 * 1000);
+    console.log("NoctBot is ready!");
 });
 
 client.on("message", m => {
-    if (m.author.id == client.user.id) return;
-
-    let imagesCount = m.attachments.filter(v => isValidImage(v.filename)).size;
-
-    if(imagesCount > 0) {
-        let now = new Date();
-        fs.writeSync(statsFile, `${now.getUTCFullYear()}-${now.getUTCMonth()+1}-${now.getUTCDate()} ${now.getUTCHours()}:${now.getUTCMinutes()};${imagesCount};${m.channel.name}\n`);
-        faceMorph(m);
-    }
+    if (m.author.id == client.user.id) 
+        return;
 
     let content = m.cleanContent.trim();
-    let lowerContent = content.toLowerCase();
-    if(db.get("messageReactions").has(lowerContent).value()) {
-        m.react(client.emojis.get(db.get("messageReactions").get(lowerContent).value()));
-        return;
-    }
 
-    if(m.mentions.users.size == 0)
+    if (trigger(msgActions, m, content))
         return;
 
-    if (!db.get("reactableMentionedUsers").intersectionWith(m.mentions.users.map(u => u.id)).isEmpty().value()) {
-        const emoteId = db
-            .get("emotes")
-            .get("angery")
-            .randomElement()
-            .value();
-        m.react(client.emojis.find(e => e.id == emoteId));
-        return;
-    }
+    if (m.mentions.users.size > 0 && m.mentions.users.first().id == client.user.id) {
+
+        if (content.startsWith(`@${client.user.username}`)) {
+            content = content.substring(`@${client.user.username}`.length).trim();
 
-    if (m.mentions.users.first().id == client.user.id) {
-        if(content.startsWith(`@${client.user.username}`)) {
-            content = content
-                .substring(`@${client.user.username} `.length);
-            let lowerCaseContent = content.toLowerCase().trim();
+            let lowerCaseContent = content.toLowerCase();
             for (let c in commands) {
                 if (lowerCaseContent.startsWith(c)) {
                     commands[c](m, content);
@@ -280,48 +55,56 @@ client.on("message", m => {
                 }
             }
 
-            if (lowerCaseContent.length > 0) {
-                let parts = lowerCaseContent.trim().split(" ");
-                let guide = db
-                    .get("guides")
-                    .map(g => Object.assign({parts: g.name.toLowerCase().split(" ")}, g))
-                    .sortBy(g => g.parts.length)
-                    .maxBy(k => db._.intersection(parts, k.parts).length)
-                    .value();
-                let hits =
-                    guide !== undefined &&
-                    db._.intersection(guide.name.toLowerCase().split(" "), parts).length > 0;
-                if (hits) {
-                    m.channel.send(guide.content);
-                    return;
-                }
-            }
+            if (lowerCaseContent.length > 0 && trigger(directMessageActions, m, lowerCaseContent))
+                return;
         }
 
-        let emoteType = "angery";
-        if(db.get("specialUsers").includes(m.author.id).value())
-            emoteType = "hug";
-        else if(db.get("bigUsers").includes(m.author.id).value())
-            emoteType = "big";
-        else if(db.get("dedUsers").includes(m.author.id).value())
-            emoteType = "ded";
-
-        const id = db
-            .get("emotes")
-            .get(emoteType)
-            .randomElement()
-            .value();
-        m.channel.send(client.emojis.find(e => e.id == id).toString());
-    } else if (m.content.includes("Noct")) {
-        m.channel.send(
-            client.emojis.find(e => e.name == "mukuNeighWaaaaaa").toString()
-        );
+        if (trigger(indirectMentionActions, m))
+            return;
     }
+
+    trigger(postActions);
 });
 
 client.on("messageReactionAdd", (r, u) => {
-    if(Math.random() <= REACT_PROBABILITY && !r.me)
+    if (Math.random() <= REACT_PROBABILITY && !r.me)
         r.message.react(r.emoji);
 });
 
-client.login(TOKEN);
+function main() {
+    let commandsPath = path.resolve(path.dirname(module.filename), "commands");
+    let files = fs.readdirSync(commandsPath);
+
+    for (let i = 0; i < files.length; i++) {
+        const file = files[i];
+        let ext = path.extname(file);
+        if (ext != ".js")
+            continue;
+
+        let obj = require(path.resolve(commandsPath, file));
+        if (obj.commands) {
+            for (let command in obj.commands) {
+                if (obj.commands.hasOwnProperty(command))
+                    commands[command] = obj.commands[command];
+            }
+        }
+        if (obj.onMessage)
+            msgActions.push(obj.onMessage);
+
+        if (obj.onIndirectMention)
+            indirectMentionActions.push(obj.onIndirectMention);
+
+        if (obj.onDirectMention)
+            directMessageActions.push(obj.onDirectMention);
+
+        if (obj.postMessage)
+            postActions.push(obj.postMessage);
+
+        if (obj.onStart)
+            startActions.push(obj.onStart);
+    }
+
+    client.login(TOKEN);
+}
+
+main();

+ 32 - 0
util.js

@@ -0,0 +1,32 @@
+const db = require("./db.js");
+
+const VALID_EXTENSIONS = new Set([
+    "png",
+    "jpg",
+    "jpeg",
+    "tiff",
+    "tif",
+    "bmp",
+    "webp"
+]);
+
+function isValidImage(fileName) {
+    let extPosition = fileName.lastIndexOf(".");
+    if(extPosition < 0)
+        return false;
+    let ext = fileName.substring(extPosition + 1).toLowerCase();
+    return VALID_EXTENSIONS.has(ext);
+}
+
+function isAuthorised(member) {
+    if (db.get("editors.users").includes(member.id).value())
+        return true;
+    if (db.get("editors.roles").intersectionWith(member.roles.keyArray()).isEmpty().value())
+        return false;
+    return true;
+}
+
+module.exports = {
+    isAuthorised: isAuthorised,
+    isValidImage: isValidImage
+};