| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- const
- WebSocket = require('ws'),
- https = require('https'),
- sleep = require("sleep").sleep
- ,SlackData = require("./slackData.js").SlackData
- ,SlackHistory = require("./slackHistory.js").SlackHistory
- ,config = require("../config.js")
- ;
- const SLACK_ENDPOINT = "https://slack.com/api/"
- ,SLACK_HOSTNAME = "slack.com"
- ,SLACK_ENDPOINT_PATH = "/api/"
- ,GETAPI = {
- rtmStart: "rtm.start"
- ,oauth: "oauth.access"
- ,channelHistory: "channels.history"
- ,directHistory: "im.history"
- ,groupHistory: "groups.history"
- ,postMsg: "chat.postMessage"
- ,removeMsg: "chat.delete"
- ,postFile: "files.upload"
- ,emojiList: "emoji.list"
- ,addReaction: "reactions.add"
- ,removeReaction: "reactions.remove"
- }
- ,HISTORY_LENGTH = 35
- ,UPDATE_LIVE = [
- "message"
- ,"pin_added"
- ,"pin_removed"
- ,"reaction_added"
- ,"reaction_removed"
- ,"star_added"
- ,"star_removed"
- ] // Message type that affect live history
- ;
- var
- SLACK_SESSIONS = []
- ;
- setInterval(function() {
- SLACK_SESSIONS.forEach(function(slackInst) {
- slackInst.closeIfInnactive();
- });
- }, 60000);
- function Slack(sess) {
- this.token = sess.slackToken;
- this.rtm = null;
- this.data = new SlackData(this);
- this.history = {};
- this.connected = false;
- }
- Slack.prototype.onRequest = function(knownVersion, cb) {
- this.lastCb = cb;
- if (this.connected === false) {
- this.connect(knownVersion);
- } else {
- this.waitForEvent(knownVersion);
- }
- };
- function httpsRequest(url, cb) {
- https.get(url, (res) => {
- if (res.statusCode !== 200) {
- cb(res.statusCode, null);
- return;
- }
- var body = null;
- res.on('data', (chunk) => {
- body = body ? Buffer.concat([body, chunk], body.length +chunk.length) : Buffer.from(chunk);
- });
- res.on('end', () => {
- try {
- body = JSON.parse(body.toString("utf8"));
- } catch (e) {}
- cb && cb(res.statusCode, body);
- });
- });
- }
- Slack.prototype.connect = function(knownVersion) {
- var _this = this;
- this.connected = undefined;
- httpsRequest(SLACK_ENDPOINT +GETAPI.rtmStart +"?token=" +this.token, (status, body) => {
- if (status !== 200) {
- _this.error = body.error;
- _this.connected = false;
- console.error("Slack api responded " +status);
- _this.lastCb(_this);
- return;
- }
- if (!body) {
- _this.error = "Slack API error";
- _this.connected = false;
- _this.lastCb(_this);
- return;
- }
- if (!body.ok) {
- _this.error = body.error;
- _this.connected = false;
- console.error("Slack api responded !ok with ", body);
- _this.lastCb(_this);
- return;
- }
- _this.getEmojis((emojis) => {
- body.emojis = emojis;
- _this.data.updateStatic(body);
- _this.connectRtm(body.url);
- _this.waitForEvent(knownVersion);
- });
- });
- };
- Slack.prototype.getEmojis = function(cb) {
- httpsRequest(SLACK_ENDPOINT +GETAPI.emojiList +"?token=" +this.token, (status, body) => {
- if (!status || !body || !body.ok)
- cb(null);
- else
- cb(body.emoji || {});
- });
- };
- Slack.prototype.waitForEvent = function(knownVersion) {
- var tick = 0
- ,_this = this
- ,interval;
- interval = setInterval(() => {
- tick++;
- if (!_this.lastCb) {
- clearInterval(interval);
- return;
- }
- if (_this.connected) {
- var updatedCtx = _this.data.getUpdates(knownVersion)
- ,updatedLive = _this.getLiveUpdates(knownVersion);
- if (updatedCtx || updatedLive) {
- var updated = {
- "static": updatedCtx
- ,"live": updatedLive
- ,"v": Math.max(_this.data.liveV, _this.data.staticV)
- // TODO "typing" stream
- };
- clearInterval(interval);
- _this.lastCb(_this, updated);
- return;
- }
- }
- if (tick >= 55) { // < 1 minute timeout
- clearInterval(interval);
- _this.lastCb(_this, { v: knownVersion });
- return;
- }
- }, 1000);
- };
- /** @return {Object|undefined} */
- Slack.prototype.getLiveUpdates = function(knownVersion) {
- var result = {};
- for (var roomId in this.history) {
- var history = this.history[roomId];
- if (history.isNew) {
- result[roomId] = history.toStatic(0);
- history.isNew = false;
- }
- else {
- var roomData = history.toStatic(knownVersion);
- if (roomData.length)
- result[roomId] = roomData;
- }
- }
- for (var roomId in result) {
- return result;
- }
- return undefined;
- };
- Slack.prototype.onMessage = function(msg) {
- this.data.onMessage(msg);
- if ((msg["channel"] || msg["channel_id"] || (msg["item"] && msg["item"]["channel"])) && msg["type"] && UPDATE_LIVE.indexOf(msg["type"]) !== -1) {
- var channelId = msg["channel"] || msg["channel_id"] || msg["item"]["channel"]
- ,histo = this.history[channelId];
- if (histo) {
- this.data.liveV = Math.max(this.data.liveV, histo.push(msg));
- histo.resort();
- } else if (this.data.getChannel(msg["channel"])) {
- this.fetchHistory(msg["channel"]);
- }
- }
- };
- Slack.prototype.connectRtm = function(url, cb) {
- var _this = this;
- this.rtm = new WebSocket(url);
- this.rtm.on("message", function(msg) {
- if (!_this.connected && cb) {
- cb();
- }
- _this.connected = true;
- _this.onMessage(JSON.parse(msg));
- SLACK_SESSIONS.push(_this);
- });
- this.rtm.once("error", function(e) {
- _this.connected = false;
- console.error(e);
- _this.close();
- });
- this.rtm.once("end", function() {
- _this.connected = false;
- console.error("RTM hang up");
- _this.close();
- });
- };
- Slack.prototype.closeIfInnactive = function() {
- //TODO
- };
- Slack.prototype.close = function() {
- };
- Slack.getOauthToken = function(code, redirectUri, cb) {
- httpsRequest(SLACK_ENDPOINT+GETAPI.oauth
- +"?client_id=" +config.clientId
- +"&client_secret=" +config.clientSecret
- +"&redirect_uri=" +redirectUri
- +"&code=" +code,
- (status, resp) => {
- if (status === 200 && resp.ok) {
- cb(resp);
- } else {
- cb(null);
- }
- });
- };
- /**
- * @param {SlackChan|SlackGroup|SlackIms} channel
- * @param {string} contentType
- * @param {function(string|null)} callback
- **/
- Slack.prototype.openUploadFileStream = function(channel, contentType, callback) {
- var req = https.request({
- hostname: SLACK_HOSTNAME
- ,method: 'POST'
- ,path: SLACK_ENDPOINT_PATH +GETAPI.postFile
- +"?token=" +this.token
- +"&channels=" +channel.id
- ,headers: {
- "Content-Type": contentType
- }
- }, (res) => {
- var errorJson;
- res.on("data", (chunk) => {
- errorJson = errorJson ? Buffer.concat([errorJson, chunk], errorJson.length +chunk.length) : Buffer.from(chunk);
- });
- res.once("end", () => {
- if (res.statusCode === 200) {
- callback(null);
- } else {
- try {
- errorJson = JSON.parse(errorJson.toString());
- } catch(e) {
- callback("error");
- return;
- }
- callback(errorJson["error"] || "error");
- }
- });
- });
- return req;
- };
- /**
- * @param {SlackChan|SlackGroup|SlackIms} channel
- * @param {string} msgId
- * @param {string} reaction
- **/
- Slack.prototype.addReaction = function(channel, msgId, reaction) {
- httpsRequest(SLACK_ENDPOINT +GETAPI.addReaction
- +"?token=" +this.token
- +"&name=" +reaction
- +"&channel="+channel.id
- +"×tamp="+msgId);
- }
- /**
- * @param {SlackChan|SlackGroup|SlackIms} channel
- * @param {string} msgId
- * @param {string} reaction
- **/
- Slack.prototype.removeReaction = function(channel, msgId, reaction) {
- httpsRequest(SLACK_ENDPOINT +GETAPI.removeReaction
- +"?token=" +this.token
- +"&name=" +reaction
- +"&channel="+channel.id
- +"×tamp="+msgId);
- }
- /**
- * @param {SlackChan|SlackGroup|SlackIms} channel
- * @param {Array.<string>} text
- * @param {Array.<Object>=} attachments
- **/
- Slack.prototype.sendMsg = function(channel, text, attachments) {
- httpsRequest(SLACK_ENDPOINT +GETAPI.postMsg
- +"?token=" +this.token
- +"&channel=" +channel.id
- +"&text=" +text.join("\n")
- + (attachments ? ("&attachments=" +encodeURIComponent(JSON.stringify(attachments))) : "")
- +"&as_user=true");
- };
- /**
- * @param {SlackChan|SlackGroup|SlackIms} channel
- * @param {string} msgId
- **/
- Slack.prototype.removeMsg = function(channel, msgId) {
- httpsRequest(SLACK_ENDPOINT +GETAPI.removeMsg
- +"?token=" +this.token
- +"&channel=" +channel.id
- +"&ts=" +msgId
- +"&as_user=true");
- };
- Slack.prototype.fetchHistory = function(targetId) {
- var _this = this
- ,baseUrl = "";
- if (targetId[0] === 'D') {
- baseUrl = SLACK_ENDPOINT +GETAPI.directHistory;
- } else if (targetId[0] === 'C') {
- baseUrl = SLACK_ENDPOINT +GETAPI.channelHistory;
- } else if (targetId[0] === 'G') {
- baseUrl = SLACK_ENDPOINT +GETAPI.groupHistory;
- }
- httpsRequest(baseUrl
- +"?token="+this.token
- +"&channel=" +targetId
- +"&count=" +HISTORY_LENGTH,
- (status, resp) => {
- if (status === 200 && resp && resp.ok) {
- var history = _this.history[targetId];
- if (!history) {
- history = _this.history[targetId] = new SlackHistory(targetId, HISTORY_LENGTH);
- history.isNew = true;
- }
- this.data.liveV = Math.max(history.pushAll(resp.messages), this.data.liveV);
- }
- });
- };
- module.exports.Slack = Slack;
|