Przeglądaj źródła

[add] receive live messages
[add] display messages

B Thibault 8 lat temu
rodzic
commit
1b963551ed

+ 1 - 2
cli/data.js

@@ -31,7 +31,7 @@ SlackWrapper.prototype.update = function(data) {
         for (var i in data["live"]) {
             var history = this.history[i];
             if (!history)
-                this.history[i] = new SlackHistory(i, data["live"][i]);
+                this.history[i] = new SlackHistory(i, 500, data["live"][i]);
             else
                 history.pushAll(data["live"][i]);
         }
@@ -39,7 +39,6 @@ SlackWrapper.prototype.update = function(data) {
             onRoomUpdated();
         }
     }
-    console.log(this);
 };
 
 SLACK = new SlackWrapper();

+ 9 - 0
cli/resources.js

@@ -4,6 +4,7 @@ var R = {
         ,chatList: "chatList"
         ,currentRoom: {
             title: "currentRoomTitle"
+            ,content: "chatWindow"
         }
     }
     ,klass: {
@@ -12,6 +13,14 @@ var R = {
         ,chatList: {
             entry: "slack-context-room"
         }
+        ,msg: {
+            item: "slackmsg-item"
+            ,ts: "slackmsg-ts"
+            ,author: "slackmsg-author"
+            ,authorname: "slackmsg-author-name"
+            ,authorAvatar: "slackmsg-author-img"
+            ,msg: "slackmsg-msg"
+        }
     }
 };
 

+ 35 - 1
cli/ui.js

@@ -66,10 +66,44 @@ function onRoomSelected() {
         name = members.join(", ");
     }
     document.getElementById(R.id.currentRoom.title).textContent = name;
