#21 Loup Garou

Atvērta
isundil vēlas sapludināt 5 revīzijas no irc.knacki.info/LG uz irc.knacki.info/master
6 mainītis faili ar 868 papildinājumiem un 39 dzēšanām
  1. 13 3
      config.js
  2. 15 0
      iBot.js
  3. 73 15
      index.js
  4. 745 0
      loupgarou.js
  5. 11 8
      quizz.js
  6. 11 13
      rapido.js

+ 13 - 3
config.js

@@ -31,8 +31,8 @@ module.exports.MODULES["#quizz"] = new (require('./quizz.js'))({
     MySQL_PERIOD_TABLE: "knackizz_period",
     MySQL_SCORES_TABLE: "knackizz_scores",
 });
-
-module.exports.MODULES["#rapido"] = new (require('./rapido.js'))({
+module.exports.MODULES["#rapido"] = [];
+module.exports.MODULES["#rapido"].push(new (require('./rapido.js'))({
     DISABLED: false,
     NEXT_WORD_DELAY: 10 * 1000, // 10 sec
     WORD_TIMEO_FPS: 0.5, // 2 seconds per key
@@ -50,7 +50,17 @@ module.exports.MODULES["#rapido"] = new (require('./rapido.js'))({
         [ 1, 1 ]
     ],
     GAME_DURATION: 1 * 60 * 60 * 1000 // 1 hour game duration
-});
+}));
+
+module.exports.MODULES["#rapido"].push(new (require('./loupgarou.js'))({
+    DISABLED: false,
+    registrationDelay: 2*60, // 2 min
+    cupidonDelay: 90,        // 1 min 30
+    seerDelay: 90,           // 1 min 30
+    wolfDelay: 180,          // 2 min
+    reminderInterval: 30,    // 30 sec
+    privateChannel: "#test-lg"
+}));
 
 module.exports.MODULES["#voicefaible"] = new (require('./levoicefaible.js'))({
 });

+ 15 - 0
iBot.js

@@ -0,0 +1,15 @@
+
+function IBot() {}
+
+IBot.prototype.init = function(bot, chanName){};
+IBot.prototype.getName = function(){};
+IBot.prototype.onActivate = function(){};
+IBot.prototype.onMessage = function(user, message){};
+IBot.prototype.onAddMode = function(user, mode) {};
+IBot.prototype.onRemMode = function(user, mode) {};
+IBot.prototype.onRename = function(oldNick, newNick) {};
+IBot.prototype.onNickPart = function(nick) {};
+IBot.prototype.onNameList = function(nicks) {};
+IBot.prototype.onSelfJoin = function() {};
+IBot.prototype.onJoin = funcion(nick) {};
+

+ 73 - 15
index.js

@@ -26,15 +26,20 @@ function KnackiBot() {
     this.password = NS_PASSWORD;
     this.modules = MODULES;
 
-    for (var i in this.modules)
-        this.modules[i].init(this, i);
-
     this.bot = new irc.Client(IRC_HOSTNAME, this.name, {
         channels: Object.keys(this.modules),
         userName: this.name,
         realName: this.name,
         stripColors: true
     });
+    for (var i in this.modules) {
+        if (!Array.isArray(this.modules[i]))
+            this.modules[i] = [this.modules[i]];
+        this.modules[i].activeGame = this.modules[i].length == 1 ? this.modules[i][0] : null;
+        if (this.modules[i].activeGame)
+            this.modules[i].activeGame.onActivate();
+        this.modules[i].forEach(mod => mod.init(this, i));
+    }
     if (USE_NS) {
         var _this = this,
             registerHandler = 0;
@@ -57,6 +62,7 @@ function KnackiBot() {
                     if (registerHandler !== undefined) {
                         clearInterval(registerHandler);
                         registerHandler = undefined;
+                        _this.setModes(this.name, "-R");
                     }
                 }
             }
@@ -71,46 +77,85 @@ function KnackiBot() {
         chan = chan.toLowerCase();
         if (_this.modules[chan]) {
             if (nick == _this.name)
-                _this.modules[chan].onSelfJoin();
+                _this.modules[chan].forEach(i => i.onSelfJoin());
             else
-                _this.modules[chan].onJoin(nick);
+                _this.modules[chan].forEach(i => i.onJoin(nick));
         }
     });
     this.bot.addListener("names", (chan, nicks) => {
         chan = chan.toLowerCase();
-        _this.modules[chan] && _this.modules[chan].onNameList(nicks);
+        _this.modules[chan] && _this.modules[chan].forEach(i => i.onNameList(nicks));
     });
     this.bot.addListener("part", (chan, nick) => {
         chan = chan.toLowerCase();
-        _this.modules[chan] && _this.modules[chan].onNickPart(nick);
+        _this.modules[chan] && _this.modules[chan].forEach(i => i.onNickPart(nick));
     });
     this.bot.addListener("kick", (chan, nick) => {
         chan = chan.toLowerCase();
-        _this.modules[chan] && _this.modules[chan].onNickPart(nick);
+        _this.modules[chan] && _this.modules[chan].forEach(i => i.onNickPart(nick));
     });
     this.bot.addListener("nick", (oldNick, newNick) => {
-        for (var i in _this.modules)
-            _this.modules[i].onRename(oldNick, newNick);
+        if (this.name === oldNick)
+            this.name = newNick;
+        else
+            for (var i in _this.modules)
+                _this.modules[chan] && _this.modules[i].forEach(i => i.onRename(oldNick, newNick));
     });
     this.bot.addListener("+mode", (chan, by, mode, user) => {
         chan = chan.toLowerCase();
-        user && _this.modules[chan] && _this.modules[chan].onAddMode(user, mode);
+        user && _this.modules[chan] && _this.modules[chan].forEach(i => i.onAddMode(user, mode));
     });
     this.bot.addListener("-mode", (chan, by, mode, user) => {
         chan = chan.toLowerCase();
-        user && _this.modules[chan] && _this.modules[chan].onRemMode(user, mode);
+        user && _this.modules[chan] && _this.modules[chan].forEach(i => i.onRemMode(user, mode));
     });
     this.bot.addListener("message", (user, room, text) => {
         room = room.toLowerCase();
-        _this.modules[room] && _this.modules[room].onMessage(user, text);
+        if (_this.modules[room]) {
+            if (text.substr(0, 1) == "!" && !_this.modules[room].activeGame) {
+                var found = false;
+                for (var i =0, len = _this.modules[room].length; i < len; ++i) {
+                    if ('!' +_this.modules[room][i].getName() === text.trim().toLowerCase()) {
+                        console.info("Loading game " +text);
+                        _this.modules[room].activeGame = _this.modules[room][i];
+                        _this.modules[room].activeGame.onActivate();
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    this.sendMsg(room, "Jeux disponibles: " +_this.modules[room].map(i => '!' +i.getName()).join(", "));
+                    return;
+                }
+            }
+            if (_this.modules[room].activeGame)
+                _this.modules[room].activeGame.onMessage(user, text);
+        }
     });
 }
 
+KnackiBot.prototype.endGame = function(game) {
+    for (var i in this.modules)
+        if (this.modules[i].activeGame && this.modules[i].activeGame === game) {
+            this.modules[i].activeGame = null;
+            return;
+        }
+}
+
 KnackiBot.prototype.createUser = function(nick) {
     return new User(nick);
 }
 
-KnackiBot.prototype.voice = function(chan, username) {
+KnackiBot.prototype.kick = function(chan, pseudo) {
+    console.info("Kicking " +pseudo +" from " +chan);
+    this.bot.send.call(this.bot, "KICK", chan, pseudo);
+}
+
+KnackiBot.prototype.setModes = function(chan, modes) {
+    this.bot.send.call(this.bot, "MODE", chan, modes);
+}
+
+KnackiBot.prototype.setVoice = function(chan, username, direction) {
     var usernames = Array.isArray(username) ? username : [ username ];
     if (usernames.length > 10)
     {
@@ -118,10 +163,23 @@ KnackiBot.prototype.voice = function(chan, username) {
         this.voice(chan, usernames.slice(10));
         return;
     }
-    usernames.splice(0, 0, "MODE", chan, "+" +"v".repeat(usernames.length));
+    usernames.splice(0, 0, "MODE", chan, (direction > 0 ? '+' : '-') +"v".repeat(usernames.length));
     this.bot.send.apply(this.bot, usernames);
 }
 
+
+KnackiBot.prototype.voice = function(chan, username) {
+    this.setVoice(chan, username, 1);
+}
+
+KnackiBot.prototype.devoice = function(chan, username) {
+    this.setVoice(chan, username, -1);
+}
+
+KnackiBot.prototype.invite = function(channel, pseudo) {
+    this.bot.send.call(this.bot, "INVITE", pseudo, channel);
+}
+
 KnackiBot.prototype.sendNotice = function(username, msg) {
     this.bot.notice(username, msg);
 }

+ 745 - 0
loupgarou.js

@@ -0,0 +1,745 @@
+
+function LoupGarou(config) {
+    this.config = config;
+}
+
+const ROLES = {
+    WOLF: "WOLF",
+    VILLAGER: "VILLAGER",
+    CUPIDON: "CUPIDON",     // OK
+    HUNTER: "HUNTER",
+    WITCH: "WITCH",
+    SEER: "SEER",           // OK
+    GIRL: "GIRL"
+};
+
+const ROLE_REPARTITION = [
+    [ ROLES.WOLF, ROLES.WOLF, ROLES.VILLAGER ],
+    [ ROLES.WOLF, ROLES.VILLAGER, ROLES.VILLAGER, ROLES.VILLAGER ],
+    [ ROLES.WOLF, ROLES.WOLF, ROLES.VILLAGER, ROLES.VILLAGER, ROLES.HUNTER ],
+    [ ROLES.WOLF, ROLES.WOLF, ROLES.VILLAGER, ROLES.VILLAGER, ROLES.HUNTER, ROLES.WITCH ]
+];
+
+var i =-1;
+const STEP = {
+    NOT_STARTED: -4,
+    REGISTRATION: -3,
+    SET_ROLES: -2,
+    SELECT_CUPIDON: -1,
+    NIGHT: ++i,
+    NIGHT_SEER: ++i,
+    NIGHT_GIRL: ++i,
+    NIGHT_LG: ++i,
+    NIGHT_WITCH: ++i,
+    DAY_LOVER: ++i,
+    DAY_HUNTER: ++i,
+    DAY: ++i,
+    DAY_VILLA: ++i,
+    DAY_EVENING: ++i
+};
+const MAX_STEP = STEP.DAY_EVENING;
+
+const langMng = [
+    {
+        unregisterLeft: function(who) { return `${who} n'est plus inscrit au Loup-Garou`; },
+        killedHunter: function(who) { return `Le chasseur tire sur ${who} dans son dernier souffle et le tue !`; },
+        killedWolf: function(who) { return `${who} a servi d'en-cas aux loups-garous !`; },
+        killedLeft: function(who) { return `${who} souffre du raj-quit et en est éliminé...`; },
+        killedLove: function(who, lover) { return `Dans un élan de chagrin, ${who} decide de rejoindre ${lover} dans sa tombe. RIP !`; },
+        killedVillager: function(who) { return `Vous avez décidé de voter contre ${who} et le tuez sans le moindre remord.`; },
+        join: function(who) { return `${who} est inscrit.e a la prochaine partie !`; },
+        inviteWolfRoom: function() { return `Invitation des Loups Garous sur le salon de nuit.`; },
+        inviteWolfRoomPrivate: function(salon) { return "Merci de rejoindre le salon " +salon; },
+        dayNoDead: function() { return "Il fait jour et le village se réveille et personne n'est mort."; },
+        dayOneDead: function() { return "Il fait jour et le village se réveille en découvrant un corps sans vie près de la fontaine."; },
+        dayMultiDead: function() { return "Il fait jour et le village se réveille en découvrant avec stupeur et tremblement une pile de corps au centre de la place du marché."; },
+        startWolfVotes: function() { return "Vous allez devoir manger quelqu'un cette nuit ! Pour voter contre un innocent tapez !vote <pseudo>"; },
+        startVotes: function() { return "Vous allez devoir trouver les loups garous et les éliminer. Pour voter contre quelqu'un tape : !vote <pseudo>"; },
+        cupidonMessage: function(players) { return "Tu vas pouvoir choisir un couple d'amoureux. Pour envoyer ta fleche, tapes les pseudos des deux personnes a coupler. Tu as le choix entre: " +players.join(", "); },
+        setNoLove: function() { return "Tu n'as planté ta flèche dans personne. Il n'y aura donc pas d'amoureux cette partie."; },
+        setLoveWith: function(p1, p2) { return `Tu as planté ta flèche et choisi ${p1} et ${p2}`; },
+        inLoveWith: function(other) { return `Cupidon a envoyé sa flèche sur toi et ton amoureux est ${other}`; },
+        remainingPseudos: function(nicks) { return "Vous avez le choix entre : " +nicks.join(", "); },
+        noVote: function() { return "Pas de vote pour l'instant..."; },
+        seerMessage: function(players) { return "Tu vas pouvoir decouvrir le role d'un joueur. Tapes juste le pseudo du joueur qui t'interesse. Tu as le choix entre: " +players.join(", "); },
+        seerTimeout: function() { return "Il est tard et tu vas te coucher. Tu pourras reessayer demain."; },
+        wolfTimeout: function() { return "Les Loups-Garous ne se sont pas nourris ce soir et sont tous morts de faim !"; },
+        loverWins: function() { return "Les amoureux gagnent la partie !"; },
+        wolfWins: function() { return "Les loup-garous gagnent la partie !"; },
+        villagerWins: function() { return "Les villageois gagnent la partie !"; },
+        congrats: function(nickList) { return nickList.length == 1 ? ("Félicitations au gagnant " +nickList[0]) : ("Félicitations aux gagnants " +nickList.join(", ")); },
+        waitingCupidon: function(timeSec) { return `En attente de cupidon (${timeSec} secondes)`; },
+        waitingSeer: function(timeSec) { return `En attente de la voyante (${timeSec} secondes)`; },
+        waitingWolf: function(timeSec) { return `En attente des loup-garous (${timeSec} secondes)`; },
+        currentVotes: function() { return "Etat des votes:"; },
+        whoiswhat: {
+            "WOLF": function(who) { return `${who} est un Loup-Garou.`; },
+            "VILLAGER": function(who) { return `${who} est un simple villageois.`; },
+            "CUPIDON": function(who) { return `${who} est un cupidon.`; },
+            "HUNTER": function(who) { return `${who} est un chasseur.`; },
+            "WITCH": function(who) { return `${who} est une sorcière.`; },
+            "SEER": function(who) { return `${who} est une voyante.`; },
+            "GIRL": function(who) { return `${who} est une petite fille.`; },
+        },
+        whowaswhat: {
+            "WOLF": function(who) { return `${who} était un Loup-Garou !`; },
+            "VILLAGER": function(who) { return `${who} était un simple villageois !`; },
+            "CUPIDON": function(who) { return `${who} était un cupidon !`; },
+            "HUNTER": function(who) { return `${who} était un chasseur !`; },
+            "WITCH": function(who) { return `${who} était une sorcière !`; },
+            "SEER": function(who) { return `${who} était une voyante !`; },
+            "GIRL": function(who) { return `${who} était une petite fille !`; },
+        },
+        advRole: {
+            "WOLF": "Vous êtes un Loup-Garou ! Votre objectif est d'éliminer tous les innocents (ceux qui ne sont pas Loups Garous). Chaque nuit, vous vous réunissez entre compères Loups pour décider d'une victime à éliminer.",
+            "VILLAGER": "Vous êtes un Villageois ! Votre objectif est d'éliminer tous les Loups Garous. Vous ne disposez d'aucun pouvoir particulier : uniquement votre perspicacité et votre force de persuasion.",
+            "CUPIDON": "Vous êtes Cupidon ! Votre objectif est d'éliminer tous les Loups Garous. Dès le début de la partie, vous devez former un couple de deux joueurs. Leur objectif sera de survivre ensemble, car si l'un d'eux meurt, l'autre se suicidera.",
+            "HUNTER": "Vous êtes le chasseur ! Votre objectif est d'éliminer tous les Loups Garous. A votre mort, vous pouvez éliminer un joueur en utilisant votre dernière balle.",
+            "WITCH": "Vous êtes la sorciere ! Votre objectif est d'éliminer tous les Loups Garous. Vous disposez de deux potions : une potion de vie pour sauver la victime des Loups et une potion de mort pour assassiner quelqu'un.",
+            "SEER": "Vous êtes la voyante ! Votre objectif est d'éliminer tous les Loups Garous. Chaque nuit, vous pouvez espionner un joueur et découvrir sa véritable identité.",
+            "GIRL": "Vous êtes la petite fille ! Votre objectif est d'éliminer tous les Loups Garous. Chaque nuit, vous pouvez espionner les loups et tenter de découvrir leurs identité."
+        },
+    }
+];
+
+const KILL_REASON = {
+    HUNTER: 0,
+    WOLF: 1,
+    LEFT: 2,
+    LOVE: 3,
+    VILLAGER: 4,
+    HUNGER: 5
+}
+
+function Player(lg, name) {
+    this.loupgarou = lg;
+    this.dead = null;
+    this.killReason = null;
+    this.role = null;
+    this.lover = null;
+    this.name = name;
+    this.reminder = null;
+    this.jobDone = null;
+}
+
+Player.prototype.rename = function(newNick) { this.name = newNick; };
+
+Player.prototype.kill = function(reason, msg) {
+    this.dead = reason;
+    this.killReason = msg;
+    this.loupgarou.onKilled(this);
+};
+
+VoteEngine = function(lg) {
+    this.loupgarou = lg;
+    this.players = null;
+    this.canBeVoted = null;
+};
+
+VoteEngine.prototype.init = function(canVote, canBeVoted) {
+    this.players = {};
+    this.canBeVoted = {};
+    canVote.forEach(i => this.players[i.toLowerCase()] = false);
+    canBeVoted.forEach(i => this.canBeVoted[i.toLowerCase()] = true);
+};
+
+VoteEngine.prototype.vote = function(channel, player, text) {
+    words = text.trim().toLowerCase().split(/\s/, 3);
+    if (words[0] !== "!vote")
+        return;
+    if (!player ||
+        !words[1] ||
+        this.players[player] === undefined ||
+        this.canBeVoted[words[1]] === undefined)
+        return;
+    this.players[player] = { against: words[1], reason: words[2] || null };
+    if (this.canBeVoted[words[1]] === true)
+        this.canBeVoted[words[1]] = Date.now();
+    this.loupgarou.onVote(channel, player, words[1]);
+};
+
+LoupGarou.prototype.onVote = function(channel, vote, voteAgainst) {
+    const votingPlayer = this.players[vote],
+        votedPlayer = this.players[voteAgainst];
+
+    if (votingPlayer && votedPlayer)
+        this.bot.sendMsg(channel, `${votingPlayer.name} vote contre ${votedPlayer.name}`);
+}
+
+VoteEngine.prototype.hasVotes = function() {
+    for (var i in this.players)
+        if (this.players[i])
+            return true;
+    return false;
+};
+
+VoteEngine.prototype.getVotes = function() {
+    var votedByName = {},
+        voted = [];
+
+    for (var i in this.players) {
+        if (this.players[i] !== false) {
+            var arr = votedByName[this.players[i].against];
+            if (!arr)
+                voted.push(arr = votedByName[this.players[i].against] = {name: this.players[i].against, from: []});
+            arr.from.push(i);
+        }
+    }
+    return voted.sort((i, j) => j.from.length -i.from.length);
+};
+
+VoteEngine.prototype.getVote = function() {
+    var voted = {};
+    for (var i in this.players) {
+        if (this.players[i] !== undefined) {
+            var arr = voted[this.players[i].against] = (voted[this.players[i].against] || []);
+            arr.push(this.players[i].reason);
+        }
+    }
+    var result = null,
+        max = 0,
+        time = null;
+
+    for (var i in voted) {
+        if (voted[i].length > max || (voted[i].length === max && (time === null || time > this.canBeVoted[i]))) {
+            result = i;
+            max = voted[i].max;
+            time = this.canBeVoted[i];
+        }
+    }
+    console.log(voted[i]);
+    return result;
+};
+
+LoupGarou.prototype.init = function(bot, chanName) {
+    this.room = chanName;
+    this.bot = bot;
+    this.users = {};
+    this.players = {};
+    this.stepListeners = {};
+    this.timeInStep = 0;
+    this.currentStep = STEP.NOT_STARTED;
+    this.currentScenario = langMng[0];
+    this.nightChannelPersons = {};
+    this.justDead = [];
+    this.voteEngine = new VoteEngine(this);
+
+    for (var i in STEP) this.stepListeners[STEP[i]] = [];
+    this.stepListeners[STEP.NOT_STARTED].push(function() { this.bot.setModes(this.room, "-m"); this.bot.endGame(this); });
+    this.stepListeners[STEP.REGISTRATION].push(LoupGarou.prototype.onRegistration);
+    this.stepListeners[STEP.SET_ROLES].push(LoupGarou.prototype.startGame);
+    this.stepListeners[STEP.SELECT_CUPIDON].push(LoupGarou.prototype.onCupidon);
+    this.stepListeners[STEP.NIGHT].push(function() { this.bot.sendMsg(this.room, "C'est la nuit, crack crack boum"); this.nextStep(); }); // FIXME this.currentScenario....
+    this.stepListeners[STEP.NIGHT_SEER].push(LoupGarou.prototype.onSeer);
+    this.stepListeners[STEP.NIGHT_GIRL].push(LoupGarou.prototype.onGirl);
+    this.stepListeners[STEP.NIGHT_LG].push(LoupGarou.prototype.onWolf);
+    this.stepListeners[STEP.NIGHT_WITCH].push(LoupGarou.prototype.onWitch);
+    this.stepListeners[STEP.DAY_LOVER].push(LoupGarou.prototype.onLovers);
+    this.stepListeners[STEP.DAY_HUNTER].push(LoupGarou.prototype.onHunter);
+    this.stepListeners[STEP.DAY].push(LoupGarou.prototype.onDay);
+    this.stepListeners[STEP.DAY].push(LoupGarou.prototype.cleanCorpses);
+    this.stepListeners[STEP.DAY].push(LoupGarou.prototype.checkEndOfGameAndContinue);
+    this.stepListeners[STEP.DAY_VILLA].push(LoupGarou.prototype.onVillagers);
+    this.stepListeners[STEP.DAY_EVENING].push(LoupGarou.prototype.onEvening);
+    this.stepListeners[STEP.DAY_EVENING].push(LoupGarou.prototype.checkEndOfGameAndContinue);
+
+    var self = this;
+    this.bot.bot.addListener("pm", (from, text) => {
+        self.onPm(from, text);
+    });
+}
+
+LoupGarou.prototype.getName = function(){ return "loupgarou"; };
+LoupGarou.prototype.onActivate = function(){
+    this.currentScenario = langMng[Math.floor(Math.random() *langMng.length)];
+    this.setStep(STEP.REGISTRATION);
+    console.info("Starting Loup-garou");
+    //this.bot.setModes(this.config.privateChannel, "+iKmnpt"); FIXME
+    this.bot.setModes(this.config.privateChannel, "+Kmnpt");
+    for (var i in this.nightChannelPersons)
+        this.bot.kick(this.config.privateChannel, i);
+};
+
+LoupGarou.prototype.onAddMode = function(user, mode) {};
+LoupGarou.prototype.onRemMode = function(user, mode) {};
+LoupGarou.prototype.onRename = function(oldNick, newNick) {
+    this.users[newNick.toLowerCase()] = this.users[oldNick.toLowerCase()];
+    delete this.users[oldNick.toLowerCase()];
+    var p = this.players[newNick.toLowerCase()] = this.players[oldNick.toLowerCase()];
+    delete this.players[oldNick.toLowerCase()];
+    if (this.nightChannelPersons[oldNick.toLowerCase()]) {
+        this.nightChannelPersons[newNick.toLowerCase()] = this.nightChannelPersons[oldNick.toLowerCase()];
+        delete this.nightChannelPersons[oldNick.toLowerCase()];
+    }
+    p && p.rename(newNick);
+};
+
+LoupGarou.prototype.onNickPart = function(nick) {
+    delete this.users[nick.toLowerCase()];
+    var p = this.players[nick.toLowerCase()];
+    if (p) {
+        if (this.currentStep > STEP.REGISTRATION) {
+            p.kill(KILL_REASON.LEFT, this.currentScenario.killedLeft(p.name));
+        } else {
+            this.bot.sendMsg(this.room, this.currentScenario.unregisterLeft(p.name));
+            delete this.players[nick.toLowerCase()];
+        }
+    }
+};
+
+LoupGarou.prototype.onNameList = function(nicks) {
+    this.users = {};
+    for (var i in nicks) {
+        var u = this.users[i.toLowerCase()] = (this.users[i.toLowerCase()] || this.bot.createUser(i));
+        u.setModeChar(nicks[i]);
+    }
+};
+
+LoupGarou.prototype.onSelfJoin = function() {
+    // Preparing night channel
+    var _this = this;
+    this.nightChannelPersons = this.nightChannelPersons || {};
+    this.bot.bot.addListener("join" +_this.config.privateChannel, nick => {
+        if (_this.bot.name.toLowerCase() !== nick.toLowerCase()) {
+            _this.nightChannelPersons[nick.toLowerCase()] = true;
+            if (_this.currentStep === STEP.NIGHT_LG)
+                _this.bot.voice(_this.config.privateChannel, nick);
+        }
+    });
+    this.bot.bot.addListener("names" +_this.config.privateChannel, nicks => {
+        for (var nick in nicks)
+            if (this.bot.name.toLowerCase() !== nick.toLowerCase())
+                _this.nightChannelPersons[nick.toLowerCase()] = true;
+    });
+    this.bot.bot.addListener("part" +_this.config.privateChannel, nick => {
+        delete _this.nightChannelPersons[nick.toLowerCase()];
+    });
+    this.bot.bot.addListener("kick" +_this.config.privateChannel, nick => {
+        delete _this.nightChannelPersons[nick.toLowerCase()];
+    });
+    this.bot.bot.addListener("message" +_this.config.privateChannel, (user, text) => {
+        var player = this.players[user];
+        if (this.currentStep === STEP.NIGHT_LG && player && player.dead === null)
+            this.voteEngine.vote(_this.config.privateChannel, player.name.toLowerCase(), text);
+    });
+    this.bot.bot.join(this.config.privateChannel);
+    this.bot.setModes(this.room, "-m");
+};
+
+LoupGarou.prototype.onJoin = function(nick) {
+  this.users[nick.toLowerCase()] = this.users[nick.toLowerCase()] || this.bot.createUser(nick);
+};
+
+LoupGarou.prototype.delaySetStep = function() {
+    LoupGarou.prototype.setStep.delay = null;
+    this.setStep(LoupGarou.prototype.setStep.nextStep);
+};
+
+LoupGarou.prototype.setStep = function(newStep) {
+    if (LoupGarou.prototype.setStep.recursive === true) {
+        LoupGarou.prototype.setStep.nextStep = newStep;
+        if (!LoupGarou.prototype.setStep.delay)
+            LoupGarou.prototype.setStep.delay = setTimeout(LoupGarou.prototype.delaySetStep.bind(this), 250);
+        return;
+    }
+    LoupGarou.prototype.nextStep.messageDelayed = [];
+    LoupGarou.prototype.setStep.recursive = true;
+    this.timeInStep = Date.now() / 1000;
+    this.clearReminder();
+    this.currentStep = newStep;
+    this.stepListeners[newStep] && this.stepListeners[newStep].some(i => i.call(this), this);
+    LoupGarou.prototype.setStep.recursive = false;
+};
+
+LoupGarou.prototype.nextStep = function() {
+    LoupGarou.prototype.nextStep.messageDelayed.forEach(i => this.bot.sendMsg(i[0], i[1]), this);
+    this.setStep(this.currentStep == MAX_STEP ? STEP.NIGHT : (this.currentStep +1));
+};
+
+LoupGarou.prototype.nextStep.messageDelayed = [];
+
+LoupGarou.prototype.onMessage = function(user, message){
+    switch (this.currentStep) {
+        case STEP.REGISTRATION:
+            if (message === "!loupgarou") {
+                if (this.players[user.toLowerCase()]) {
+                    this.bot.sendMsg(this.room, `${user}, tu es déjà inscrit au Loup-Garou`);
+                    this.bot.voice(this.room, user);
+                    return;
+                }
+                var p = this.players[user.toLowerCase()] = new Player(this, user);
+                this.bot.sendMsg(this.room, this.currentScenario.join(p.name));
+                this.bot.voice(this.room, p.name);
+            }
+    }
+};
+
+LoupGarou.prototype.devoiceAll = function() {
+    this.bot.devoice(this.room, Object.keys(this.users));
+}
+
+LoupGarou.prototype.voiceAll = function() {
+    var players = [];
+    for (var i in this.players)
+        this.players[i].dead === null && players.push(this.players[i].name);
+    this.bot.voice(this.room, players);
+}
+
+LoupGarou.prototype.clearReminder = function() {
+    if (this.currentReminder)
+        clearInterval(this.currentReminder);
+}
+
+LoupGarou.prototype.setReminder = function(cb) {
+    this.clearReminder();
+    if (this.config.reminderInterval > 0)
+        this.currentReminder = setInterval(cb.bind(this), this.config.reminderInterval * 1000);
+}
+
+LoupGarou.prototype.onRegistration = function() {
+    this.players = {};
+    this.bot.sendMsg(this.room, "Pour vous inscrire à la prochaine partie de Loup Garou, tapez !loupgarou !");
+    this.devoiceAll();
+    this.bot.setModes(this.room, "-m");
+    setTimeout((function() {this.setStep(STEP.SET_ROLES);}).bind(this), this.config.registrationDelay * 1000);
+    this.setReminder(function() {
+        var names = [];
+        for (var i in this.players) names.push(this.players[i].name);
+        this.bot.sendMsg(this.room, "Pour vous inscrire à la prochaine partie de Loup Garou avec " +names.join(", ") +", tapez !loupgarou ! Prochaine partie dans " +Math.round((this.config.registrationDelay -(Date.now() / 1000) +this.timeInStep)) +" secondes");
+    });
+};
+
+LoupGarou.prototype.doSetupRoles = function(roleList) {
+    roleList = roleList.sort(() => Math.random() -0.5);
+    var i =0;
+    for (var p in this.players) {
+        var player = this.players[p];
+        player.role = roleList[i];
+        this.bot.sendMsg(player.name, this.currentScenario.advRole[player.role]);
+        ++i;
+    }
+};
+
+LoupGarou.prototype.setupRoles = function() {
+    var nbPlayers = Object.keys(this.players).length;
+    if (!ROLE_REPARTITION.some(roles => {
+            if (roles.length === nbPlayers) {
+                this.doSetupRoles(roles);
+                return true;
+            }
+        }, this)) {
+        // Error: not enought players !
+        this.bot.sendMsg(this.room, "Il n'y a pas assez de personnes inscrites pour pouvoir jouer au Loup-Garou. Fin de la partie.");
+        this.devoiceAll();
+        console.info("Loup-Garou: not enought player");
+        this.setStep(STEP.NOT_STARTED);
+        return false;
+    }
+    return true;
+};
+
+LoupGarou.prototype.debug = function() {
+    var debug = {};
+    for (var i in this.players) debug[this.players[i].name] = {
+        dead: this.players[i].dead,
+        role: this.players[i].role
+    };
+    console.log(debug);
+};
+
+LoupGarou.prototype.isWolf = function(p) {
+    return p.role === ROLES.WOLF;
+};
+
+LoupGarou.prototype.startGame = function() {
+    // Setup roles
+    if (!this.setupRoles())
+        return;
+    this.bot.setModes(this.room, "+m");
+    this.devoiceAll();
+    this.debug();
+    // Invite wolfs to the private channel
+    this.bot.sendMsg(this.room, this.currentScenario.inviteWolfRoom());
+    for (var i in this.players) if (this.isWolf(this.players[i])) {
+        var p = this.players[i];
+        this.bot.sendMsg(p.name, this.currentScenario.inviteWolfRoomPrivate(this.config.privateChannel));
+        this.bot.invite(this.config.privateChannel, p.name);
+    }
+    this.nextStep(); // cupidon
+};
+
+LoupGarou.prototype.withRole = function(r) {
+    var result = [];
+    for (var i in this.players)
+        if (this.players[i].role === r && this.players[i].dead === null)
+            result.push(this.players[i]);
+    return result;
+};
+
+LoupGarou.prototype.setLovers = function(cupidon, p1, p2) {
+    if (this.currentStep !== STEP.SELECT_CUPIDON)
+        return;
+    p1.lover = p2;
+    p2.lover = p1;
+    this.bot.sendMsg(cupidon.name, this.currentScenario.setLoveWith(p1.name, p2.name));
+    if (p1 !== cupidon) LoupGarou.prototype.nextStep.messageDelayed.push([p1.name, this.currentScenario.inLoveWith(p2.name)]);
+    if (p2 !== cupidon) LoupGarou.prototype.nextStep.messageDelayed.push([p2.name, this.currentScenario.inLoveWith(p1.name)]);
+};
+
+LoupGarou.prototype.aliveNicks = function() {
+    var playerNames = [];
+    for (var i in this.players)
+        playerNames.push(this.players[i].name);
+    return playerNames;
+};
+
+LoupGarou.prototype.aliveNotWolfNicks = function() {
+    var playerNames = [];
+    for (var i in this.players)
+        if (!this.isWolf(this.players[i]))
+            playerNames.push(this.players[i].name);
+    return playerNames;
+};
+
+LoupGarou.prototype.sendCupidonMsg = function (targets) {
+    var msg = this.currentScenario.cupidonMessage(this.aliveNicks());
+    targets.forEach(i => this.bot.sendMsg(i.name, msg), this);
+};
+
+LoupGarou.prototype.onCupidonInternal = function(player, msg) {
+    msg = msg.split(" ");
+    if (msg.length === 2 && this.players[msg[0].trim().toLowerCase()] && this.players[msg[1].trim().toLowerCase()]) {
+        var p1 = this.players[msg[0].trim().toLowerCase()],
+            p2 = this.players[msg[1].trim().toLowerCase()];
+        if (p1 && p2) {
+            this.setLovers(player, p1, p2);
+            player.jobDone = true;
+            return;
+        }
+    }
+    this.sendCupidonMsg([player]);
+};
+
+LoupGarou.prototype.onCupidon = function() {
+    var players = this.withRole(ROLES.CUPIDON);
+    if (players.length) {
+        this.bot.sendMsg(this.room, this.currentScenario.waitingCupidon(this.config.cupidonDelay));
+        this.sendCupidonMsg(players);
+        setTimeout((function() {
+            players.forEach(p => p.jobDone === null && this.bot.sendMsg(p.name, this.currentScenario.setNoLove()), this);
+            this.nextStep();
+        }).bind(this), this.config.cupidonDelay * 1000);
+    }
+    else
+        this.nextStep();
+};
+
+LoupGarou.prototype.onSeerInternal = function(player, msg) {
+    var req = msg.trim().toLowerCase();
+    if (this.players[req] && this.players[req] !== player) {
+        this.bot.sendMsg(player.name, this.currentScenario.whoiswhat[this.players[req].role](this.players[req].name));
+        player.jobDone = true;
+    } else {
+        this.bot.sendMsg(player.name, this.currentScenario.seerMessage(this.aliveNicks().filter(nick => nick !== player.name)));
+    }
+};
+
+LoupGarou.prototype.onSeer = function() {
+    var players = this.withRole(ROLES.SEER);
+    if (players.length) {
+        this.bot.sendMsg(this.room, this.currentScenario.waitingSeer(this.config.seerDelay));
+        players.forEach(i => {
+            this.bot.sendMsg(i.name, this.currentScenario.seerMessage(this.aliveNicks().filter(nick => nick !== i.name)));
+        }, this);
+        setTimeout((function() {
+            players.forEach(p => {
+                if (p.jobDone === null)
+                    this.bot.sendMsg(p.name, this.currentScenario.seerTimeout());
+                p.jobDone = null;
+            }, this);
+            this.nextStep();
+        }).bind(this), this.config.cupidonDelay * 1000);
+    } else {
+        this.nextStep();
+    }
+};
+
+LoupGarou.prototype.onGirlInternal = function(player) {
+    //FIXME ...
+    this.bot.sendMsg(this.room, "Attente petite fille"); // FIXME this.currentScenario
+    this.nextStep();
+};
+
+LoupGarou.prototype.onGirl = function() {
+    var players = this.withRole(ROLES.GIRL);
+    if (players.length) {
+        this.onGirlInternal(players[0]);
+    } else {
+        this.nextStep();
+    }
+};
+
+LoupGarou.prototype.sendCurrentVotes = function(channel) {
+    if (this.voteEngine.hasVotes()) {
+        this.bot.sendMsg(channel, this.currentScenario.currentVotes());
+        this.voteEngine.getVotes().forEach(i => {
+            this.bot.sendMsg(channel, i.name +' : ' +i.from.join(", "));
+        }, this);
+    }
+    else
+        this.bot.sendMsg(channel, this.currentScenario.noVote());
+};
+
+LoupGarou.prototype.onWolf = function() {
+    var players = this.withRole(ROLES.WOLF);
+    if (players.length) {
+        this.bot.voice(this.config.privateChannel, Object.keys(this.nightChannelPersons));
+        this.bot.sendMsg(this.config.privateChannel, this.currentScenario.startWolfVotes());
+        this.bot.sendMsg(this.config.privateChannel, this.currentScenario.remainingPseudos(this.aliveNotWolfNicks()));
+        this.voteEngine.init(players.map(i => i.name), this.aliveNotWolfNicks());
+        this.setReminder(function() {
+            this.bot.sendMsg(this.room, this.currentScenario.waitingWolf(Math.round((this.config.wolfDelay -(Date.now() / 1000) +this.timeInStep))));
+            this.bot.sendMsg(this.config.privateChannel, this.currentScenario.remainingPseudos(this.aliveNotWolfNicks()));
+            this.sendCurrentVotes(this.config.privateChannel);
+        });
+        setTimeout((function() {
+            this.bot.devoice(this.config.privateChannel, Object.keys(this.nightChannelPersons));
+            if (!this.voteEngine.hasVotes()) {
+                var msg = this.currentScenario.wolfTimeout();
+                this.bot.sendMsg(this.room, msg);
+                players.forEach(i => {
+                    this.bot.sendMsg(this.room, this.currentScenario.whowaswhat[i.role](i.name))
+                    i.kill(KILL_REASON.HUNGER, msg);
+                });
+                this.checkEndOfGameAndContinue(); // Will end game
+                return;
+            } else {
+                var p = this.players[this.voteEngine.getVote()];
+                p.kill(KILL_REASON.WOLF, this.currentScenario.killedWolf(p.name));
+                this.bot.sendMsg(this.room, this.current);
+                this.nextStep();
+            }
+        }).bind(this), this.config.wolfDelay * 1000);
+    } else {
+        this.nextStep();
+    }
+};
+
+LoupGarou.prototype.onVillagers = function() {
+    var remainingPlayers = [];
+    for (var i in this.players)
+        if (this.players[i].dead === null)
+            remainingPlayers.push(this.players[i].name);
+
+    this.bot.sendMsg(this.room, this.currentScenario.startVotes());
+    this.bot.sendMsg(this.room, this.currentScenario.remainingPseudos(remainingPlayers.sort()));
+    this.voiceAll();
+    this.nextStep();
+};
+
+LoupGarou.prototype.onEvening = function() {
+    this.devoiceAll();
+    var anyone = [];
+    for (var i in this.players) anyone.push(this.players[i]);
+    anyone = anyone.sort(() => Math.random() -.5)[0];
+    anyone.kill(KILL_REASON.VILLAGER, this.currentScenario.killedVillager(anyone.name));
+};
+
+LoupGarou.prototype.onWitchInternal = function(player) {
+    //FIXME ...
+    this.bot.sendMsg(this.room, "Attente sorciere"); // FIXME this.currentScenario
+    this.nextStep();
+};
+
+LoupGarou.prototype.onWitch = function() {
+    var players = this.withRole(ROLES.WITCH);
+    if (players.length) {
+        this.onWitchInternal(players[0]);
+    } else {
+        this.nextStep();
+    }
+};
+
+LoupGarou.prototype.onLovers = function() {
+    for (var i =0, len = this.justDead.length; i < len; ++i)
+        if (this.justDead[i].lover)
+            this.justDead[i].lover.kill(KILL_REASON.LOVE, this.currentScenario.killedLove(this.justDead[i].lover.name, this.justDead[i].name));
+    this.nextStep();
+};
+
+LoupGarou.prototype.onHunter = function() {
+    for (var i =0, len = this.justDead.length; i < len; ++i)
+        if (this.justDead[i].role === ROLES.HUNTER)
+            this.bot.sendMsg(this.justDead[i].name, "!pan"); // FIXME this.currentScenario...
+    this.nextStep();
+};
+
+LoupGarou.prototype.cleanCorpses = function() {
+    this.justDead = [];
+};
+
+LoupGarou.prototype.checkEndOfGameAndContinue = function() {
+    if (this.checkEndOfGame())
+        this.setStep(STEP.NOT_STARTED);
+    else
+        this.nextStep();
+};
+
+LoupGarou.prototype.checkEndOfGame = function() {
+    var alive = [];
+    for (var i in this.players) this.players[i].dead === null && alive.push(this.players[i]);
+    console.log("alive => ", alive.map(i => i.name));
+    if (alive.length === 2 && alive[0].lover === alive[1]) {
+        this.bot.sendMsg(this.room, this.currentScenario.loverWins());
+        this.bot.sendMsg(this.room, this.currentScenario.congrats(alive.map(i => i.name)));
+        return true;
+    }
+    var liveWolfCount = alive.filter(i => this.isWolf(i), this).length;
+    if (liveWolfCount === alive.length) {
+        this.bot.sendMsg(this.room, this.currentScenario.wolfWins());
+        this.bot.sendMsg(this.room, this.currentScenario.congrats(alive.map(i => i.name)));
+        return true;
+    }
+    if(!liveWolfCount) {
+        this.bot.sendMsg(this.room, this.currentScenario.villagerWins());
+        this.bot.sendMsg(this.room, this.currentScenario.congrats(alive.map(i => i.name)));
+        return true;
+    }
+    return false;
+};
+
+LoupGarou.prototype.advertiseDeath = function(player) {
+    this.bot.sendMsg(this.room, player.killReason +" " +this.currentScenario.whowaswhat[player.role](player.name));
+};
+
+LoupGarou.prototype.onKilled = function(player) {
+    if (this.currentStep < STEP.DAY)
+        this.justDead.push(player);
+    else
+        this.advertiseDeath(player);
+};
+
+LoupGarou.prototype.onDay = function() {
+    if (this.justDead.length === 0)
+        this.bot.sendMsg(this.room, this.currentScenario.dayNoDead());
+    else if (this.justDead.length === 1)
+        this.bot.sendMsg(this.room, this.currentScenario.dayOneDead());
+    else
+        this.bot.sendMsg(this.room, this.currentScenario.dayMultiDead());
+    this.justDead.forEach(i => this.advertiseDeath(i), this);
+};
+
+LoupGarou.prototype.onPm = function(from, text) {
+    if (this.currentStep === STEP.NOT_STARTED)
+        return;
+    var player = this.players[from.toLowerCase()];
+    if (!player)
+        return;
+    if (this.currentStep === STEP.SELECT_CUPIDON && player.role === ROLES.CUPIDON && player.jobDone === null)
+        this.onCupidonInternal(player, text);
+    else if (this.currentStep === STEP.NIGHT_SEER && player.role === ROLES.SEER && player.jobDone === null)
+        this.onSeerInternal(player, text);
+};
+
+module.exports = LoupGarou;
+

+ 11 - 8
quizz.js

@@ -81,7 +81,7 @@ Question.toHint = function(response, normalizedResponse, boundaries, responseInd
         return "Un nombre entre " +boundaries[0] +" et " +boundaries[1];
     }
     else
-        console.error("Unknown response type !");
+        console.error("[quizz] Unknown response type !");
 };
 
 Question.prototype.getHint = function(hintLevel) {
@@ -160,7 +160,7 @@ const ScoreUtils = (function() {
 
 function initQuestionList(filename) {
     return new Promise((ok, ko) => {
-        console.log("Reloading question db");
+        console.log("[quizz] Reloading question db");
         var stream = fs.createReadStream(filename),
             reader = readline.createInterface({input: stream}),
             questions = [],
@@ -178,7 +178,7 @@ function initQuestionList(filename) {
                 if (question.question && question.response)
                     questions.push(question);
             } catch (e) {
-                console.error("Failed to load Database: ", e, "on line", lineNo);
+                console.error("[quizz] Failed to load Database: ", e, "on line", lineNo);
                 borken = true;
                 reader.close();
                 stream.destroy();
@@ -213,6 +213,9 @@ QuizzBot.prototype.init = function(bot, chanName) {
     this.mySQLExportWrapper();
 }
 
+QuizzBot.prototype.onActivate = function(){}
+QuizzBot.prototype.getName = function(){ return "quizz"; }
+
 QuizzBot.prototype.onSelfJoin = function() {
     this.init = true;
     this.reloadDb();
@@ -307,7 +310,7 @@ QuizzBot.prototype.nextQuestionWrapper = function() {
 
 QuizzBot.prototype.nextQuestion = function() {
     this.currentQuestion = this.questions[Math.floor(Math.random() * this.questions.length)];
-    console.log(this.currentQuestion);
+    console.log("[quizz]", this.currentQuestion);
     this.currentHint = 0;
     this.questionDate = Date.now();
     this.bot.sendMsg(this.room, "#" +this.currentQuestion.id +" " +this.currentQuestion.question);
@@ -334,7 +337,7 @@ QuizzBot.prototype.reloadDb = function() {
         _this.bot.sendMsg(this.room, questions.length +" questions loaded from database");
         _this.start();
     }).catch(err => {
-        console.error(err);
+        console.error("[quizz]", err);
         _this.bot.sendMsg(this.room, err);
     });
 }
@@ -626,15 +629,15 @@ QuizzBot.prototype.mySQLExport = function() {
     this.exportScores().then(() => {
         Cache.setExportTs();
         this.resetScores();
-        console.log("Successfully exported scores to MySQL");
+        console.log("[quizz] Successfully exported scores to MySQL");
         this.mySQLExportWrapper();
     }).catch((errString) => {
-        console.error("mySQL Export error saving to database: ", errString);
+        console.error("[quizz] mySQL Export error saving to database: ", errString);
     });
 }
 
 QuizzBot.prototype.exportScores = function() {
-    console.log("Start exporting scores");
+    console.log("[quizz] Start exporting scores");
     return new Promise((ok, ko) => {
         if (!MySQL)
             return ko();

+ 11 - 13
rapido.js

@@ -7,7 +7,7 @@ Object.assign(global, require("./config.js"));
 
 function initWordList(filename) {
     return new Promise((ok, ko) => {
-        console.log("Reloading dictionnary");
+        console.log("[rapido] Reloading dictionnary");
         var stream = fs.createReadStream(filename),
             reader = readline.createInterface({input: stream}),
             words = [];
@@ -37,6 +37,9 @@ Rapido.prototype.init = function(bot, chanName) {
     this.resetScoresWrapper();
 }
 
+Rapido.prototype.getName = function() { return "rapido"; }
+Rapido.prototype.onActivate = function() { };
+
 Rapido.prototype.onSelfJoin = function() {
     this.init = true;
     this.reloadDb();
@@ -67,10 +70,10 @@ Rapido.prototype.reloadDb = function() {
     initWordList(this.config.DICTIONARY_PATH).then(words => {
         _this.reloading = false;
         _this.words = words;
-        _this.bot.sendMsg(this.room, words.length +" words loaded from database");
+        console.info("[rapido] " +words.length +" words loaded from database");
         _this.endWord();
     }).catch(err => {
-        console.error(err);
+        console.error("[rapido]", err);
         _this.bot.sendMsg(this.room, err);
     });
 }
@@ -128,8 +131,10 @@ Rapido.prototype.onWordTimeout = function() {
         this.bot.sendMsg(this.room, "Bah alors ? " +this.wordRequest.join(", ") +" vous fichez quoi ?");
     }
     this.endWord();
-    if (this.setProgression >= this.config.WORD_IN_SET)
+    if (this.setProgression >= this.config.WORD_IN_SET) {
         this.bot.sendMsg(this.room, "fin du temps réglementaire, tapez !rapido pour une prochaine partie.");
+        this.bot.endGame(this);
+    }
     else
         this.startNextWordTimer();
 }
@@ -142,7 +147,7 @@ Rapido.prototype.startNewSet = function() {
 Rapido.prototype.startNextWordTimer = function() {
     var _this = this;
     this.currentWord = this.words[Math.floor(Math.random() * this.words.length)];
-    console.log(this.currentWord);
+    console.log(`[rapido] ${this.currentWord}`);
     _this.bot.sendMsg(this.room, "Attention attention, prochain mot dans " +Math.floor(this.config.NEXT_WORD_DELAY / 1000) +" secondes");
     if (this.setProgression == 0)
         _this.bot.sendMsg(this.room, "Rappel : il faut taper le plus vite possible en minuscule et sans espaces le mot proposé");
@@ -177,11 +182,6 @@ Rapido.prototype.sendScore = function() {
 
 Rapido.prototype.computeScore = function(ellapsed, word, index) {
     const fps = 1000 * word.length / ellapsed;
-    console.log({
-        fps: fps,
-        ellapsed: ellapsed,
-        word: word
-    });
     var base = 2 +(index == 0 ? 2 : 0);
     for (var i =0, len = this.config.SCORE_MAP.length; i < len; ++i) {
         var scoreItem = this.config.SCORE_MAP[i];
@@ -195,9 +195,7 @@ Rapido.prototype.onMessageInternal = function(username, user, msg) {
     const lmsg = msg.toLowerCase();
     msg = lmsg.substr(0, 1) + msg.substr(1);
 
-    if (lmsg === "!next") {
-        this.bot.sendMsg(this.room, "La commande !next a été remplacé par !rapido.");
-    } else if (lmsg === "!rapido") {
+    if (lmsg === "!rapido") {
         if (this.wordRequest.indexOf(username) === -1)
             this.wordRequest.push(username);
         if (!this.currentWord && this.wordRequest.length >= this.config.MIN_PLAYERS)