commit 3f7a3134e4e4978549f0bc89f5188a4bdfe2df4d Author: danbulant Date: Tue Sep 1 13:04:00 2020 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..25c8fdb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +package-lock.json \ No newline at end of file diff --git a/config.json b/config.json new file mode 100644 index 0000000..eacabc4 --- /dev/null +++ b/config.json @@ -0,0 +1,3 @@ +{ + "token": "NzQ5MTc3MzEzNjE4NTU4OTk3.X0oLvg.ux4hH4E43YCj0xy3xAAJ6QkEQmQ" +} \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..02bcdf9 --- /dev/null +++ b/index.js @@ -0,0 +1,115 @@ +const config = require("./config.json"); +const discord = require("discord.js"); +const { app, answers, messages, challenge, points } = require("./src"); + +const client = new discord.Client({ + presence: { + status: "online", + activity: { + name: "CNMC", + type: "PLAYING" + } + } +}); + +client.on("ready", () => { + console.log("Ready"); +}); + +client.on("message", async msg => { + try { + await app.message(msg); + } catch(e) { + console.error("APP", e); + } +}); + +function check(member, bonus = 0) { + var pointNum = bonus + (answers.get(member.user.id) ? (0.5 + ((challenge.get("num") || 1) * 0.5)) : 0); + if(points.get(member.user.id) === pointNum) return; + if(points.get(member.user.id) && points.get(member.user.id) > 1) return; + var diff = pointNum - (points.get(member.user.id) || 0); + points.set(pointNum); + member.setNickname(member.displayName.replace(/\[([0-9.]+)\]/, (match, num) => { + return `[${parseFloat(num) + diff}]`; + })); +} + +client.on("messageDelete", msg => { + if(messages.get(msg.id)) { + messages.delete(msg.id); + messages.delete(msg.id); + var an = answers.get(msg.author.id) || 0; + if(an) answers.set(msg.author.id, an - 1); + check(msg.member); + } +}); + +client.on("messageReactionAdd", async (react, user) => { + if(react.message.channel.id !== "745989024099074068") return; + if(user.id !== "694395936809418816") { + if(user.id !== client.user.id) { + await react.users.remove(user); + } + return; + } + if(user.id === client.user.id) return; + if(react.emoji.name !== "❌" && react.emoji.name !== "✅") return; + if(react.emoji.name === "✅") { + var r = await react.message.reactions.resolve("❌"); + await r.remove(); + + var points = 0; + + if(challenge.get("hint")) { + var msg = await react.message.channel.messages.fetch(challenge.get("hint")); + if(msg.createdTimestamp < react.message.createdTimestamp) { + points -= 0.5; + } + } + + if(!challenge.get("answered")) { + challenge.set("answered", 0); + } + switch(challenge.get("answered")) { + case 0: + points += 2; + break; + case 1: + points += 1.5; + break; + case 2: + points += 1; + break; + default: + points += 0.5; + } + challenge.set("answered", challenge.get("answered") + 1); + + if(points > 0) { + check(react.message.member, points); + } + + react.message.channel.messages.cache.filter(val => val.author.id === react.message.author.id) + .filter(val => val.id !== react.message.id) + .forEach(msg => msg.reactions.removeAll()); + } else { + var r = await react.message.reactions.resolve("✅"); + await r.remove(); + } +}) + +client.on("messageUpdate", (msg, msg2) => { + if(msg.channel.id === "745989024099074068") { + if(messages.get(msg.id)) { + messages.delete(msg.id); + var an = answers.get(msg.author.id) || 0; + if(an) answers.set(msg.author.id, an - 1); + check(msg.member); + } + msg.author.send("You've edited the messages so it got deleted."); + msg.delete(); + } +}); + +client.login(config.token); \ No newline at end of file diff --git a/lib/middleware.js b/lib/middleware.js new file mode 100644 index 0000000..8360c6b --- /dev/null +++ b/lib/middleware.js @@ -0,0 +1,119 @@ + + +module.exports = class Router { + /** + * @type {{ + path: string, + callback: function(Message): boolean | string, + break: boolean, + opts: { + guildOnly: boolean, + permissions: string[], + clientPermissions: string[], + ownerOnly: boolean + } + }[]} middlewares + */ + middlewares = []; + + /** + * Registers a command + * @param {string} path command format + * @param {function(Message): boolean | string} callback to call on match + * @param {{ + guildOnly: boolean, + permissions: string[], + clientPermissions: string[] + }} opts options to use + */ + command(path, callback, opts = {}) { + this.middlewares.push({ + path, + callback, + opts, + break: true + }); + + return this; + } + + use(path, router, opts = {}) { + if(typeof path !== "string") { + [router, opts] = [path, router]; + path = ""; + } + if(typeof router === "function") { + this.middlewares.push({ + path, + callback: router, + opts, + break: false + }); + return this; + } + for(var middleware of router.middlewares) { + this.middlewares.push({ + ...middleware, + path: (path + " " + middleware.path).trim(), + opts: Object.assign(middleware.opts, opts) + }); + } + return this; + } + + processPath(path, content) { + if(!path) return {}; + var segments = path.split(" "); + var contentSegments = content.split(" "); + if(!contentSegments) return null; + if(segments.length > contentSegments.length) return null; + var args = {}; + for(var segmentID in segments) { + var segment = segments[segmentID]; + if(segment.startsWith(":")) { + args[segment.substr(1)] = contentSegments[segmentID]; + } else if(segment !== contentSegments[segmentID]) return null; + } + var keys = segments.filter(segment => segment.startsWith(":")).map(segment => segment.substr(1)); + if(contentSegments.length > segments.length && keys.length) { + args[keys[keys.length - 1]] += contentSegments.splice(segments.length).join(" "); + } + return args; + } + + /** + * + * @param {Message} msg + * @param {{ + guildOnly: boolean, + permissions: string[], + clientPermissions: string[] + }} opts options to use + */ + async checkOpts(msg, opts) { + if(!opts) return true; + if(!msg.guild && opts.guildOnly) return false; + if(opts.permissions && msg.member) + for(var permission of opts.permissions) + if(!msg.member.hasPermission(permission)) return false; + if(opts.clientPermissions && msg.guild.me) + for(var permission of opts.clientPermissions) + if(!msg.guild.me.hasPermission(permission)) return false; + return true; + } + + async message(msg) { + var middlewares = this.middlewares; + + for(var middleware of middlewares) { + var args = this.processPath(middleware.path, msg.content); + if(args === null) continue; + if(!this.checkOpts(msg, middleware.opts)) continue; + var result = await middleware.callback(msg, args); + if(typeof result === "string") { + await msg.channel.send(result); + } + if(result === false || middleware.break) break; + } + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..574114b --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "cnmc", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "discord.js": "^12.3.1" + } +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..0033840 --- /dev/null +++ b/src/index.js @@ -0,0 +1,58 @@ +const App = require("../lib/middleware"); +const app = new App(); + +const answers = new Map(); +const messages = new Map(); +const challenge = new Map(); +const points = new Map(); + +app + .use(msg => !msg.author.bot) // ignore bots + .use(async msg => { + if(msg.author.id === "694395936809418816" && msg.content === "!reset") { + answers.clear(); + messages.clear(); + challenge.clear(); + msg.reply("done."); + return false; + } else if(msg.author.id === "694395936809418816" && msg.content.startsWith("!start")) { + var num = msg.content.split(" ")[1][0]; + if(isNaN(parseInt(num))) { + msg.reply("Invalid type, must start with a number"); + return false; + } + challenge.set("num", num); + msg.reply("challenge type set."); + return false; + } + }) + .use(msg => msg.channel.id === "745989024099074068") + .use(msg => { + if(challenge.get("hint")) return; + if(msg.author.id === "694395936809418816") { + challenge.set("hint", msg.id); + return false; + } + }) + .use(msg => { + if(!/^\[[0-9.]+\]/.test(msg.member.displayName)) { + msg.author.send("You must register first"); + msg.delete(); + return false; + } + }) + .use(async msg => { + var answerCount = answers.get(msg.author.id) || 0; + answerCount++; + answers.set(msg.author.id, answerCount); + if(answerCount > 3) return await msg.delete(); + if(answerCount === 3) { + await msg.author.send("You've used all your 3 answers, you won't be able to add any more."); + } + console.log("New answer:", msg.content, "by", msg.member.displayName); + messages.set(msg.id, 1); + msg.react("✅"); + msg.react("❌"); + }) + +module.exports = { app, answers, messages, challenge, points }; \ No newline at end of file