Jelajahi Sumber

[add] irc connector

B Thibault 8 tahun lalu
induk
melakukan
b01314900d

+ 1 - 1
srv/src/ircServer/ircCmd.js

@@ -4,7 +4,7 @@ const CMD_REG = (/(\:\S+ )?(\w+)((?: [^:\ ][^\ ]*)*)(?: :(.*))?/)
 function parseParams(str) {
     if (str && str.length)
         str = str.substr(1);
-    return str.split(/\s/);
+    return str.split(/\s/).filter(i => i.length);
 }
 
 function IrcCmd(regResult) {

+ 72 - 12
srv/src/ircServer/ircConnection.js

@@ -9,6 +9,8 @@ const parseIrcCmd = require('./ircCmd.js').parse
 
     ,CONFIG = require("../../config.js");
 
+const DEBUG = false;
+
 const SERVER_CONFIG = {
     port: CONFIG.port,
     hostname: CONFIG.hostname,
@@ -28,18 +30,42 @@ IrcConnection = module.exports.IrcConnection = function(sock) {
     this.polling = false;
     this.joinedChannels = {};
 
+    this.pendingMsgs = [];
+
+    this.pingInterval = 0;
+    this.pingSent = 0;
+
     this.sock = sock;
     this.sock.on("data", (data) => {
         data.toString("utf-8").split(/\r?\n/g).forEach((str) => { _this.parse(str); });
     });
     this.sock.once("close", () => {
         console.log("[IRC] closed connection" +(this.account ? (" for user #" +this.account.id) : ""));
-        this.polling = false;
+        if (_this.pingInterval)
+            clearInterval(_this.pingInterval);
+        _this.polling = false;
     });
 }
 
+IrcConnection.prototype.delayCacheExpiration = function() {
+    var services = this.account.getServices(),
+        now = Date.now();
+    // update cache interval to avoid expiration
+    for (var serviceId in services) {
+        switch (services[serviceId].type) {
+            case "Slack":
+                slackManager.lazyGet(serviceId, services[serviceId].oauthParam, now);
+            break;
+
+            default:
+                console.error("Unknown service type for ", services[serviceId], " with account #" +req.account.id);
+        }
+    }
+};
+
 IrcConnection.prototype.write = function(line) {
-    console.log("[IRC] >>> " +line);
+    if (DEBUG)
+        console.log("[IRC] >>> " +line);
     this.sock.write(line +"\r\n");
 };
 
@@ -87,6 +113,8 @@ IrcConnection.prototype.checkConnectionDone = function(account, nickname, userna
                     console.error("Unknown service type for ", services[serviceId], " with account #" +req.account.id);
             }
         }
+
+        this.pingInterval = setInterval(this.sendPing.bind(this), 60000);
         this.polling = true;
         this.poll();
         return true;
@@ -96,6 +124,14 @@ IrcConnection.prototype.checkConnectionDone = function(account, nickname, userna
 
 const NAMES_NICK_LIMIT = 3;
 
+IrcConnection.prototype.sendPing = function() {
+    if (this.pingSent) {
+        this.sock.end();
+    }
+    this.pingSent = '' +Math.floor(Math.random() * 1000000) +1;
+    this.write(':' +SERVER_CONFIG.hostname +' PING :' +this.pingSent);
+};
+
 IrcConnection.prototype.sendJoin = function(channel) {
     let res = this.joinedChannels[channel.id] = { topic: "", purpose: "" },
         chanCompat = this.chanCompat(channel.id);
@@ -121,7 +157,7 @@ IrcConnection.prototype.sendJoin = function(channel) {
 
 IrcConnection.prototype.sendTopic = function(channel) {
     this.joinedChannels[channel.id].topic = channel.topic.value;
-    this.write(":" +(channel.topic.creator ? this.chatContext.getUser(channel.topic.creator).getName() : SERVER_CONFIG.hostname) +" TOPIC #" +this.chanCompat(channel.id) +" :" +channel.topic.value);
+    this.write(":" +(channel.topic.creator ? this.chatContext.getUser(channel.topic.creator).getName() : SERVER_CONFIG.hostname) +" TOPIC " +this.chanCompat(channel.id) +" :" +channel.topic.value);
 };
 
 IrcConnection.prototype.sendPurpose = function(channel) {
@@ -175,6 +211,13 @@ IrcConnection.prototype.sendLiveUpdate = function(live) {
     for (var chanId in live) {
         let chanCompatId = this.chanCompat(chanId, this);
         live[chanId].forEach((msg) => {
+            if (msg.user && this.chatContext.isMe(msg.user)) {
+                for (var i=0, nbPending =this.pendingMsgs.length; i < nbPending; i++)
+                    if (this.pendingMsgs[i].isMe === !!msg.isMeMessage && this.pendingMsgs[i].chan === chanId && this.pendingMsgs[i].msg === msg.text) {
+                        this.pendingMsgs.splice(i, 1);
+                        return;
+                    }
+            }
             var pretext = ":" +(msg.user ? this.userCompat(msg.user) : msg.username) +" PRIVMSG " +chanCompatId +" :";
             if (msg.isMeMessage)
                 pretext += String.fromCharCode(0x01) +"ACTION ";
@@ -203,7 +246,8 @@ IrcConnection.prototype.parse = function(str) {
     var cmd = parseIrcCmd(str),
         _this = this;
     if (cmd) {
-        console.log("[IRC] <<< " +str);
+        if (DEBUG)
+            console.log("[IRC] <<< " +str);
         if (cmd.command === "QUIT") {
             _this.sock.end();
         } else if (!this.isAuthed()) {
@@ -212,7 +256,7 @@ IrcConnection.prototype.parse = function(str) {
                     if (account) {
                         _this.checkConnectionDone(account);
                     } else {
-                        console.log("Invalid password from IRC gateway");
+                        console.error("[IRC] Invalid password");
                         _this.sock.end();
                     }
                 });
@@ -228,12 +272,22 @@ IrcConnection.prototype.parse = function(str) {
                 case "PING":
                     this.write(":" +SERVER_CONFIG.hostname +" PONG :" +cmd.args[0]);
                 break;
+                case "PONG":
+                    if (cmd.args[0] === this.pingSent) {
+                        this.pingSent = 0;
+                        this.delayCacheExpiration();
+                    } else {
+                        console.error("[IRC] wrong PONG response (sent " +this.pingSent +", got " +cmd.args[0] +')');
+                        this.sock.end();
+                    }
+                break;
                 case "PRIVMSG":
                     let msg = cmd.args[1],
-                        target = cmd.args[0].split('@', 2);
-                    if (target[0] === '#')
+                        target = cmd.args[0].split('@', 2),
+                        sent = false;
+                    if (target[0][0] === '#')
                         target[0] = target[0].substr(1);
-                    for (let i =0, nbCtx = this.chatContext.contexts.length; i < nbCtx; i++) {
+                    ctxLoop: for (let i =0, nbCtx = this.chatContext.contexts.length; i < nbCtx; i++) {
                         let ctx = this.chatContext.contexts[i];
                         if (ctx.getChatContext().team && ctx.getChatContext().team.name === target[1]) {
                             for (let chanId in ctx.getChatContext().channels) {
@@ -244,18 +298,24 @@ IrcConnection.prototype.parse = function(str) {
                                             msg = msg.substr(8, msg.length -9);
                                         else
                                             msg = msg.substr(8);
-                                        ctx.sendMeMsg(chan, [ msg ]);
+                                        msg = ctx.sendMeMsg(chan, [ msg ]);
+                                        this.pendingMsgs.push({isMe: true, chan: chan.id, msg: msg});
                                     } else {
-                                        ctx.sendMsg(chan, [ msg ]);
+                                        msg = ctx.sendMsg(chan, [ msg ]);
+                                        this.pendingMsgs.push({isMe: false, chan: chan.id, msg: msg});
                                     }
-                                    break;
+                                    sent = true;
+                                    break ctxLoop;
                                 }
                             }
                         }
                     }
+                    if (!sent)
+                        this.write(":" +SERVER_CONFIG.hostname +" NOTICE * :No such user or team " +cmd.args[0]);
                 break;
                 default:
-                    console.log(cmd);
+                    if (DEBUG)
+                        console.log(cmd);
                 break;
             }
         }

+ 5 - 2
srv/src/multichatManager.js

@@ -241,9 +241,11 @@ MultiChatManager.prototype.getUser = function(userId) {
  * @return {boolean}
 **/
 MultiChatManager.prototype.isMe = function(userId) {
-    for (var i =0, nbCtx = this.contexts.length; i < nbCtx; i++)
-        if (this.contexts[i].getChatContext().self.id === userId)
+    for (var i =0, nbCtx = this.contexts.length; i < nbCtx; i++) {
+        var ctx = this.contexts[i].getChatContext();
+        if (ctx.self && ctx.self.id === userId)
             return true;
+    }
     return false;
 };
 
@@ -311,6 +313,7 @@ MultiChatManager.prototype.startPolling = function(knownVersion, reqT, callback,
  * @param {number} knownVersion
  * @param {Function} callback
  * @param {(function(number, function((Array<{expose:Function, modified: number}>|null))))=} checkConfigUpdate
+ * @param {boolean=} withTyping default true
 **/
 MultiChatManager.prototype.poll = function(knownVersion, reqT, callback, checkConfigUpdate, withTyping) {
     this.contexts.forEach(function(ctx) {

+ 10 - 2
srv/src/slack.js

@@ -597,13 +597,16 @@ Slack.prototype.removeReaction = function(channel, msgId, reaction) {
 /**
  * @param {SlackChan|SlackGroup|SlackIms} channel
  * @param {Array.<string>} text
+ * @return {string} sent msg
 **/
 Slack.prototype.sendMeMsg = function(channel, text) {
+    var text = idify(this.data, text.join("\n"));
     httpsRequest(SLACK_ENDPOINT +GETAPI.postMeMsg
         +"?token=" +this.token
         +"&channel=" +channel.remoteId
-        +"&text=" +text.join("\n")
+        +"&text=" +text
         +"&as_user=true");
+    return text;
 };
 
 /**
@@ -690,6 +693,7 @@ function idify(data, str) {
  * @param {SlackChan|SlackGroup|SlackIms} channel
  * @param {Array.<string>} text
  * @param {Array.<Object>=} attachments
+ * @return {string} sent message
 **/
 Slack.prototype.sendMsg = function(channel, text, attachments) {
     if (attachments) {
@@ -697,13 +701,16 @@ Slack.prototype.sendMsg = function(channel, text, attachments) {
             if (attachmentObj["ts"])
                 attachmentObj["ts"] /= 1000;
         });
+        var decodedText = idify(this.data, text.join("\n"));
+
         httpsRequest(SLACK_ENDPOINT +GETAPI.postMsg
             +"?token=" +this.token
             +"&channel=" +channel.remoteId
-            +"&text=" +idify(this.data, text.join("\n"))
+            +"&text=" +decodedText
             +"&link_names=true"
             + (attachments ? ("&attachments=" +encodeURIComponent(JSON.stringify(attachments))) : "")
             +"&as_user=true");
+        return fullDecodedText;
     } else {
         var decodedText = [];
         text.forEach(function(i) {
@@ -717,6 +724,7 @@ Slack.prototype.sendMsg = function(channel, text, attachments) {
             text: fullDecodedText
         };
         this.rtm.send('{"id":' +this.rtmId++ +',"type":"message","channel":"' +channel.remoteId +'", "text":' +JSON.stringify(fullDecodedText) +'}');
+        return fullDecodedText;
     }
 };