+    onRoomUpdated();
+}
+
+function createMessageDom(msg) {
+    var dom = document.createElement("div")
+        ,ts = document.createElement("div")
+        ,text = document.createElement("div")
+        ,author = document.createElement("div")
+        ,authorImg = document.createElement("img")
+        ,authorName = document.createElement("span");
+    dom.className = R.klass.msg.item;
+    ts.className = R.klass.msg.ts;
+    text.className = R.klass.msg.msg;
+    author.className = R.klass.msg.author;
+    authorImg.className = R.klass.msg.authorAvatar;
+    authorName.className = R.klass.msg.authorname;
+    ts.textContent = (new Date(msg.ts * 1000)).toLocaleTimeString();
+    text.textContent = msg.raw["text"];
+    var sender = SLACK.context.getMember(msg.raw["user"]);
+    authorName.textContent = sender.name;
+    authorImg.src = sender.icons.image_48;
+    author.appendChild(authorImg);
+    author.appendChild(authorName);
+    dom.appendChild(author);
+    dom.appendChild(text);
+    dom.appendChild(ts);
+    return dom;
 }
 
 function onRoomUpdated() {
-    console.log("Updated room", SLACK.history[SELECTED_ROOM.id]);
+    var chatFrag = document.createDocumentFragment();
+
+    document.getElementById(R.id.currentRoom.content).textContent = "";
+    if (SLACK.history[SELECTED_ROOM.id])
+        SLACK.history[SELECTED_ROOM.id].messages.forEach(function(msg) {
+            chatFrag.appendChild(createMessageDom(msg));
+        });
+    document.getElementById(R.id.currentRoom.content).appendChild(chatFrag);
 }
 
 function onChanClick(e) {

+ 1 - 1
srv/public/index.html

@@ -12,7 +12,7 @@
         </aside>
         <div class="slack-chat-container">
             <div class="slack-chat-title" id="currentRoomTitle"></div>
-            <div class="slack-chat-content"></div>
+            <div class="slack-chat-content" id="chatWindow"></div>
             <div class="slack-chat-control">
                 <input type="text" />
                 <input type="submit" />

+ 10 - 7
srv/public/slack.min.js

@@ -1,7 +1,10 @@
-function f(b,a){this.id=b.id;this.name=b.name;this.b=parseFloat(b.last_read);this.a={};if(b.members)for(var g=0,c=b.members.length;g<c;g++){var e=h(a,b.members[g]);this.a[e.id]=e;e.f[this.id]=this}}function l(b,a){var g=[];this.id=a.id;this.a={};for(var c=0,e=a.members.length;c<e;c++){var d=h(b,a.members[c]);this.a[a.members[c]]=d;d.f[this.id]=this;g.push(d.name)}this.name=g.join(", ");this.b=parseFloat(a.last_read)}function p(b,a){this.id=a.id;this.c=b;this.b=parseFloat(a.last_read)}
-function q(b){this.id=b.id;this.name=b.name;this.status=b.status;this.f={};this.a=null}function r(b){this.id=b.id;this.name=b.name;this.f={};this.a=null}function t(){this.f={};this.b={};this.g={};this.a={};this.c=null;this.h={}}function h(b,a){return b.a[a]||b.h[a]||null}"undefined"!==typeof module&&(module.i.j=t);function u(){}function v(b,a){this.id="string"===typeof b?b:b.id;this.a=[];a&&w(this,a)}function w(b,a){for(a.forEach(function(){this.a.push(new u)}.bind(b));500<b.a.length;)b.a.shift()}v.prototype.push=function(){for(this.a.push(new u);500<this.a.length;)this.a.shift()};"undefined"!==typeof module&&(module.i.l=v);function x(){var b=document.createDocumentFragment(),a=y.a.c?Object.keys(y.a.c.f):[];a.sort(function(b,a){return b[0]!==a[0]?b[0]-a[0]:(y.a.f[b]||y.a.b[b]).name.localeCompare((y.a.f[a]||y.a.b[a]).name)});a.forEach(function(a){a=y.a.f[a]||y.a.b[a];var c=document.createElement("li");c.id=a.id;c.className="slack-context-room";c.textContent=a.name;c&&b.appendChild(c)});a=y.a.a?Object.keys(y.a.a):[];a.sort(function(b,a){return y.a.a[b].name.localeCompare(y.a.a[a].name)});a.forEach(function(a){a=y.a.a[a].a;
-var c=document.createElement("li");c.id=a.id;c.className="slack-context-room";c.textContent=a.c.name;c&&b.appendChild(c)});document.getElementById("chanList").textContent="";document.getElementById("chanList").appendChild(b)}
-function z(b){for(;b.target!==b.currentTarget&&b.target;){if(b.target.classList.contains("slack-context-room")){if((b=y.a.f[b.target.id]||y.a.g[b.target.id]||y.a.b[b.target.id])&&b!==A){A&&document.getElementById(A.id).classList.remove("selected");document.getElementById(b.id).classList.add("selected");document.body.classList.remove("no-room-selected");A=b;b=void 0;var a=A.name||(A.c?A.c.name:void 0);if(!a){a=[];for(b in A.a)a.push(A.a[b].name);a=a.join(", ")}document.getElementById("currentRoomTitle").textContent=
-a;A.b&&!y.b[A.id]&&(b=new XMLHttpRequest,b.open("GET","api?room="+A.id,!0),b.send(null))}break}b.target=b.target.parentElement}}document.addEventListener("DOMContentLoaded",function(){document.getElementById("chatList").addEventListener("click",z);B()});var y;y=new function(){this.c=0;this.a=new t;this.b={}};var C=5,A=null;function D(b){var a=new XMLHttpRequest;a.timeout=6E4;a.onreadystatechange=function(){if(4===a.readyState)if(a.status){var g=null,c=2===Math.floor(a.status/100);if(c){C=5;g=a.response;try{g=JSON.parse(g)}catch(e){g=null}}else C+=Math.floor(C/2),C=Math.min(60,C);b(c,g)}else D(b),C=5};a.open("GET","api?v="+y.c,!0);a.send(null)}
-function E(b,a){if(b){if(a){var g=y;a.v&&(g.c=a.v);if(a["static"]){for(var c=g.a,e=a["static"],d=0,k=e.bots.length;d<k;d++)c.h[e.bots[d].id]=new r(e.bots[d]);d=0;for(k=e.users.length;d<k;d++)c.a[e.users[d].id]=new q(e.users[d]);d=0;for(k=e.ims.length;d<k;d++){var m=h(c,e.ims[d].user);m&&(m.a=new p(m,e.ims[d]),c.g[m.a.id]=m.a)}d=0;for(k=e.channels.length;d<k;d++)c.f[e.channels[d].id]=new f(e.channels[d],c);d=0;for(k=e.groups.length;d<k;d++)c.b[e.groups[d].id]=new l(c,e.groups[d]);c.c=h(c,e.self.id);
-x()}if(a.live){for(var n in a.live)(c=g.b[n])?w(c,a.live[n]):g.b[n]=new v(n,a.live[n]);A&&a.live[A.id]&&console.log("Updated room",y.b[A.id])}console.log(g)}B()}else setTimeout(B,1E3*C)}function B(){D(E)};
+function g(a,b){this.id=a.id;this.name=a.name;this.b=parseFloat(a.last_read);this.a={};if(a.members)for(var e=0,c=a.members.length;e<c;e++){var f=h(b,a.members[e]);this.a[f.id]=f;f.f[this.id]=this}}function l(a,b){var e=[];this.id=b.id;this.a={};for(var c=0,f=b.members.length;c<f;c++){var d=h(a,b.members[c]);this.a[b.members[c]]=d;d.f[this.id]=this;e.push(d.name)}this.name=e.join(", ");this.b=parseFloat(b.last_read)}function m(a,b){this.id=b.id;this.c=a;this.b=parseFloat(b.last_read)}
+function q(a){this.id=a.id;this.name=a.name;this.status=a.status;this.b={w:a.profile.image_24,A:a.profile.image_32,i:a.profile.image_48,m:a.profile.image_72,u:a.profile.image_192,C:a.profile.image_512};this.f={};this.a=null}function r(a){this.id=a.id;this.name=a.name;this.b={B:a.icons.image_36,i:a.icons.image_48,m:a.icons.image_72};this.f={};this.a=null}function t(){this.f={};this.b={};this.g={};this.a={};this.c=null;this.h={}}function h(a,b){return a.a[b]||a.h[b]||null}
+"undefined"!==typeof module&&(module.l.o=t);function u(a){this.j=parseFloat(a.ts);this.raw=a}function v(a,b,e){this.id="string"===typeof a?a:a.id;this.a=[];this.b=b;e&&w(this,e)}function w(a,b){b.forEach(function(a){this.push(a)}.bind(a))}v.prototype.push=function(a){for(var b=parseFloat(a.ts),e=0,c=this.a.length;e<c;e++)if(this.a[e].j===b)return!1;for(this.a.push(new u(a));this.a.length>this.b;)this.a.shift()};"undefined"!==typeof module&&(module.l.s=v);function x(){var a=document.createDocumentFragment(),b=y.a.c?Object.keys(y.a.c.f):[];b.sort(function(a,b){return a[0]!==b[0]?a[0]-b[0]:(y.a.f[a]||y.a.b[a]).name.localeCompare((y.a.f[b]||y.a.b[b]).name)});b.forEach(function(b){b=y.a.f[b]||y.a.b[b];var c=document.createElement("li");c.id=b.id;c.className="slack-context-room";c.textContent=b.name;c&&a.appendChild(c)});b=y.a.a?Object.keys(y.a.a):[];b.sort(function(a,b){return y.a.a[a].name.localeCompare(y.a.a[b].name)});b.forEach(function(b){b=y.a.a[b].a;
+var c=document.createElement("li");c.id=b.id;c.className="slack-context-room";c.textContent=b.c.name;c&&a.appendChild(c)});document.getElementById("chanList").textContent="";document.getElementById("chanList").appendChild(a)}
+function z(a){var b=document.createElement("div"),e=document.createElement("div"),c=document.createElement("div"),f=document.createElement("div"),d=document.createElement("img"),k=document.createElement("span");b.className="slackmsg-item";e.className="slackmsg-ts";c.className="slackmsg-msg";f.className="slackmsg-author";d.className="slackmsg-author-img";k.className="slackmsg-author-name";e.textContent=(new Date(1E3*a.j)).toLocaleTimeString();c.textContent=a.raw.text;a=h(y.a,a.raw.user);k.textContent=
+a.name;d.src=a.b.i;f.appendChild(d);f.appendChild(k);b.appendChild(f);b.appendChild(c);b.appendChild(e);return b}function A(){var a=document.createDocumentFragment();document.getElementById("chatWindow").textContent="";y.b[B.id]&&y.b[B.id].a.forEach(function(b){a.appendChild(z(b))});document.getElementById("chatWindow").appendChild(a)}
+function C(a){for(;a.target!==a.currentTarget&&a.target;){if(a.target.classList.contains("slack-context-room")){if((a=y.a.f[a.target.id]||y.a.g[a.target.id]||y.a.b[a.target.id])&&a!==B){B&&document.getElementById(B.id).classList.remove("selected");document.getElementById(a.id).classList.add("selected");document.body.classList.remove("no-room-selected");B=a;a=void 0;var b=B.name||(B.c?B.c.name:void 0);if(!b){b=[];for(a in B.a)b.push(B.a[a].name);b=b.join(", ")}document.getElementById("currentRoomTitle").textContent=
+b;A();B.b&&!y.b[B.id]&&(a=new XMLHttpRequest,a.open("GET","api?room="+B.id,!0),a.send(null))}break}a.target=a.target.parentElement}}document.addEventListener("DOMContentLoaded",function(){document.getElementById("chatList").addEventListener("click",C);D()});var y;y=new function(){this.c=0;this.a=new t;this.b={}};var E=5,B=null;function F(a){var b=new XMLHttpRequest;b.timeout=6E4;b.onreadystatechange=function(){if(4===b.readyState)if(b.status){var e=null,c=2===Math.floor(b.status/100);if(c){E=5;e=b.response;try{e=JSON.parse(e)}catch(f){e=null}}else E+=Math.floor(E/2),E=Math.min(60,E);a(c,e)}else F(a),E=5};b.open("GET","api?v="+y.c,!0);b.send(null)}
+function G(a,b){if(a){if(b){var e=y;b.v&&(e.c=b.v);if(b["static"]){for(var c=e.a,f=b["static"],d=0,k=f.bots.length;d<k;d++)c.h[f.bots[d].id]=new r(f.bots[d]);d=0;for(k=f.users.length;d<k;d++)c.a[f.users[d].id]=new q(f.users[d]);d=0;for(k=f.ims.length;d<k;d++){var n=h(c,f.ims[d].user);n&&(n.a=new m(n,f.ims[d]),c.g[n.a.id]=n.a)}d=0;for(k=f.channels.length;d<k;d++)c.f[f.channels[d].id]=new g(f.channels[d],c);d=0;for(k=f.groups.length;d<k;d++)c.b[f.groups[d].id]=new l(c,f.groups[d]);c.c=h(c,f.self.id);
+x()}if(b.live){for(var p in b.live)(c=e.b[p])?w(c,b.live[p]):e.b[p]=new v(p,500,b.live[p]);B&&b.live[B.id]&&A()}}D()}else setTimeout(D,1E3*E)}function D(){F(G)};

+ 8 - 0
srv/public/style.css

@@ -28,3 +28,11 @@ body {
     background-color: lightgrey;
 }
 
+.slack-chat-title { font-size: 1.75em; font-style: italic; }
+.slackmsg-item { margin-top: 15px; }
+.slackmsg-author { display: inline-block; }
+.slackmsg-author-img { margin-right: 15px; }
+.slackmsg-author-name { position: absolute; max-height: 1em; overflow: hidden; font-style: italic; }
+.slackmsg-msg { display: inline-block; vertical-align: top; margin-top: 1em; }
+.slackmsg-ts {}
+

+ 14 - 13
srv/src/httpServ.js

@@ -112,20 +112,21 @@ Server.prototype.onRequest = function(req, res) {
             res.end();
         } else {
             res.slack.onRequest(req.urlObj.queryTokens.v || 0, (slack, newData) => {
-                console.log("write handler");
-                if (!slack.connected) {
-                    res.writeHeader("403", {
-                        "Content-Type": "application/json"
-                    });
-                    res.write(slack.error);
-                } else {
-                    res.writeHeader("200", {
-                        "Content-Type": "application/json"
-                    });
-                    res.write(JSON.stringify(newData));
-                }
+                try {
+                    if (!slack.connected) {
+                        res.writeHeader("403", {
+                            "Content-Type": "application/json"
+                        });
+                        res.write(slack.error);
+                    } else {
+                        res.writeHeader("200", {
+                            "Content-Type": "application/json"
+                        });
+                        res.write(JSON.stringify(newData));
+                    }
+                    res.end();
+                } catch (e) {}
                 sessionManager.saveSession(req.session);
-                res.end();
             });
         }
     }

