From c09c957093e6f9875aa781efcd1a26277903704a Mon Sep 17 00:00:00 2001 From: Martin Schoeler Date: Thu, 18 May 2017 16:15:39 -0300 Subject: [PATCH 1/8] convert irc-to-js --- packages/rocketchat-irc/server/server.js | 455 +++++++++++++++++++++++ 1 file changed, 455 insertions(+) create mode 100644 packages/rocketchat-irc/server/server.js diff --git a/packages/rocketchat-irc/server/server.js b/packages/rocketchat-irc/server/server.js new file mode 100644 index 000000000000..6e085df64956 --- /dev/null +++ b/packages/rocketchat-irc/server/server.js @@ -0,0 +1,455 @@ +////// +// Assign values + +//Package availability +const IRC_AVAILABILITY = RocketChat.settings.get('IRC_Enabled'); + +// Cache prep +const net = Npm.require('net'); +const Lru = Npm.require('lru-cache'); +const MESSAGE_CACHE_SIZE = RocketChat.settings.get('IRC_Message_Cache_Size'); +const ircReceiveMessageCache = Lru(MESSAGE_CACHE_SIZE);//eslint-disable-line +const ircSendMessageCache = Lru(MESSAGE_CACHE_SIZE);//eslint-disable-line + +// IRC server +const IRC_PORT = RocketChat.settings.get('IRC_Port'); +const IRC_HOST = RocketChat.settings.get('IRC_Host'); + +const ircClientMap = {}; + +////// +// Core functionality + +const bind = function(f) { + const g = Meteor.bindEnvironment((self, ...args) => f.apply(self, args)); + return function(...args) { g(this, ...args); }; +}; + +const async = (f, ...args) => Meteor.wrapAsync(f)(...args); + +class IrcClient { + constructor(loginReq) { + this.loginReq = loginReq; + + this.user = this.loginReq.user; + ircClientMap[this.user._id] = this; + this.ircPort = IRC_PORT; + this.ircHost = IRC_HOST; + this.msgBuf = []; + + this.isConnected = false; + this.isDistroyed = false; + this.socket = new net.Socket; + this.socket.setNoDelay; + this.socket.setEncoding('utf-8'); + this.socket.setKeepAlive(true); + this.onConnect = bind(this.onConnect); + this.onClose = bind(this.onClose); + this.onTimeout = bind(this.onTimeout); + this.onError = bind(this.onError); + this.onReceiveRawMessage = bind(this.onReceiveRawMessage); + this.socket.on('data', this.onReceiveRawMessage); + this.socket.on('close', this.onClose); + this.socket.on('timeout', this.onTimeout); + this.socket.on('error', this.onError); + + this.isJoiningRoom = false; + this.receiveMemberListBuf = {}; + this.pendingJoinRoomBuf = []; + + this.successLoginMessageRegex = /RocketChat.settings.get('IRC_RegEx_successLogin');/; + this.failedLoginMessageRegex = /RocketChat.settings.get('IRC_RegEx_failedLogin');/; + this.receiveMessageRegex = /RocketChat.settings.get('IRC_RegEx_receiveMessage');/; + this.receiveMemberListRegex = /RocketChat.settings.get('IRC_RegEx_receiveMemberList');/; + this.endMemberListRegex = /RocketChat.settings.get('IRC_RegEx_endMemberList');/; + this.addMemberToRoomRegex = /RocketChat.settings.get('IRC_RegEx_addMemberToRoom');/; + this.removeMemberFromRoomRegex = /RocketChat.settings.get('IRC_RegEx_removeMemberFromRoom');/; + this.quitMemberRegex = /RocketChat.settings.get('IRC_RegEx_quitMember');/; + } + + connect(loginCb) { + this.loginCb = loginCb; + this.socket.connect(this.ircPort, this.ircHost, this.onConnect); + this.initRoomList(); + } + + disconnect() { + this.isDistroyed = true; + this.socket.destroy(); + } + + onConnect() { + console.log('[irc] onConnect -> '.yellow, this.user.username, 'connect success.'); + this.socket.write(`NICK ${ this.user.username }\r\n`); + this.socket.write(`USER ${ this.user.username } 0 * :${ this.user.name }\r\n`); + // message order could not make sure here + this.isConnected = true; + const messageBuf = this.msgBuf; + messageBuf.forEach(msg => { + this.socket.write(msg); + }); + } + + onClose() { + console.log('[irc] onClose -> '.yellow, this.user.username, 'connection close.'); + this.isConnected = false; + if (this.isDistroyed) { + delete ircClientMap[this.user._id]; + } else { + this.connect(); + } + } + + onTimeout() { + console.log('[irc] onTimeout -> '.yellow, this.user.username, 'connection timeout.', arguments); + } + + onError() { + console.log('[irc] onError -> '.yellow, this.user.username, 'connection error.', arguments); + } + + onReceiveRawMessage(data) { + data = data.toString().split('\n'); + + data.forEach(line => { + line = line.trim(); + console.log(`[${ this.ircHost }:${ this.ircPort }]:`, line); + + // Send heartbeat package to irc server + if (line.indexOf('PING') === 0) { + this.socket.write(line.replace('PING :', 'PONG ')); + return; + } + let matchResult = this.receiveMessageRegex.exec(line); + if (matchResult) { + this.onReceiveMessage(matchResult[1], matchResult[2], matchResult[3]); + return; + } + matchResult = this.receiveMemberListRegex.exec(line); + if (matchResult) { + this.onReceiveMemberList(matchResult[1], matchResult[2].split(' ')); + return; + } + matchResult = this.endMemberListRegex.exec(line); + if (matchResult) { + this.onEndMemberList(matchResult[1]); + return; + } + matchResult = this.addMemberToRoomRegex.exec(line); + if (matchResult) { + this.onAddMemberToRoom(matchResult[1], matchResult[2]); + return; + } + matchResult = this.removeMemberFromRoomRegex.exec(line); + if (matchResult) { + this.onRemoveMemberFromRoom(matchResult[1], matchResult[2]); + return; + } + matchResult = this.quitMemberRegex.exec(line); + if (matchResult) { + this.onQuitMember(matchResult[1]); + return; + } + matchResult = this.successLoginMessageRegex.exec(line); + if (matchResult) { + this.onSuccessLoginMessage(); + return; + } + matchResult = this.failedLoginMessageRegex.exec(line); + if (matchResult) { + this.onFailedLoginMessage(); + return; + } + }); + } + + onSuccessLoginMessage() { + console.log('[irc] onSuccessLoginMessage -> '.yellow); + if (this.loginCb) { + this.loginCb(null, this.loginReq); + } + } + + onFailedLoginMessage() { + console.log('[irc] onFailedLoginMessage -> '.yellow); + this.loginReq.allowed = false; + this.disconnect(); + if (this.loginCb) { + this.loginCb(null, this.loginReq); + } + } + + onReceiveMessage(source, target, content) { + let room; + const now = new Date; + const timestamp = now.getTime(); + let cacheKey = [source, target, content].join(','); + console.log('[irc] ircSendMessageCache.get -> '.yellow, 'key:', cacheKey, 'value:', ircSendMessageCache.get(cacheKey), 'ts:', timestamp - 1000); + if (ircSendMessageCache.get(cacheKey) > (timestamp - 1000)) { + return; + } else { + ircSendMessageCache.set(cacheKey, timestamp); + } + console.log('[irc] onReceiveMessage -> '.yellow, 'source:', source, 'target:', target, 'content:', content); + source = this.createUserWhenNotExist(source); + if (target[0] === '#') { + room = RocketChat.models.Rooms.findOneByName(target.substring(1)); + } else { + room = this.createDirectRoomWhenNotExist(source, this.user); + } + const message = { + msg: content, + ts: now + }; + cacheKey = `${ source.username }${ timestamp }`; + ircReceiveMessageCache.set(cacheKey, true); + console.log('[irc] ircReceiveMessageCache.set -> '.yellow, 'key:', cacheKey); + RocketChat.sendMessage(source, message, room); + } + + onReceiveMemberList(roomName, members) { + this.receiveMemberListBuf[roomName] = this.receiveMemberListBuf[roomName].concat(members); + } + + onEndMemberList(roomName) { + const newMembers = this.receiveMemberListBuf[roomName]; + console.log('[irc] onEndMemberList -> '.yellow, 'room:', roomName, 'members:', newMembers.join(',')); + const room = RocketChat.models.Rooms.findOneByNameAndType(roomName, 'c'); + if (!room) { + return; + } + const oldMembers = room.usernames; + const appendMembers = _.difference(newMembers, oldMembers); + const removeMembers = _.difference(oldMembers, newMembers); + appendMembers.forEach(member => this.createUserWhenNotExist(member)); + + RocketChat.models.Rooms.removeUsernamesById(room._id, removeMembers); + RocketChat.models.Rooms.addUsernamesById(room._id, appendMembers); + + this.isJoiningRoom = false; + roomName = this.pendingJoinRoomBuf.shift(); + if (roomName) { + this.joinRoom({ + t: 'c', + name: roomName + }); + } + } + + sendRawMessage(msg) { + console.log('[irc] sendRawMessage -> '.yellow, msg.slice(0, -2)); + if (this.isConnected) { + this.socket.write(msg); + } else { + this.msgBuf.push(msg); + } + } + + sendMessage(room, message) { + console.log('[irc] sendMessage -> '.yellow, 'userName:', message.u.username); + let target = ''; + if (room.t === 'c') { + target = `#${ room.name }`; + } else if (room.t === 'd') { + const usernames = room.usernames; + usernames.forEach(name => { + if (message.u.username !== name) { + target = name; + return; + } + }); + } + const cacheKey = [this.user.username, target, message.msg].join(','); + console.log('[irc] ircSendMessageCache.set -> '.yellow, 'key:', cacheKey, 'ts:', message.ts.getTime()); + ircSendMessageCache.set(cacheKey, message.ts.getTime()); + const msg = `PRIVMSG ${ target } :${ message.msg }\r\n`; + this.sendRawMessage(msg); + } + + initRoomList() { + const roomsCursor = RocketChat.models.Rooms.findByTypeContainingUsername('c', this.user.username, { fields: { name: 1, t: 1 }}); + const rooms = roomsCursor.fetch(); + + rooms.forEach(room => { + this.joinRoom(room); + }); + } + + joinRoom(room) { + let msg; + if (room.t !== 'c' || room.name === 'general') { + return; + } + if (this.isJoiningRoom) { + this.pendingJoinRoomBuf.push(room.name); + } else { + console.log('[irc] joinRoom -> '.yellow, 'roomName:', room.name, 'pendingJoinRoomBuf:', this.pendingJoinRoomBuf.join(',')); + msg = `JOIN #${ room.name }\r\n`; + this.receiveMemberListBuf[room.name] = []; + this.sendRawMessage(msg); + this.isJoiningRoom = true; + } + } + + leaveRoom(room) { + if (room.t !== 'c') { + return; + } + const msg = `PART #${ room.name }\r\n`; + this.sendRawMessage(msg); + } + + getMemberList(room) { + if (room.t !== 'c') { + return; + } + const msg = `NAMES #${ room.name }\r\n`; + this.receiveMemberListBuf[room.name] = []; + this.sendRawMessage(msg); + } + + onAddMemberToRoom(member, roomName) { + if (this.user.username === member) { + return; + } + console.log('[irc] onAddMemberToRoom -> '.yellow, 'roomName:', roomName, 'member:', member); + this.createUserWhenNotExist(member); + RocketChat.models.Rooms.addUsernameByName(roomName, member); + } + + onRemoveMemberFromRoom(member, roomName) { + console.log('[irc] onRemoveMemberFromRoom -> '.yellow, 'roomName:', roomName, 'member:', member); + RocketChat.models.Rooms.removeUsernameByName(roomName, member); + } + + onQuitMember(member) { + console.log('[irc] onQuitMember ->'.yellow, 'username:', member); + RocketChat.models.Rooms.removeUsernameFromAll(member); + Meteor.users.update({ name: member }, { $set: { status: 'offline' }}); + } + + createUserWhenNotExist(name) { + let user; + user = Meteor.users.findOne({ name }); + if (!user) { + console.log('[irc] createNotExistUser ->'.yellow, 'userName:', name); + Meteor.call('registerUser', { + email: `${ name }@rocketchat.org`, + pass: 'rocketchat', + name + }); + Meteor.users.update({ name }, { + $set: { + status: 'online', + username: name + } + }); + user = Meteor.users.findOne({ name }); + } + return user; + } + + createDirectRoomWhenNotExist(source, target) { + console.log('[irc] createDirectRoomWhenNotExist -> '.yellow, 'source:', source, 'target:', target); + const rid = [source._id, target._id].sort().join(''); + const now = new Date(); + RocketChat.models.Rooms.upsert({ _id: rid}, { + $set: { + usernames: [source.username, target.username] + }, + $setOnInsert: { + t: 'd', + msgs: 0, + ts: now + } + }); + RocketChat.models.Subscriptions.upsert({ rid, $and: [{ 'u._id': target._id}]}, { + $setOnInsert: { + name: source.username, + t: 'd', + open: false, + alert: false, + unread: 0, + u: { _id: target._id, username: target.username }} + }); + return { t: 'd', _id: rid }; + } +} + +IrcClient.getByUid = function(uid) { + return ircClientMap[uid]; +}; + +IrcClient.create = function(login) { + let ircClient; + if (login.user == null) { + return login; + } + if (!(login.user._id in ircClientMap)) { + ircClient = new IrcClient(login); + return async(ircClient.connect); + } + return login; +}; + +class IrcLoginer { + constructor(login) { + console.log('[irc] validateLogin -> '.yellow, login); + return IrcClient.create(login); + } +} + +class IrcSender { + constructor(message) { + const name = message.u.username; + const timestamp = message.ts.getTime(); + const cacheKey = `${ name }${ timestamp }`; + if (ircReceiveMessageCache.get(cacheKey)) { + return message; + } + const room = RocketChat.models.Rooms.findOneById(message.rid, { fields: { name: 1, usernames: 1, t: 1 }}); + const ircClient = IrcClient.getByUid(message.u._id); + ircClient.sendMessage(room, message); + return message; + } + +} + +class IrcRoomJoiner { + constructor(user, room) { + const ircClient = IrcClient.getByUid(user._id); + ircClient.joinRoom(room); + return room; + } + +} + +class IrcRoomLeaver { + constructor(user, room) { + const ircClient = IrcClient.getByUid(user._id); + ircClient.leaveRoom(room); + return room; + } + +} + +class IrcLogoutCleanUper { + constructor(user) { + const ircClient = IrcClient.getByUid(user._id); + ircClient.disconnect(); + return user; + } +} + +////// +// Make magic happen + +// Only proceed if the package has been enabled +if (IRC_AVAILABILITY === true) { + RocketChat.callbacks.add('beforeValidateLogin', IrcLoginer, RocketChat.callbacks.priority.LOW, 'irc-loginer'); + RocketChat.callbacks.add('beforeSaveMessage', IrcSender, RocketChat.callbacks.priority.LOW, 'irc-sender'); + RocketChat.callbacks.add('beforeJoinRoom', IrcRoomJoiner, RocketChat.callbacks.priority.LOW, 'irc-room-joiner'); + RocketChat.callbacks.add('beforeCreateChannel', IrcRoomJoiner, RocketChat.callbacks.priority.LOW, 'irc-room-joiner-create-channel'); + RocketChat.callbacks.add('beforeLeaveRoom', IrcRoomLeaver, RocketChat.callbacks.priority.LOW, 'irc-room-leaver'); + RocketChat.callbacks.add('afterLogoutCleanUp', IrcLogoutCleanUper, RocketChat.callbacks.priority.LOW, 'irc-clean-up'); +} From aeb73e2dc234429600fab461ea94fb5757852bd7 Mon Sep 17 00:00:00 2001 From: Martin Schoeler Date: Wed, 24 May 2017 11:28:54 -0300 Subject: [PATCH 2/8] fix review --- packages/rocketchat-irc/server/server.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/rocketchat-irc/server/server.js b/packages/rocketchat-irc/server/server.js index 6e085df64956..5b32aad87831 100644 --- a/packages/rocketchat-irc/server/server.js +++ b/packages/rocketchat-irc/server/server.js @@ -85,9 +85,7 @@ class IrcClient { // message order could not make sure here this.isConnected = true; const messageBuf = this.msgBuf; - messageBuf.forEach(msg => { - this.socket.write(msg); - }); + messageBuf.forEach(msg => this.socket.write(msg)); } onClose() { @@ -329,8 +327,7 @@ class IrcClient { } createUserWhenNotExist(name) { - let user; - user = Meteor.users.findOne({ name }); + let user = Meteor.users.findOne({ name }); if (!user) { console.log('[irc] createNotExistUser ->'.yellow, 'userName:', name); Meteor.call('registerUser', { @@ -381,12 +378,11 @@ IrcClient.getByUid = function(uid) { }; IrcClient.create = function(login) { - let ircClient; if (login.user == null) { return login; } if (!(login.user._id in ircClientMap)) { - ircClient = new IrcClient(login); + const ircClient = new IrcClient(login); return async(ircClient.connect); } return login; From 91f51414bed8425cbfbe01ac581c7d54fc29d6da Mon Sep 17 00:00:00 2001 From: Martin Schoeler Date: Thu, 25 May 2017 10:39:25 -0300 Subject: [PATCH 3/8] use imports --- packages/rocketchat-irc/server/server.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/rocketchat-irc/server/server.js b/packages/rocketchat-irc/server/server.js index 5b32aad87831..c6f356d92ef3 100644 --- a/packages/rocketchat-irc/server/server.js +++ b/packages/rocketchat-irc/server/server.js @@ -1,3 +1,6 @@ +import net from 'net'; +import Lru from 'lru-cache'; + ////// // Assign values @@ -5,8 +8,6 @@ const IRC_AVAILABILITY = RocketChat.settings.get('IRC_Enabled'); // Cache prep -const net = Npm.require('net'); -const Lru = Npm.require('lru-cache'); const MESSAGE_CACHE_SIZE = RocketChat.settings.get('IRC_Message_Cache_Size'); const ircReceiveMessageCache = Lru(MESSAGE_CACHE_SIZE);//eslint-disable-line const ircSendMessageCache = Lru(MESSAGE_CACHE_SIZE);//eslint-disable-line From 8c99ba4c8f54d1b7cb2500f40a57c24dae11ffcb Mon Sep 17 00:00:00 2001 From: Martin Schoeler Date: Mon, 29 May 2017 16:17:42 -0300 Subject: [PATCH 4/8] fix reviews --- packages/rocketchat-irc/server/server.js | 60 ++++++++++-------------- 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/packages/rocketchat-irc/server/server.js b/packages/rocketchat-irc/server/server.js index c6f356d92ef3..316747d1b972 100644 --- a/packages/rocketchat-irc/server/server.js +++ b/packages/rocketchat-irc/server/server.js @@ -389,53 +389,43 @@ IrcClient.create = function(login) { return login; }; -class IrcLoginer { - constructor(login) { - console.log('[irc] validateLogin -> '.yellow, login); - return IrcClient.create(login); - } +function IrcLoginer(login) { + console.log('[irc] validateLogin -> '.yellow, login); + return IrcClient.create(login); } -class IrcSender { - constructor(message) { - const name = message.u.username; - const timestamp = message.ts.getTime(); - const cacheKey = `${ name }${ timestamp }`; - if (ircReceiveMessageCache.get(cacheKey)) { - return message; - } - const room = RocketChat.models.Rooms.findOneById(message.rid, { fields: { name: 1, usernames: 1, t: 1 }}); - const ircClient = IrcClient.getByUid(message.u._id); - ircClient.sendMessage(room, message); + +function IrcSender(message) { + const name = message.u.username; + const timestamp = message.ts.getTime(); + const cacheKey = `${ name }${ timestamp }`; + if (ircReceiveMessageCache.get(cacheKey)) { return message; } - + const room = RocketChat.models.Rooms.findOneById(message.rid, { fields: { name: 1, usernames: 1, t: 1 }}); + const ircClient = IrcClient.getByUid(message.u._id); + ircClient.sendMessage(room, message); + return message; } -class IrcRoomJoiner { - constructor(user, room) { - const ircClient = IrcClient.getByUid(user._id); - ircClient.joinRoom(room); - return room; - } +function IrcRoomJoiner(user, room) { + const ircClient = IrcClient.getByUid(user._id); + ircClient.joinRoom(room); + return room; } -class IrcRoomLeaver { - constructor(user, room) { - const ircClient = IrcClient.getByUid(user._id); - ircClient.leaveRoom(room); - return room; - } +function IrcRoomLeaver(user, room) { + const ircClient = IrcClient.getByUid(user._id); + ircClient.leaveRoom(room); + return room; } -class IrcLogoutCleanUper { - constructor(user) { - const ircClient = IrcClient.getByUid(user._id); - ircClient.disconnect(); - return user; - } +function IrcLogoutCleanUper(user) { + const ircClient = IrcClient.getByUid(user._id); + ircClient.disconnect(); + return user; } ////// From 356d3f11c90b5589ff6d09a3266229f948077797 Mon Sep 17 00:00:00 2001 From: Martin Schoeler Date: Wed, 31 May 2017 10:37:06 -0300 Subject: [PATCH 5/8] remove coffee files --- packages/rocketchat-irc/package.js | 3 +- packages/rocketchat-irc/server/server.coffee | 411 ------------------- 2 files changed, 1 insertion(+), 413 deletions(-) delete mode 100644 packages/rocketchat-irc/server/server.coffee diff --git a/packages/rocketchat-irc/package.js b/packages/rocketchat-irc/package.js index 311343292f8f..d1192c74501e 100644 --- a/packages/rocketchat-irc/package.js +++ b/packages/rocketchat-irc/package.js @@ -13,14 +13,13 @@ Npm.depends({ Package.onUse(function(api) { api.use([ 'ecmascript', - 'coffeescript', 'underscore', 'rocketchat:lib' ]); api.addFiles([ 'server/settings.js', - 'server/server.coffee' + 'server/server.js' ], 'server'); api.export(['Irc'], ['server']); diff --git a/packages/rocketchat-irc/server/server.coffee b/packages/rocketchat-irc/server/server.coffee deleted file mode 100644 index 4f753e1683eb..000000000000 --- a/packages/rocketchat-irc/server/server.coffee +++ /dev/null @@ -1,411 +0,0 @@ -# # # -# Assign values -# - -# Package availability -IRC_AVAILABILITY = RocketChat.settings.get('IRC_Enabled'); - -# Cache prep -net = Npm.require('net') -Lru = Npm.require('lru-cache') -MESSAGE_CACHE_SIZE = RocketChat.settings.get('IRC_Message_Cache_Size'); -ircReceiveMessageCache = Lru MESSAGE_CACHE_SIZE -ircSendMessageCache = Lru MESSAGE_CACHE_SIZE - -# IRC server -IRC_PORT = RocketChat.settings.get('IRC_Port'); -IRC_HOST = RocketChat.settings.get('IRC_Host'); - -ircClientMap = {} - - -# # # -# Core functionality -# - -bind = (f) -> - g = Meteor.bindEnvironment (self, args...) -> f.apply(self, args) - (args...) -> g @, args... - -async = (f, args...) -> - Meteor.wrapAsync(f)(args...) - -class IrcClient - constructor: (@loginReq) -> - @user = @loginReq.user - ircClientMap[@user._id] = this - @ircPort = IRC_PORT - @ircHost = IRC_HOST - @msgBuf = [] - - @isConnected = false - @isDistroyed = false - @socket = new net.Socket - @socket.setNoDelay - @socket.setEncoding 'utf-8' - @socket.setKeepAlive true - @onConnect = bind @onConnect - @onClose = bind @onClose - @onTimeout = bind @onTimeout - @onError = bind @onError - @onReceiveRawMessage = bind @onReceiveRawMessage - @socket.on 'data', @onReceiveRawMessage - @socket.on 'close', @onClose - @socket.on 'timeout', @onTimeout - @socket.on 'error', @onError - - @isJoiningRoom = false - @receiveMemberListBuf = {} - @pendingJoinRoomBuf = [] - - @successLoginMessageRegex = /RocketChat.settings.get('IRC_RegEx_successLogin');/ - @failedLoginMessageRegex = /RocketChat.settings.get('IRC_RegEx_failedLogin');/ - @receiveMessageRegex = /RocketChat.settings.get('IRC_RegEx_receiveMessage');/ - @receiveMemberListRegex = /RocketChat.settings.get('IRC_RegEx_receiveMemberList');/ - @endMemberListRegex = /RocketChat.settings.get('IRC_RegEx_endMemberList');/ - @addMemberToRoomRegex = /RocketChat.settings.get('IRC_RegEx_addMemberToRoom');/ - @removeMemberFromRoomRegex = /RocketChat.settings.get('IRC_RegEx_removeMemberFromRoom');/ - @quitMemberRegex = /RocketChat.settings.get('IRC_RegEx_quitMember');/ - - connect: (@loginCb) => - @socket.connect @ircPort, @ircHost, @onConnect - @initRoomList() - - disconnect: () -> - @isDistroyed = true - @socket.destroy() - - onConnect: () => - console.log '[irc] onConnect -> '.yellow, @user.username, 'connect success.' - @socket.write "NICK #{@user.username}\r\n" - @socket.write "USER #{@user.username} 0 * :#{@user.name}\r\n" - # message order could not make sure here - @isConnected = true - @socket.write msg for msg in @msgBuf - - onClose: (data) => - console.log '[irc] onClose -> '.yellow, @user.username, 'connection close.' - @isConnected = false - if @isDistroyed - delete ircClientMap[@user._id] - else - @connect() - - onTimeout: () => - console.log '[irc] onTimeout -> '.yellow, @user.username, 'connection timeout.', arguments - - onError: () => - console.log '[irc] onError -> '.yellow, @user.username, 'connection error.', arguments - - onReceiveRawMessage: (data) => - data = data.toString().split('\n') - for line in data - line = line.trim() - console.log "[#{@ircHost}:#{@ircPort}]:", line - # Send heartbeat package to irc server - if line.indexOf('PING') == 0 - @socket.write line.replace('PING :', 'PONG ') - continue - - matchResult = @receiveMessageRegex.exec line - if matchResult - @onReceiveMessage matchResult[1], matchResult[2], matchResult[3] - continue - - matchResult = @receiveMemberListRegex.exec line - if matchResult - @onReceiveMemberList matchResult[1], matchResult[2].split ' ' - continue - - matchResult = @endMemberListRegex.exec line - if matchResult - @onEndMemberList matchResult[1] - continue - - matchResult = @addMemberToRoomRegex.exec line - if matchResult - @onAddMemberToRoom matchResult[1], matchResult[2] - continue - - matchResult = @removeMemberFromRoomRegex.exec line - if matchResult - @onRemoveMemberFromRoom matchResult[1], matchResult[2] - continue - - matchResult = @quitMemberRegex.exec line - if matchResult - @onQuitMember matchResult[1] - continue - - matchResult = @successLoginMessageRegex.exec line - if matchResult - @onSuccessLoginMessage() - continue - - matchResult = @failedLoginMessageRegex.exec line - if matchResult - @onFailedLoginMessage() - continue - - onSuccessLoginMessage: () -> - console.log '[irc] onSuccessLoginMessage -> '.yellow - if @loginCb - @loginCb null, @loginReq - - onFailedLoginMessage: () -> - console.log '[irc] onFailedLoginMessage -> '.yellow - @loginReq.allowed = false - @disconnect() - if @loginCb - @loginCb null, @loginReq - - onReceiveMessage: (source, target, content) -> - now = new Date - timestamp = now.getTime() - - cacheKey = [source, target, content].join ',' - console.log '[irc] ircSendMessageCache.get -> '.yellow, 'key:', cacheKey, 'value:', ircSendMessageCache.get(cacheKey), 'ts:', (timestamp - 1000) - if ircSendMessageCache.get(cacheKey) > (timestamp - 1000) - return - else - ircSendMessageCache.set cacheKey, timestamp - - console.log '[irc] onReceiveMessage -> '.yellow, 'source:', source, 'target:', target, 'content:', content - source = @createUserWhenNotExist source - if target[0] == '#' - room = RocketChat.models.Rooms.findOneByName target.substring(1) - else - room = @createDirectRoomWhenNotExist(source, @user) - - message = - msg: content - ts: now - cacheKey = "#{source.username}#{timestamp}" - ircReceiveMessageCache.set cacheKey, true - console.log '[irc] ircReceiveMessageCache.set -> '.yellow, 'key:', cacheKey - RocketChat.sendMessage source, message, room - - onReceiveMemberList: (roomName, members) -> - @receiveMemberListBuf[roomName] = @receiveMemberListBuf[roomName].concat members - - onEndMemberList: (roomName) -> - newMembers = @receiveMemberListBuf[roomName] - console.log '[irc] onEndMemberList -> '.yellow, 'room:', roomName, 'members:', newMembers.join ',' - room = RocketChat.models.Rooms.findOneByNameAndType roomName, 'c' - unless room - return - - oldMembers = room.usernames - appendMembers = _.difference newMembers, oldMembers - removeMembers = _.difference oldMembers, newMembers - - for member in appendMembers - @createUserWhenNotExist member - - RocketChat.models.Rooms.removeUsernamesById room._id, removeMembers - RocketChat.models.Rooms.addUsernamesById room._id, appendMembers - - @isJoiningRoom = false - roomName = @pendingJoinRoomBuf.shift() - if roomName - @joinRoom - t: 'c' - name: roomName - - sendRawMessage: (msg) -> - console.log '[irc] sendRawMessage -> '.yellow, msg.slice(0, -2) - if @isConnected - @socket.write msg - else - @msgBuf.push msg - - sendMessage: (room, message) -> - console.log '[irc] sendMessage -> '.yellow, 'userName:', message.u.username - target = '' - if room.t == 'c' - target = "##{room.name}" - else if room.t == 'd' - for name in room.usernames - if message.u.username != name - target = name - break - - cacheKey = [@user.username, target, message.msg].join ',' - console.log '[irc] ircSendMessageCache.set -> '.yellow, 'key:', cacheKey, 'ts:', message.ts.getTime() - ircSendMessageCache.set cacheKey, message.ts.getTime() - msg = "PRIVMSG #{target} :#{message.msg}\r\n" - @sendRawMessage msg - - initRoomList: -> - roomsCursor = RocketChat.models.Rooms.findByTypeContainingUsername 'c', @user.username, - fields: - name: 1 - t: 1 - - rooms = roomsCursor.fetch() - for room in rooms - @joinRoom(room) - - joinRoom: (room) -> - if room.t isnt 'c' or room.name == 'general' - return - - if @isJoiningRoom - @pendingJoinRoomBuf.push room.name - else - console.log '[irc] joinRoom -> '.yellow, 'roomName:', room.name, 'pendingJoinRoomBuf:', @pendingJoinRoomBuf.join ',' - msg = "JOIN ##{room.name}\r\n" - @receiveMemberListBuf[room.name] = [] - @sendRawMessage msg - @isJoiningRoom = true - - leaveRoom: (room) -> - if room.t isnt 'c' - return - msg = "PART ##{room.name}\r\n" - @sendRawMessage msg - - getMemberList: (room) -> - if room.t isnt 'c' - return - msg = "NAMES ##{room.name}\r\n" - @receiveMemberListBuf[room.name] = [] - @sendRawMessage msg - - onAddMemberToRoom: (member, roomName) -> - if @user.username == member - return - - console.log '[irc] onAddMemberToRoom -> '.yellow, 'roomName:', roomName, 'member:', member - @createUserWhenNotExist member - - RocketChat.models.Rooms.addUsernameByName roomName, member - - onRemoveMemberFromRoom: (member, roomName)-> - console.log '[irc] onRemoveMemberFromRoom -> '.yellow, 'roomName:', roomName, 'member:', member - RocketChat.models.Rooms.removeUsernameByName roomName, member - - onQuitMember: (member) -> - console.log '[irc] onQuitMember ->'.yellow, 'username:', member - RocketChat.models.Rooms.removeUsernameFromAll member - - Meteor.users.update {name: member}, - $set: - status: 'offline' - - createUserWhenNotExist: (name) -> - user = Meteor.users.findOne {name: name} - unless user - console.log '[irc] createNotExistUser ->'.yellow, 'userName:', name - Meteor.call 'registerUser', - email: "#{name}@rocketchat.org" - pass: 'rocketchat' - name: name - Meteor.users.update {name: name}, - $set: - status: 'online' - username: name - user = Meteor.users.findOne {name: name} - return user - - - createDirectRoomWhenNotExist: (source, target) -> - console.log '[irc] createDirectRoomWhenNotExist -> '.yellow, 'source:', source, 'target:', target - rid = [source._id, target._id].sort().join('') - now = new Date() - RocketChat.models.Rooms.upsert - _id: rid - , - $set: - usernames: [source.username, target.username] - $setOnInsert: - t: 'd' - msgs: 0 - ts: now - - RocketChat.models.Subscriptions.upsert - rid: rid - $and: [{'u._id': target._id}] - , - $setOnInsert: - name: source.username - t: 'd' - open: false - alert: false - unread: 0 - u: - _id: target._id - username: target.username - return { - t: 'd' - _id: rid - } - -IrcClient.getByUid = (uid) -> - return ircClientMap[uid] - -IrcClient.create = (login) -> - unless login.user? - return login - unless login.user._id of ircClientMap - ircClient = new IrcClient login - return async ircClient.connect - - return login - - -class IrcLoginer - constructor: (login) -> - console.log '[irc] validateLogin -> '.yellow, login - return IrcClient.create login - - -class IrcSender - constructor: (message) -> - name = message.u.username - timestamp = message.ts.getTime() - cacheKey = "#{name}#{timestamp}" - if ircReceiveMessageCache.get cacheKey - return message - - room = RocketChat.models.Rooms.findOneById message.rid, { fields: { name: 1, usernames: 1, t: 1 } } - ircClient = IrcClient.getByUid message.u._id - ircClient.sendMessage room, message - return message - - -class IrcRoomJoiner - constructor: (user, room) -> - ircClient = IrcClient.getByUid user._id - ircClient.joinRoom room - return room - - -class IrcRoomLeaver - constructor: (user, room) -> - ircClient = IrcClient.getByUid user._id - ircClient.leaveRoom room - return room - - -class IrcLogoutCleanUper - constructor: (user) -> - ircClient = IrcClient.getByUid user._id - ircClient.disconnect() - return user - - -# # # -# Make magic happen -# - -# Only proceed if the package has been enabled -if IRC_AVAILABILITY == true - RocketChat.callbacks.add 'beforeValidateLogin', IrcLoginer, RocketChat.callbacks.priority.LOW, 'irc-loginer' - RocketChat.callbacks.add 'beforeSaveMessage', IrcSender, RocketChat.callbacks.priority.LOW, 'irc-sender' - RocketChat.callbacks.add 'beforeJoinRoom', IrcRoomJoiner, RocketChat.callbacks.priority.LOW, 'irc-room-joiner' - RocketChat.callbacks.add 'beforeCreateChannel', IrcRoomJoiner, RocketChat.callbacks.priority.LOW, 'irc-room-joiner-create-channel' - RocketChat.callbacks.add 'beforeLeaveRoom', IrcRoomLeaver, RocketChat.callbacks.priority.LOW, 'irc-room-leaver' - RocketChat.callbacks.add 'afterLogoutCleanUp', IrcLogoutCleanUper, RocketChat.callbacks.priority.LOW, 'irc-clean-up' -else - return From d0929da0f01fd583ab2d2ce35b22f61935548122 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 31 May 2017 11:23:41 -0300 Subject: [PATCH 6/8] Update server.js --- packages/rocketchat-irc/server/server.js | 59 ++++++++++-------------- 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/packages/rocketchat-irc/server/server.js b/packages/rocketchat-irc/server/server.js index 316747d1b972..5d50d6e46239 100644 --- a/packages/rocketchat-irc/server/server.js +++ b/packages/rocketchat-irc/server/server.js @@ -179,7 +179,6 @@ class IrcClient { } onReceiveMessage(source, target, content) { - let room; const now = new Date; const timestamp = now.getTime(); let cacheKey = [source, target, content].join(','); @@ -191,15 +190,13 @@ class IrcClient { } console.log('[irc] onReceiveMessage -> '.yellow, 'source:', source, 'target:', target, 'content:', content); source = this.createUserWhenNotExist(source); + let room; if (target[0] === '#') { room = RocketChat.models.Rooms.findOneByName(target.substring(1)); } else { room = this.createDirectRoomWhenNotExist(source, this.user); } - const message = { - msg: content, - ts: now - }; + const message = { msg: content,ts: now }; cacheKey = `${ source.username }${ timestamp }`; ircReceiveMessageCache.set(cacheKey, true); console.log('[irc] ircReceiveMessageCache.set -> '.yellow, 'key:', cacheKey); @@ -221,7 +218,6 @@ class IrcClient { const appendMembers = _.difference(newMembers, oldMembers); const removeMembers = _.difference(oldMembers, newMembers); appendMembers.forEach(member => this.createUserWhenNotExist(member)); - RocketChat.models.Rooms.removeUsernamesById(room._id, removeMembers); RocketChat.models.Rooms.addUsernamesById(room._id, appendMembers); @@ -268,26 +264,21 @@ class IrcClient { initRoomList() { const roomsCursor = RocketChat.models.Rooms.findByTypeContainingUsername('c', this.user.username, { fields: { name: 1, t: 1 }}); const rooms = roomsCursor.fetch(); - - rooms.forEach(room => { - this.joinRoom(room); - }); + rooms.forEach(room => this.joinRoom(room)); } joinRoom(room) { - let msg; if (room.t !== 'c' || room.name === 'general') { return; } if (this.isJoiningRoom) { - this.pendingJoinRoomBuf.push(room.name); - } else { - console.log('[irc] joinRoom -> '.yellow, 'roomName:', room.name, 'pendingJoinRoomBuf:', this.pendingJoinRoomBuf.join(',')); - msg = `JOIN #${ room.name }\r\n`; - this.receiveMemberListBuf[room.name] = []; - this.sendRawMessage(msg); - this.isJoiningRoom = true; + return this.pendingJoinRoomBuf.push(room.name); } + console.log('[irc] joinRoom -> '.yellow, 'roomName:', room.name, 'pendingJoinRoomBuf:', this.pendingJoinRoomBuf.join(',')); + const msg = `JOIN #${ room.name }\r\n`; + this.receiveMemberListBuf[room.name] = []; + this.sendRawMessage(msg); + this.isJoiningRoom = true; } leaveRoom(room) { @@ -328,23 +319,23 @@ class IrcClient { } createUserWhenNotExist(name) { - let user = Meteor.users.findOne({ name }); - if (!user) { - console.log('[irc] createNotExistUser ->'.yellow, 'userName:', name); - Meteor.call('registerUser', { - email: `${ name }@rocketchat.org`, - pass: 'rocketchat', - name - }); - Meteor.users.update({ name }, { - $set: { - status: 'online', - username: name - } - }); - user = Meteor.users.findOne({ name }); + const user = Meteor.users.findOne({ name }); + if (user) { + return user; } - return user; + console.log('[irc] createNotExistUser ->'.yellow, 'userName:', name); + Meteor.call('registerUser', { + email: `${ name }@rocketchat.org`, + pass: 'rocketchat', + name + }); + Meteor.users.update({ name }, { + $set: { + status: 'online', + username: name + } + }); + return Meteor.users.findOne({ name }); } createDirectRoomWhenNotExist(source, target) { From 0b3d06fdb0a8c85ff9f3c39ce7ae3a07f292f4b6 Mon Sep 17 00:00:00 2001 From: Martin Schoeler Date: Wed, 31 May 2017 14:07:22 -0300 Subject: [PATCH 7/8] Bump Force Travis Restart --- packages/rocketchat-irc/server/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rocketchat-irc/server/server.js b/packages/rocketchat-irc/server/server.js index 5d50d6e46239..1dff914cd7ee 100644 --- a/packages/rocketchat-irc/server/server.js +++ b/packages/rocketchat-irc/server/server.js @@ -1,7 +1,7 @@ import net from 'net'; import Lru from 'lru-cache'; -////// +/////// // Assign values //Package availability From fe5bfdd81d5f08ca87cd65e3bf272ca771f0788d Mon Sep 17 00:00:00 2001 From: Martin Schoeler Date: Wed, 31 May 2017 14:20:01 -0300 Subject: [PATCH 8/8] fix eslint --- packages/rocketchat-irc/server/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rocketchat-irc/server/server.js b/packages/rocketchat-irc/server/server.js index 1dff914cd7ee..fd085e206005 100644 --- a/packages/rocketchat-irc/server/server.js +++ b/packages/rocketchat-irc/server/server.js @@ -196,7 +196,7 @@ class IrcClient { } else { room = this.createDirectRoomWhenNotExist(source, this.user); } - const message = { msg: content,ts: now }; + const message = { msg: content, ts: now }; cacheKey = `${ source.username }${ timestamp }`; ircReceiveMessageCache.set(cacheKey, true); console.log('[irc] ircReceiveMessageCache.set -> '.yellow, 'key:', cacheKey);