|
|
@@ -1,205 +1,8 @@
|
|
|
|
|
|
-const irc = require("irc"),
|
|
|
- fs = require("fs"),
|
|
|
- readline = require("readline"),
|
|
|
- cache = new (require("data-store"))({ path: "persistentDb.json" }),
|
|
|
- arrayPad = require('./strpad.js').arrayPad;
|
|
|
+const irc = require("irc");
|
|
|
|
|
|
Object.assign(global, require("./config.js"));
|
|
|
|
|
|
-const MySQL = USE_MYSQL ? require("mysql2").createConnection({host: MySQL_HOST, user: MySQL_USER, database: MySQL_DB, password: MySQL_PASS}) : null;
|
|
|
-
|
|
|
-const HOSTNAME = require('os').hostname(); // For Mysql bot identification
|
|
|
-
|
|
|
-function Cache() {}
|
|
|
-Cache.SetScores = function(scores) {
|
|
|
- var dataToSet = {};
|
|
|
- for (var i in scores)
|
|
|
- if (scores[i].score)
|
|
|
- dataToSet[scores[i].name] = scores[i].score;
|
|
|
- cache.set("scores", dataToSet);
|
|
|
- cache.set("saveTs", Date.now());
|
|
|
-}
|
|
|
-
|
|
|
-Cache.getReportedQuestions = function(questionId, username) {
|
|
|
- return cache.get("report") || {};
|
|
|
-}
|
|
|
-
|
|
|
-Cache.reportQuestion = function(questionId, username) {
|
|
|
- var reported = cache.get("report") || {};
|
|
|
- var questionReported = reported[questionId] || {};
|
|
|
- reported[questionId] = questionReported;
|
|
|
- if (questionReported[username])
|
|
|
- return;
|
|
|
- questionReported[username] = Date.now();
|
|
|
- cache.set("report", reported);
|
|
|
-}
|
|
|
-
|
|
|
-Cache.clearReports = function() {
|
|
|
- cache.set("report", {});
|
|
|
-}
|
|
|
-
|
|
|
-Cache.unreportQuestion = function(questionId) {
|
|
|
- var reported = cache.get("report") || {};
|
|
|
- if (!reported[questionId])
|
|
|
- return;
|
|
|
- delete reported[questionId];
|
|
|
- cache.set("report", reported);
|
|
|
-}
|
|
|
-
|
|
|
-Cache.setExportTs = function() {
|
|
|
- cache.set("mysqlTs", Date.now());
|
|
|
-}
|
|
|
-
|
|
|
-Cache.getLastMysqlSave = function() {
|
|
|
- return cache.get("mysqlTs") || 0;
|
|
|
-}
|
|
|
-
|
|
|
-Cache.GetData = function() {
|
|
|
- return {
|
|
|
- scores: cache.get("scores"),
|
|
|
- lastSave: cache.get("saveTs") || 0,
|
|
|
- lastMysqlExport: cache.get("mysqlTs") || 0
|
|
|
- };
|
|
|
-}
|
|
|
-
|
|
|
-function Question(id, obj) {
|
|
|
- this.id = id;
|
|
|
- this.question = obj.question;
|
|
|
- this.response = obj.response;
|
|
|
- this.normalizedResponse = "";
|
|
|
- if (Array.isArray(obj.response))
|
|
|
- this.normalizedResponse = this.response.map(i => Question.normalize(i));
|
|
|
- else
|
|
|
- this.normalizedResponse = Question.normalize(this.response);
|
|
|
-}
|
|
|
-
|
|
|
-Question.normalize = function(str) {
|
|
|
- return str.normalize('NFD').replace(/[\u0300-\u036f]/g, "").toLowerCase().trim();
|
|
|
-}
|
|
|
-
|
|
|
-const QuestionType = { bool: {}, number: {}, string: {} };
|
|
|
-
|
|
|
-Question.prototype.booleanValue = function(str) {
|
|
|
- str = str || (Array.isArray(this.normalizedResponse) ? this.normalizedResponse[0] : this.normalizedResponse);
|
|
|
- var index = ["non", "oui", "faux", "vrai"].indexOf(str);
|
|
|
- if (index >= 0)
|
|
|
- return index % 2 === 1;
|
|
|
- return undefined;
|
|
|
-}
|
|
|
-
|
|
|
-Question.prototype.isBoolean = function(str) {
|
|
|
- return this.booleanValue(str) !== undefined;
|
|
|
-}
|
|
|
-
|
|
|
-String.prototype.isWord = function() {
|
|
|
- return (/\W/).exec(this) === null;
|
|
|
-}
|
|
|
-
|
|
|
-Question.prototype.getQuestionType = function() {
|
|
|
- if (this.isBoolean())
|
|
|
- return QuestionType.bool;
|
|
|
- if (!isNaN(Number(this.response)))
|
|
|
- return QuestionType.number;
|
|
|
- return QuestionType.string;
|
|
|
-}
|
|
|
-
|
|
|
-Question.toHint = function(response, normalizedResponse, boundaries, responseIndex, questionType, hintLevel) {
|
|
|
- if (questionType === QuestionType.string) {
|
|
|
- if (hintLevel == 0)
|
|
|
- return normalizedResponse.replace(/[\w]/g, '*');
|
|
|
- else if (hintLevel == 1)
|
|
|
- return normalizedResponse.replace(/[\w]/g, (a, b) => b ? '*' : response.charAt(b));
|
|
|
- else if (normalizedResponse.isWord() && !responseIndex) {
|
|
|
- var displayed = [];
|
|
|
- const revealPercent = 0.1;
|
|
|
- displayed[normalizedResponse.length -2] = 1;
|
|
|
- displayed.fill(1, 0, normalizedResponse.length-1).fill(0, Math.ceil(normalizedResponse.length * revealPercent), normalizedResponse.length);
|
|
|
- displayed.sort(()=>Math.random() > 0.5?-1:1)
|
|
|
- return normalizedResponse.replace(/./g, (a, b) => b && !displayed[b -1] ? '*':response.charAt(b));
|
|
|
- }
|
|
|
- else
|
|
|
- return normalizedResponse.replace(/[\w]+/g, (a, wordIndex) => a.replace(/./g, (a, b) => b ? '*':response.charAt(wordIndex)));
|
|
|
- }
|
|
|
- else if (questionType === QuestionType.number) {
|
|
|
- const responseInt = Number(normalizedResponse),
|
|
|
- randomMin = ([ 30, 10, 3 ])[hintLevel],
|
|
|
- randomSpread = 5 * (5 -hintLevel);
|
|
|
- boundaries[0] = Math.max(
|
|
|
- Math.floor(responseInt -(Math.random() *randomSpread) -randomMin),
|
|
|
- boundaries[0]);
|
|
|
- boundaries[1] = Math.min(
|
|
|
- Math.ceil(responseInt +(Math.random() *randomSpread) +randomMin),
|
|
|
- boundaries[1]);
|
|
|
- return "Un nombre entre " +boundaries[0] +" et " +boundaries[1];
|
|
|
- }
|
|
|
- else
|
|
|
- console.error("Unknown response type !");
|
|
|
-};
|
|
|
-
|
|
|
-Question.prototype.getHint = function(hintLevel) {
|
|
|
- var type = this.getQuestionType();
|
|
|
- if (type === QuestionType.bool)
|
|
|
- return "Vrai / Faux ?";
|
|
|
- else if (type === QuestionType.number && !this.boundaries)
|
|
|
- this.boundaries = (Number(this.response) >= 0 ? [ 0, Infinity] : [ -Infinity, 0 ]);
|
|
|
- if (!Array.isArray(this.response))
|
|
|
- return Question.toHint(this.response, this.normalizedResponse, this.boundaries, 0, type, hintLevel);
|
|
|
- var hints = [];
|
|
|
- for (var i =0, len = this.response.length; i < len; ++i)
|
|
|
- hints.push(Question.toHint(this.response[i], this.normalizedResponse[i], this.boundaries, i, type, hintLevel));
|
|
|
- return hints.join (" ou ");
|
|
|
-}
|
|
|
-
|
|
|
-Question.prototype.check = function(response) {
|
|
|
- response = Question.normalize(response);
|
|
|
- var boolValue = this.booleanValue();
|
|
|
- if (boolValue !== undefined)
|
|
|
- return boolValue === this.booleanValue(response);
|
|
|
- if (Array.isArray(this.normalizedResponse))
|
|
|
- return this.normalizedResponse.indexOf(response) >= 0;
|
|
|
- return response === this.normalizedResponse;
|
|
|
-}
|
|
|
-
|
|
|
-Question.prototype.end = function() {
|
|
|
- if (this.boundaries)
|
|
|
- delete this.boundaries;
|
|
|
-}
|
|
|
-
|
|
|
-function initQuestionList(filename) {
|
|
|
- return new Promise((ok, ko) => {
|
|
|
- console.log("Reloading question db");
|
|
|
- var stream = fs.createReadStream(filename),
|
|
|
- reader = readline.createInterface({input: stream}),
|
|
|
- questions = [],
|
|
|
- lineNo = 0,
|
|
|
- borken = false;
|
|
|
- reader.on("line", line => {
|
|
|
- if (borken) return;
|
|
|
- try {
|
|
|
- const firstChar = line.charAt(0);
|
|
|
- if ([';', '#'].indexOf(firstChar) >= 0) {
|
|
|
- ++lineNo;
|
|
|
- return;
|
|
|
- }
|
|
|
- var question = new Question(++lineNo, JSON.parse(line));
|
|
|
- if (question.question && question.response)
|
|
|
- questions.push(question);
|
|
|
- } catch (e) {
|
|
|
- console.error("Failed to load Database: ", e, "on line", lineNo);
|
|
|
- borken = true;
|
|
|
- reader.close();
|
|
|
- stream.destroy();
|
|
|
- ko("Failed to load database: syntax error on question #" +lineNo);
|
|
|
- }
|
|
|
- });
|
|
|
- reader.on("close", () => {
|
|
|
- if (!borken)
|
|
|
- ok(questions);
|
|
|
- });
|
|
|
- });
|
|
|
-}
|
|
|
-
|
|
|
function User(nick) {
|
|
|
this.admin = false;
|
|
|
this.name = nick;
|
|
|
@@ -217,154 +20,95 @@ User.prototype.unsetMode = function(mode) {
|
|
|
this.admin = false;
|
|
|
};
|
|
|
|
|
|
-function KnackiBot(previousData) {
|
|
|
+function KnackiBot() {
|
|
|
var _this = this;
|
|
|
this.name = IRC_BOTNAME;
|
|
|
- this.room = IRC_ROOM;
|
|
|
- this.init = false;
|
|
|
this.password = NS_PASSWORD;
|
|
|
- this.users = {};
|
|
|
- this.activeUsers = {};
|
|
|
-
|
|
|
- if (previousData) {
|
|
|
- for (var i in previousData.scores) {
|
|
|
- this.users[i.toLowerCase()] = new User(i);
|
|
|
- this.users[i.toLowerCase()].score = previousData.scores[i];
|
|
|
- }
|
|
|
- }
|
|
|
+ this.modules = MODULES;
|
|
|
|
|
|
- this.mySQLExportWrapper();
|
|
|
+ for (var i in this.modules)
|
|
|
+ this.modules[i].init(this, i);
|
|
|
|
|
|
this.bot = new irc.Client(IRC_HOSTNAME, this.name, {
|
|
|
- channels: [ this.room ],
|
|
|
+ channels: Object.keys(this.modules),
|
|
|
userName: this.name,
|
|
|
realName: this.name,
|
|
|
stripColors: true
|
|
|
});
|
|
|
- this.bot.addListener("error", console.error);
|
|
|
- this.bot.addListener("join" +this.room, (nick) => {
|
|
|
- if (nick == _this.name) {
|
|
|
- if (USE_NS) {
|
|
|
- _this.bot.say("NickServ", "recover " +_this.name +" " +_this.password);
|
|
|
- _this.bot.say("NickServ", "identify " +_this.password);
|
|
|
+ if (USE_NS)
|
|
|
+ var _this = this,
|
|
|
+ registerHandler = 0;
|
|
|
+
|
|
|
+ var tryRegister = function() {
|
|
|
+ console.log("Trying to identify using NickServ");
|
|
|
+ _this.bot.say("NickServ", "identify " +NS_PASSWORD);
|
|
|
+ };
|
|
|
+ this.bot.addListener("raw", raw => {
|
|
|
+ if (raw.rawCommand == "MODE" && raw.args[0] === _this.name) {
|
|
|
+ var registeredMatch = (/(\+|-)[^\+-]*r/).exec(raw.args[1]);
|
|
|
+ if (!registeredMatch)
|
|
|
+ return;
|
|
|
+ if (registeredMatch[1] === '-') {
|
|
|
+ if (registerHandler === undefined) {
|
|
|
+ registerHandler = setInterval(tryRegister, 30000);
|
|
|
+ tryRegister();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (registerHandler !== undefined) {
|
|
|
+ clearInterval(registerHandler);
|
|
|
+ registerHandler = undefined;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- _this.init = true;
|
|
|
- _this.reloadDb();
|
|
|
- } else {
|
|
|
- _this.users[nick.toLowerCase()] = _this.users[nick.toLowerCase()] || new User(nick);
|
|
|
- _this.activeUsers[nick.toLowerCase()] = true;
|
|
|
- }
|
|
|
- });
|
|
|
- this.bot.addListener("names"+this.room, (nicks) => {
|
|
|
- var oldusers = _this.users;
|
|
|
- _this.activeUsers = {};
|
|
|
- for (var i in nicks) {
|
|
|
- var u = _this.users[i.toLowerCase()] = (_this.users[i.toLowerCase()] || new User(i));
|
|
|
- u.setModeChar(nicks[i]);
|
|
|
- _this.activeUsers[i.toLowerCase()] = true;
|
|
|
+ });
|
|
|
+ this.bot.addListener("registered", () => {
|
|
|
+ registerHandler = setInterval(tryRegister, 30000);
|
|
|
+ tryRegister();
|
|
|
+ });
|
|
|
+ this.bot.addListener("error", console.error);
|
|
|
+ this.bot.addListener("join", (chan, nick) => {
|
|
|
+ if (_this.modules[chan]) {
|
|
|
+ if (nick == _this.name)
|
|
|
+ _this.modules[chan].onSelfJoin();
|
|
|
+ else
|
|
|
+ _this.modules[chan].onJoin(nick);
|
|
|
}
|
|
|
});
|
|
|
- this.bot.addListener("part"+this.room, (user) => delete _this.activeUsers[user.toLowerCase()]);
|
|
|
- this.bot.addListener("kick"+this.room, (user) => delete _this.activeUsers[user.toLowerCase()]);
|
|
|
- this.bot.addListener("+mode", (channel, by, mode, user) => {
|
|
|
- if (user === _this.name) {
|
|
|
- usersToVoice = [];
|
|
|
- for (var i in _this.users) {
|
|
|
- if (_this.users[i].score && _this.activeUsers[i])
|
|
|
- usersToVoice.push(i);
|
|
|
- }
|
|
|
- _this.voice(usersToVoice);
|
|
|
- } else {
|
|
|
- user && _this.users[user.toLowerCase()] && _this.users[user.toLowerCase()].setMode(mode);
|
|
|
- }
|
|
|
+ this.bot.addListener("names", (chan, nicks) => {
|
|
|
+ _this.modules[chan] && _this.modules[chan].onNameList(nicks);
|
|
|
});
|
|
|
+ this.bot.addListener("part", (chan, nick) => { _this.modules[chan] && _this.modules[chan].onNickPart(nick); });
|
|
|
+ this.bot.addListener("kick", (chan, nick) => { _this.modules[chan] && _this.modules[chan].onNickPart(nick); });
|
|
|
this.bot.addListener("nick", (oldNick, newNick) => {
|
|
|
- _this.users[newNick.toLowerCase()] = _this.users[newNick.toLowerCase()] || new User(newNick);
|
|
|
- _this.users[newNick.toLowerCase()].isAdmin = _this.users[oldNick.toLowerCase()] && _this.users[oldNick.toLowerCase()].isAdmin;
|
|
|
- _this.activeUsers[newNick.toLowerCase()] = true;
|
|
|
- delete _this.activeUsers[oldNick.toLowerCase()];
|
|
|
+ for (var i in _this.modules)
|
|
|
+ _this.modules[i].onRename(oldNick, newNick);
|
|
|
});
|
|
|
- this.bot.addListener("-mode", (channel, by, mode, user) => {
|
|
|
- user && _this.users[user.toLowerCase()] && _this.users[user.toLowerCase()].unsetMode(mode);
|
|
|
+ this.bot.addListener("+mode", (chan, by, mode, user) => {
|
|
|
+ user && _this.modules[chan] && _this.modules[chan].onAddMode(user, mode);
|
|
|
});
|
|
|
- this.bot.addListener("message"+this.room, (user, text) => {
|
|
|
- _this.users[user.toLowerCase()] && _this.onMessage(user, _this.users[user.toLowerCase()], text.trimEnd().replace(/\s+/, ' '));
|
|
|
+ this.bot.addListener("-mode", (chan, by, mode, user) => {
|
|
|
+ user && _this.modules[chan] && _this.modules[chan].onRemMode(user, mode);
|
|
|
});
|
|
|
-}
|
|
|
-
|
|
|
-KnackiBot.prototype.mySQLExportWrapper = function() {
|
|
|
- if (!USE_MYSQL)
|
|
|
- return;
|
|
|
- var msRemaining = 0,
|
|
|
- lastMySQLExport = Cache.getLastMysqlSave();
|
|
|
- if (lastMySQLExport)
|
|
|
- msRemaining = GAME_DURATION -(Date.now() -lastMySQLExport);
|
|
|
- if (msRemaining <= 0)
|
|
|
- this.mySQLExport();
|
|
|
- else
|
|
|
- this.exportScoresTimer = setTimeout(this.mySQLExportWrapper.bind(this), Math.min(2147483000, msRemaining));
|
|
|
-}
|
|
|
-
|
|
|
-KnackiBot.prototype.mySQLExport = function() {
|
|
|
- this.exportScores().then(() => {
|
|
|
- Cache.setExportTs();
|
|
|
- this.resetScores();
|
|
|
- console.log("Successfully exported scores to MySQL");
|
|
|
- this.mySQLExportWrapper();
|
|
|
- }).catch((errString) => {
|
|
|
- console.error("mySQL Export error saving to database: ", errString);
|
|
|
+ this.bot.addListener("message", (user, room, text) => {
|
|
|
+ _this.modules[room] && _this.modules[room].onMessage(user, text);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
-KnackiBot.prototype.exportScores = function() {
|
|
|
- return new Promise((ok, ko) => {
|
|
|
- var ts = Cache.getLastMysqlSave() || START_TIME;
|
|
|
- ts = Math.floor(ts / 1000) *1000;
|
|
|
- ts = MySQL.escape(new Date(ts));
|
|
|
- ts = ts.substr(1, ts.length -2);
|
|
|
- var sep = ts.lastIndexOf('.');
|
|
|
- if (sep > 12) ts = ts.substr(0, sep);
|
|
|
-
|
|
|
- var toSave = [];
|
|
|
- for (var i in this.users)
|
|
|
- if (this.users[i].score) {
|
|
|
- toSave.push(this.users[i].name);
|
|
|
- toSave.push(this.users[i].score);
|
|
|
- }
|
|
|
- if (toSave.length == 0)
|
|
|
- return ok();
|
|
|
- MySQL.execute("INSERT INTO " +MySQL_PERIOD_TABLE +" (start, host) VALUES(?, ?)", [ts, HOSTNAME], (err, result) => {
|
|
|
- if (err || !result.insertId)
|
|
|
- return ko(err || "Cannot get last inserted id");
|
|
|
- MySQL.execute("INSERT INTO " +MySQL_SCORES_TABLE +"(period_id, pseudo, score) VALUES " +(",("+result.insertId+",?,?)").repeat(toSave.length /2).substr(1), toSave, (err) => {
|
|
|
- if (err)
|
|
|
- ko(err);
|
|
|
- else
|
|
|
- ok();
|
|
|
- });
|
|
|
- });
|
|
|
- });
|
|
|
-}
|
|
|
-
|
|
|
-KnackiBot.prototype.resetScores = function() {
|
|
|
- if (this.sumScores(false) > 0) {
|
|
|
- this.sendMsg("Fin de la manche ! Voici les scores finaux:");
|
|
|
- this.sendScore(false);
|
|
|
- }
|
|
|
- for (var i in this.users)
|
|
|
- this.users[i].score = 0;
|
|
|
- Cache.SetScores({});
|
|
|
+KnackiBot.prototype.createUser = function(nick) {
|
|
|
+ return new User(nick);
|
|
|
}
|
|
|
|
|
|
-KnackiBot.prototype.voice = function(username) {
|
|
|
+KnackiBot.prototype.voice = function(chan, username) {
|
|
|
var usernames = Array.isArray(username) ? username : [ username ];
|
|
|
if (usernames.length > 10)
|
|
|
{
|
|
|
- this.voice(usernames.slice(0, 10));
|
|
|
- this.voice(usernames.slice(10));
|
|
|
+ this.voice(chan, usernames.slice(0, 10));
|
|
|
+ this.voice(chan, usernames.slice(10));
|
|
|
return;
|
|
|
}
|
|
|
- usernames.splice(0, 0, "MODE", this.room, "+" +"v".repeat(usernames.length));
|
|
|
+ usernames.splice(0, 0, "MODE", chan, "+" +"v".repeat(usernames.length));
|
|
|
+ console.trace();
|
|
|
+ console.log(usernames);
|
|
|
this.bot.send.apply(this.bot, usernames);
|
|
|
}
|
|
|
|
|
|
@@ -372,311 +116,9 @@ KnackiBot.prototype.sendNotice = function(username, msg) {
|
|
|
this.bot.notice(username, msg);
|
|
|
}
|
|
|
|
|
|
-KnackiBot.prototype.sendMsg = function(msg) {
|
|
|
- this.bot.say(this.room, msg);
|
|
|
+KnackiBot.prototype.sendMsg = function(room, msg) {
|
|
|
+ this.bot.say(room, msg);
|
|
|
}
|
|
|
|
|
|
-KnackiBot.prototype.stop = function() {
|
|
|
- this.timer && clearTimeout(this.timer);
|
|
|
- this.timer = null;
|
|
|
- this.currentQuestion = null;
|
|
|
- this.currentHint = 0;
|
|
|
-}
|
|
|
-
|
|
|
-KnackiBot.prototype.onTick = function() {
|
|
|
- if (!this.currentQuestion)
|
|
|
- return;
|
|
|
- if (this.currentHint < 3)
|
|
|
- this.sendNextHint(false);
|
|
|
- else {
|
|
|
- var response = Array.isArray(this.currentQuestion.response) ?
|
|
|
- this.currentQuestion.response.map(i => "\""+i+"\"").join(" ou ") :
|
|
|
- this.currentQuestion.response;
|
|
|
- this.sendMsg("Perdu, la réponse était: " +response);
|
|
|
- this.nextQuestionWrapper();
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-KnackiBot.prototype.resetTimer = function() {
|
|
|
- this.timer && clearTimeout(this.timer);
|
|
|
- this.timer = setInterval(this.onTick.bind(this), AUTO_HINT_DELAY);
|
|
|
-}
|
|
|
-
|
|
|
-KnackiBot.prototype.sendNextHint = function(resetTimer) {
|
|
|
- this.sendMsg(this.currentQuestion.getHint(this.currentHint));
|
|
|
- ++this.currentHint;
|
|
|
- this.lastHint = Date.now();
|
|
|
- resetTimer !== false && this.resetTimer();
|
|
|
-}
|
|
|
-
|
|
|
-KnackiBot.prototype.nextQuestionWrapper = function() {
|
|
|
- this.currentQuestion.end();
|
|
|
- this.currentQuestion = null;
|
|
|
- setTimeout(this.nextQuestion.bind(this), NEXT_QUESTION_DELAY);
|
|
|
-}
|
|
|
-
|
|
|
-KnackiBot.prototype.nextQuestion = function() {
|
|
|
- this.currentQuestion = this.questions[Math.floor(Math.random() * this.questions.length)];
|
|
|
- console.log(this.currentQuestion);
|
|
|
- this.currentHint = 0;
|
|
|
- this.questionDate = Date.now();
|
|
|
- this.sendMsg("#" +this.currentQuestion.id +" " +this.currentQuestion.question);
|
|
|
- this.sendNextHint();
|
|
|
-}
|
|
|
-
|
|
|
-KnackiBot.prototype.start = function() {
|
|
|
- if (this.reloading) {
|
|
|
- this.sendMsg("Error: database still reloading");
|
|
|
- return;
|
|
|
- }
|
|
|
- if (!this.currentQuestion) {
|
|
|
- this.nextQuestion();
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-KnackiBot.prototype.reloadDb = function() {
|
|
|
- var _this = this;
|
|
|
- this.stop();
|
|
|
- this.reloading = true;
|
|
|
- initQuestionList(QUESTIONS_PATH).then(questions => {
|
|
|
- _this.reloading = false;
|
|
|
- _this.questions = questions;
|
|
|
- _this.sendMsg(questions.length +" questions loaded from database");
|
|
|
- _this.start();
|
|
|
- }).catch(err => {
|
|
|
- console.error(err);
|
|
|
- _this.sendMsg(err);
|
|
|
- });
|
|
|
-}
|
|
|
-
|
|
|
-KnackiBot.prototype.delScore = function(user) {
|
|
|
- var data = this.users[user.toLowerCase()];
|
|
|
- if (!data) {
|
|
|
- this.sendMsg("User not found...");
|
|
|
- return;
|
|
|
- }
|
|
|
- if (!data.score) {
|
|
|
- this.sendMsg("Score for user already null");
|
|
|
- return;
|
|
|
- }
|
|
|
- data.score = 0;
|
|
|
- this.sendMsg("Removed score");
|
|
|
- Cache.SetScores(this.users);
|
|
|
-}
|
|
|
-
|
|
|
-KnackiBot.prototype.sumScores = function(onlyPresent) {
|
|
|
- var score = 0;
|
|
|
- for (var i in this.users)
|
|
|
- score += (this.users[i].score && (this.activeUsers[i] || !onlyPresent)) ? this.users[i].score : 0;
|
|
|
- return score;
|
|
|
-}
|
|
|
-
|
|
|
-KnackiBot.prototype.sendScore = function(onlyPresent) {
|
|
|
- var scores = [];
|
|
|
- for (var i in this.users)
|
|
|
- this.users[i].score && (this.activeUsers[i] || !onlyPresent) && scores.push({name: this.users[i].name, score: this.users[i].score});
|
|
|
- if (scores.length == 0) {
|
|
|
- this.sendMsg("Pas de points pour le moment");
|
|
|
- return;
|
|
|
- }
|
|
|
- scores = scores.sort((a, b) => b.score - a.score).slice(0, 10);
|
|
|
- var index = 0;
|
|
|
- var scoreLines = arrayPad(scores.map(i => [ ((++index) +"."), i.name, (i.score +" points") ]));
|
|
|
- if (scoreLines[0].length < 30) {
|
|
|
- // merge score lines 2 by 2
|
|
|
- var tmp = [];
|
|
|
- for (var i =0, len = scoreLines.length; i < len; i += 2)
|
|
|
- tmp.push((scoreLines[i] || "") +" - " +(scoreLines[i +1] || ""));
|
|
|
- scoreLines = tmp;
|
|
|
- }
|
|
|
- scoreLines.forEach(i => this.sendMsg(i));
|
|
|
-}
|
|
|
-
|
|
|
-KnackiBot.prototype.computeScore = function(username) {
|
|
|
- var rep = Array.isArray(this.currentQuestion.response) ? this.currentQuestion.response.map(i => '"'+i+'"').join(" ou ") : this.currentQuestion.response,
|
|
|
- responseMsg = "Réponse `" +rep +"` trouvée en " +Math.floor((Date.now() -this.questionDate) / 1000) +" secondes ";
|
|
|
- if (this.currentHint <= 1)
|
|
|
- responseMsg += "sans indice";
|
|
|
- else if (this.currentHint === 2)
|
|
|
- responseMsg += "avec 1 seul indice";
|
|
|
- else
|
|
|
- responseMsg += "avec " +this.currentHint +" indices";
|
|
|
- var score = 4 - this.currentHint;
|
|
|
- this.sendMsg(responseMsg);
|
|
|
- this.sendMsg(score +" points pour " +username);
|
|
|
- return score;
|
|
|
-}
|
|
|
-
|
|
|
-KnackiBot.prototype.findQuestionById = function(qId) {
|
|
|
- for (var i =0, len = this.questions.length; i < len; ++i) {
|
|
|
- if (this.questions[i].id === qId)
|
|
|
- return this.questions[i];
|
|
|
- if (this.questions[i].id > qId)
|
|
|
- break;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-KnackiBot.prototype.onMessage = function(username, user, msg) {
|
|
|
- const lmsg = msg.toLowerCase();
|
|
|
- if (lmsg.startsWith("!reload")) {
|
|
|
- if (user.admin)
|
|
|
- this.reloadDb();
|
|
|
- else
|
|
|
- this.sendMsg("Must be channel operator");
|
|
|
- }
|
|
|
- else if (lmsg.startsWith("!aide")) {
|
|
|
- this.sendMsg("Usage: !aide | !indice | !reload | !next | !rename | !score [del pseudo] || !top");
|
|
|
- }
|
|
|
- else if (lmsg === "!indice" || lmsg === "!conseil") {
|
|
|
- if (this.currentQuestion) {
|
|
|
- if (this.currentHint < 3) {
|
|
|
- if (Date.now() -this.lastHint > MIN_HINT_DELAY)
|
|
|
- this.sendNextHint();
|
|
|
- }
|
|
|
- else
|
|
|
- this.sendMsg("Pas plus d'indice...");
|
|
|
- }
|
|
|
- }
|
|
|
- else if (lmsg === "!next") {
|
|
|
- if (user.admin) {
|
|
|
- if (!this.currentQuestion)
|
|
|
- return;
|
|
|
- var response = Array.isArray(this.currentQuestion.response) ?
|
|
|
- this.currentQuestion.response.map(i => "\""+i+"\"").join(" ou ") :
|
|
|
- this.currentQuestion.response;
|
|
|
- this.sendMsg("La réponse était: " +response);
|
|
|
- this.nextQuestionWrapper();
|
|
|
- } else {
|
|
|
- this.sendMsg("Must be channel operator");
|
|
|
- }
|
|
|
- }
|
|
|
- else if (lmsg.startsWith("!report list")) {
|
|
|
- if (user.admin) {
|
|
|
- var questions = Cache.getReportedQuestions();
|
|
|
- for (var i in questions)
|
|
|
- for (var j in questions[i])
|
|
|
- questions[i][j] = (new Date(questions[i][j])).toLocaleString();
|
|
|
- this.sendNotice(username, JSON.stringify(questions));
|
|
|
- }
|
|
|
- else
|
|
|
- this.sendMsg("Must be channel operator");
|
|
|
- }
|
|
|
- else if (lmsg == ("!report clear")) {
|
|
|
- if (user.admin) {
|
|
|
- Cache.clearReports();
|
|
|
- this.sendMsg("Toutes les questions sont marquées comme restaurées");
|
|
|
- }
|
|
|
- else
|
|
|
- this.sendMsg("Must be channel operator");
|
|
|
- }
|
|
|
- else if (lmsg.startsWith("!report del ")) {
|
|
|
- if (user.admin) {
|
|
|
- var questionId = msg.substr("!report del ".length).trim();
|
|
|
- if (questionId.startsWith('#'))
|
|
|
- questionId = questionId.substr(1);
|
|
|
- questionId = Number(questionId);
|
|
|
- if (isNaN(questionId)) {
|
|
|
- this.sendMsg("Erreur: Usage: !report del #1234");
|
|
|
- return;
|
|
|
- }
|
|
|
- Cache.unreportQuestion(questionId);
|
|
|
- this.sendMsg("Question #" +questionId +" marquée comme restaurée");
|
|
|
- }
|
|
|
- else
|
|
|
- this.sendMsg("Must be channel operator");
|
|
|
- }
|
|
|
- else if (lmsg.startsWith("!report ")) {
|
|
|
- var questionId = msg.substr("!report ".length).trim();
|
|
|
- if (questionId.startsWith('#'))
|
|
|
- questionId = questionId.substr(1);
|
|
|
- questionId = Number(questionId);
|
|
|
- if (isNaN(questionId)) {
|
|
|
- this.sendMsg("Erreur: Usage: !report #1234");
|
|
|
- return;
|
|
|
- }
|
|
|
- if (!this.findQuestionById(questionId))
|
|
|
- this.sendMsg("Erreur: question non trouvée");
|
|
|
- else {
|
|
|
- Cache.reportQuestion(questionId, username);
|
|
|
- this.sendMsg("Question #" +questionId +" marquée comme défectueuse par " +username);
|
|
|
- }
|
|
|
- }
|
|
|
- else if (lmsg.startsWith("!score del ")) {
|
|
|
- if (user.admin)
|
|
|
- this.delScore(msg.substr("!score del ".length).trim());
|
|
|
- else
|
|
|
- this.sendMsg("Must be channel operator");
|
|
|
- }
|
|
|
- else if (lmsg.startsWith("!rename")) {
|
|
|
- if (!user.admin) {
|
|
|
- this.sendMsg("Must be channel operator");
|
|
|
- return;
|
|
|
- }
|
|
|
- var args = (msg.split(/\s+/)).splice(1);
|
|
|
- if (args.length < 2) {
|
|
|
- this.sendNotice(username, "Usage: !rename nouveau_pseudo ancien_pseudo [ancien_pseudo...]");
|
|
|
- return;
|
|
|
- }
|
|
|
- var sum = 0,
|
|
|
- users = [],
|
|
|
- target = args[0];
|
|
|
-
|
|
|
- args = args.map(username => username.toLowerCase());
|
|
|
- var userToMod = null;
|
|
|
- for (var i in this.users) {
|
|
|
- var userIndex = args.indexOf(i.toLowerCase());
|
|
|
- if (userIndex > 0) {
|
|
|
- sum += this.users[i].score;
|
|
|
- this.users[i].score = 0;
|
|
|
- if (!this.activeUsers[i])
|
|
|
- delete this.users[i];
|
|
|
- }
|
|
|
- else if (userIndex == 0)
|
|
|
- userToMod = this.users[i];
|
|
|
- }
|
|
|
- if (!userToMod) {
|
|
|
- userToMod = this.users[target] = new User(target);
|
|
|
- this.sendMsg("Created user " +target);
|
|
|
- }
|
|
|
- userToMod.score += sum;
|
|
|
- Cache.SetScores(this.users);
|
|
|
- }
|
|
|
- else if (lmsg.startsWith("!score all")) {
|
|
|
- if (!user.admin) {
|
|
|
- this.sendMsg("Must be channel operator");
|
|
|
- return;
|
|
|
- }
|
|
|
- var scores = [];
|
|
|
- for (var i in this.users)
|
|
|
- this.users[i].score && scores.push({name: this.users[i].name, score: this.users[i].score});
|
|
|
- if (scores.length == 0) {
|
|
|
- this.sendMsg("Pas de points pour le moment");
|
|
|
- return;
|
|
|
- }
|
|
|
- scores = scores.sort((a, b) => b.score - a.score).slice(0, 10);
|
|
|
- this.sendNotice(username, scores.map(i => i.name+":"+i.score).join(", "));
|
|
|
- }
|
|
|
- else if (lmsg.startsWith("!score")) {
|
|
|
- this.sendScore(true);
|
|
|
- }
|
|
|
- else if (lmsg.startsWith("!top")) {
|
|
|
- this.sendScore(false);
|
|
|
- }
|
|
|
- else if (this.currentQuestion) {
|
|
|
- var dieOnFailure = this.currentQuestion.isBoolean() && this.currentQuestion.isBoolean(Question.normalize(msg));
|
|
|
- if (this.currentQuestion.check(msg)) {
|
|
|
- user.score += this.computeScore(username);
|
|
|
- Cache.SetScores(this.users);
|
|
|
- this.voice(username);
|
|
|
- this.nextQuestionWrapper();
|
|
|
- }
|
|
|
- else if (dieOnFailure) {
|
|
|
- var rep = Array.isArray(this.currentQuestion.response) ? this.currentQuestion.response.map(i => '"'+i+'"').join(" ou ") : this.currentQuestion.response;
|
|
|
- this.sendMsg("Perdu, la réponse était: " +rep);
|
|
|
- this.nextQuestionWrapper();
|
|
|
- }
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-new KnackiBot(Cache.GetData());
|
|
|
+new KnackiBot();
|
|
|
|