+ 29 - 11
srv/src/slack.js

@@ -10,13 +10,15 @@ const
 ;
 
 const SLACK_ENDPOINT = "https://slack.com/api/"
-,GETAPI = {
-    rtmStart: "rtm.start"
-    ,oauth: "oauth.access"
-    ,channelHistory: "channels.history"
-    ,directHistory: "im.history"
-    ,groupHistory: "groups.history"
-};
+    ,GETAPI = {
+        rtmStart: "rtm.start"
+        ,oauth: "oauth.access"
+        ,channelHistory: "channels.history"
+        ,directHistory: "im.history"
+        ,groupHistory: "groups.history"
+    }
+    ,HISTORY_LENGTH = 25
+;
 
 function Slack(sess) {
     this.token = sess.slackToken;
@@ -143,8 +145,22 @@ Slack.prototype.getLiveUpdates = function(knownVersion) {
 };
 
 Slack.prototype.onMessage = function(msg) {
-    //TODO handle live
     this.data.onMessage(msg);
+    if (msg["channel"] && msg["type"] === "message") {
+        var histo = this.history[msg["channel"]];
+        if (!histo) {
+            if (this.data.getChannel(msg["channel"])) {
+                this.fetchHistory(msg["channel"], ((histo) => {
+                    histo.push(msg);
+                    this.data.liveV = Math.max(this.data.liveV, parseFloat(msg["ts"]));
+                }).bind(this));
+            }
+        }
+        else {
+            histo.push(msg);
+            this.data.liveV = Math.max(this.data.liveV, parseFloat(msg["ts"]));
+        }
+    }
 };
 
 Slack.prototype.connectRtm = function(url, cb) {
@@ -187,7 +203,7 @@ Slack.getOauthToken = function(code, cb) {
     });
 };
 
-Slack.prototype.fetchHistory = function(targetId) {
+Slack.prototype.fetchHistory = function(targetId, callback) {
     var _this = this
         ,baseUrl = "";
 
@@ -201,16 +217,18 @@ Slack.prototype.fetchHistory = function(targetId) {
     httpsRequest(baseUrl
         +"?token="+this.token
         +"&channel=" +targetId
-        +"&count=25",
+        +"&count=" +HISTORY_LENGTH,
     (status, resp) => {
         if (status === 200 && resp && resp.ok) {
             var history = _this.history[targetId];
             if (history)
                 history.pushAll(resp.messages);
             else {
-                history = _this.history[targetId] = new SlackHistory(targetId, resp.messages);
+                history = _this.history[targetId] = new SlackHistory(targetId, HISTORY_LENGTH, resp.messages);
                 history.isNew = true;
             }
+            if (callback)
+                callback(history);
         }
     });
 };

+ 8 - 0
srv/src/slackData.js

@@ -417,6 +417,14 @@ SlackData.prototype.getMember = function(mId) {
     return this.users[mId] || this.bots[mId] || null;
 };
 
+/**
+ * @param {string} chanId
+ * @return {SlackChan|SlackGroup|SlackIms|null}
+**/
+SlackData.prototype.getChannel = function(chanId) {
+    return this.channels[chanId] || this.ims[chanId] || this.groups[chanId] || null;
+};
+
 /** @return {Object} */
 SlackData.prototype.buildStatic = function() {
     var res = {

+ 19 - 14
srv/src/slackHistory.js

@@ -6,19 +6,20 @@ function SlackMessage(e) {
     /** @type {string} **/
     this.userId = e["user"];
 
-    /** @type {string} **/
-    this.ts = e["ts"];
+    /** @type {number} **/
+    this.ts = parseFloat(e["ts"]);
 
-    /** @type {string} **/
-    this.message = e["text"];
+    /** @type {*} **/
+    this.raw = e;
 }
 
 /**
  * @constructor
  * @param {SlackChan|SlackGroup|SlackIms|string} room or roomId
+ * @param {number} keepMessages number of messages to keep in memory
  * @param {Array|undefined} evts
 **/
-function SlackHistory(room, evts) {
+function SlackHistory(room, keepMessages, evts) {
     /** @type {string} */
     this.id = typeof room === "string" ? room : room.id;
     /** @type {Array.<SlackMessage>} */
@@ -26,15 +27,14 @@ function SlackHistory(room, evts) {
     /** @type number */
     this.v = 0;
 
+    /** @const @type {number} */
+    this.keepMessages = keepMessages;
+
     if (evts) this.pushAll(evts);
 }
 
 SlackMessage.prototype.toStatic = function() {
-    return {
-        "ts": this.ts
-        ,"user": this.userId
-        ,"text": this.message
-    };
+    return this.raw;
 }
 
 /**
@@ -42,15 +42,20 @@ SlackMessage.prototype.toStatic = function() {
 **/
 SlackHistory.prototype.pushAll = function(evts) {
     evts.forEach(function(e) {
-        this.messages.push(new SlackMessage(e));
+        this.push(e);
     }.bind(this));
-    while (this.messages.length > 500)
-        this.messages.shift();
 }
 
 SlackHistory.prototype.push = function(ev) {
+    var ts = parseFloat(ev["ts"]);
+
+    for (var i =0, nbMsg = this.messages.length; i < nbMsg; i++) {
+        if (this.messages[i].ts === ts) {
+            return false;
+        }
+    }
     this.messages.push(new SlackMessage(ev));
-    while (this.messages.length > 500)
+    while (this.messages.length > this.keepMessages)
         this.messages.shift();
 }