From d1d6017f91d7ad3df7cf702d034193e2c8f1aee1 Mon Sep 17 00:00:00 2001 From: Gabriel Engel <gabriel.engel@fgsys.com> Date: Fri, 12 Jun 2015 06:09:45 -0300 Subject: [PATCH] adding embedded hubot --- .../.npm/package/npm-shrinkwrap.json | 215 ++++++++++++++++++ packages/rocketchat-hubot/hubot.coffee | 193 ++++++++++++++++ packages/rocketchat-hubot/hubot.coffee.new | 187 --------------- packages/rocketchat-hubot/package.js | 11 +- server/methods/sendMessage.coffee | 7 + 5 files changed, 425 insertions(+), 188 deletions(-) delete mode 100644 packages/rocketchat-hubot/hubot.coffee.new diff --git a/packages/rocketchat-hubot/.npm/package/npm-shrinkwrap.json b/packages/rocketchat-hubot/.npm/package/npm-shrinkwrap.json index ba6a6192266..370832fe5fd 100644 --- a/packages/rocketchat-hubot/.npm/package/npm-shrinkwrap.json +++ b/packages/rocketchat-hubot/.npm/package/npm-shrinkwrap.json @@ -1,5 +1,220 @@ { "dependencies": { + "codex-blackboard-hubot-scripts": { + "version": "https://github.com/cscott/codex-blackboard-hubot-scripts/tarball/f57c178a2faee9b36d07a7905c29093b9824e0b0", + "dependencies": { + "hubot-calculator": { + "version": "0.4.0", + "dependencies": { + "coffee-script": { + "version": "1.6.3" + }, + "mathjs": { + "version": "1.7.0", + "dependencies": { + "decimal.js": { + "version": "4.0.2" + } + } + } + } + }, + "hubot-google-hangouts": { + "version": "0.7.1", + "dependencies": { + "coffee-script": { + "version": "1.6.3" + }, + "googleapis": { + "version": "0.4.7", + "dependencies": { + "request": { + "version": "2.25.0", + "dependencies": { + "qs": { + "version": "0.6.6" + }, + "json-stringify-safe": { + "version": "5.0.1" + }, + "forever-agent": { + "version": "0.5.2" + }, + "tunnel-agent": { + "version": "0.3.0" + }, + "http-signature": { + "version": "0.10.1", + "dependencies": { + "assert-plus": { + "version": "0.1.5" + }, + "asn1": { + "version": "0.1.11" + }, + "ctype": { + "version": "0.5.3" + } + } + }, + "hawk": { + "version": "1.0.0", + "dependencies": { + "hoek": { + "version": "0.9.1" + }, + "boom": { + "version": "0.4.2" + }, + "cryptiles": { + "version": "0.2.2" + }, + "sntp": { + "version": "0.2.4" + } + } + }, + "aws-sign": { + "version": "0.3.0" + }, + "oauth-sign": { + "version": "0.3.0" + }, + "cookie-jar": { + "version": "0.3.0" + }, + "node-uuid": { + "version": "1.4.3" + }, + "mime": { + "version": "1.2.11" + }, + "form-data": { + "version": "0.1.4", + "dependencies": { + "combined-stream": { + "version": "0.0.7", + "dependencies": { + "delayed-stream": { + "version": "0.0.5" + } + } + }, + "async": { + "version": "0.9.2" + } + } + } + } + }, + "async": { + "version": "0.2.6" + }, + "gapitoken": { + "version": "0.1.0", + "dependencies": { + "jws": { + "version": "0.0.2", + "dependencies": { + "tap": { + "version": "0.3.3", + "dependencies": { + "inherits": { + "version": "1.0.0" + }, + "yamlish": { + "version": "0.0.5" + }, + "slide": { + "version": "1.1.6" + }, + "runforcover": { + "version": "0.0.2", + "dependencies": { + "bunker": { + "version": "0.1.2", + "dependencies": { + "burrito": { + "version": "0.2.12", + "dependencies": { + "traverse": { + "version": "0.5.2" + }, + "uglify-js": { + "version": "1.1.1" + } + } + } + } + } + } + }, + "nopt": { + "version": "2.2.1", + "dependencies": { + "abbrev": { + "version": "1.0.7" + } + } + }, + "mkdirp": { + "version": "0.3.5" + }, + "difflet": { + "version": "0.2.6", + "dependencies": { + "traverse": { + "version": "0.6.6" + }, + "charm": { + "version": "0.1.2" + }, + "deep-is": { + "version": "0.1.3" + } + } + }, + "deep-equal": { + "version": "0.0.0" + }, + "buffer-equal": { + "version": "0.0.1" + } + } + }, + "base64url": { + "version": "0.0.3" + } + } + } + } + } + } + } + } + }, + "hubot-google-images": { + "version": "0.1.5" + }, + "hubot-google-translate": { + "version": "0.1.0" + }, + "hubot-help": { + "version": "0.1.1" + }, + "hubot-scripts": { + "version": "2.16.1", + "dependencies": { + "redis": { + "version": "0.8.4" + } + } + }, + "hubot-youtube": { + "version": "0.1.2" + } + } + }, "coffee-script": { "version": "1.9.3" }, diff --git a/packages/rocketchat-hubot/hubot.coffee b/packages/rocketchat-hubot/hubot.coffee index 8492b62f8d5..c94afb708d9 100644 --- a/packages/rocketchat-hubot/hubot.coffee +++ b/packages/rocketchat-hubot/hubot.coffee @@ -1,4 +1,197 @@ CoffeeScript = Npm.require('coffee-script') CoffeeScript.register() +HubotScripts = Npm.require('codex-blackboard-hubot-scripts'); Hubot = Npm.require('hubot') + +# Start a hubot, connected to our chat room. +'use strict' + +# Log messages? +DEBUG = true + +# Monkey-patch Hubot to support private messages +Hubot.Response::priv = (strings...) -> + @robot.adapter.priv @envelope, strings... + +# More monkey-patching +Hubot.Robot::loadAdapter = -> # disable + +# grrrr, Meteor.bindEnvironment doesn't preserve `this` apparently +bind = (f) -> + g = Meteor.bindEnvironment (self, args...) -> f.apply(self, args) + (args...) -> g @, args... + +class Robot extends Hubot.Robot + constructor: (args...) -> + super args... + @hear = bind @hear + @respond = bind @respond + @enter = bind @enter + @leave = bind @leave + @topic = bind @topic + @error = bind @error + @catchAll = bind @catchAll + loadAdapter: -> false + hear: (regex, callback) -> super regex, Meteor.bindEnvironment callback + respond: (regex, callback) -> super regex, Meteor.bindEnvironment callback + enter: (callback) -> super Meteor.bindEnvironment(callback) + leave: (callback) -> super Meteor.bindEnvironment(callback) + topic: (callback) -> super Meteor.bindEnvironment(callback) + error: (callback) -> super Meteor.bindEnvironment(callback) + catchAll: (callback) -> super Meteor.bindEnvironment(callback) + +sendHelper = Meteor.bindEnvironment (robot, envelope, strings, map) -> + while strings.length > 0 + string = strings.shift() + if typeof(string) == 'function' + string() + else + try + map(string) + catch err + console.error "Hubot error: #{err}" if DEBUG + robot.logger.error "RocketChat send error: #{err}" + +class RocketChatAdapter extends Hubot.Adapter + # Public: Raw method for sending data back to the chat source. Extend this. + # + # envelope - A Object with message, room and user details. + # strings - One or more Strings for each message to send. + # + # Returns nothing. + send: (envelope, strings...) -> + sendHelper @robot, envelope, strings, (string) => + console.log "send #{envelope.rid}: #{string} (#{envelope.u.username})" if DEBUG + return @priv envelope, string if envelope.message.private + Meteor.call "sendMessage", + u: + username: "rocketbot" + msg: string + rid: envelope.rid + + # Public: Raw method for sending emote data back to the chat source. + # + # envelope - A Object with message, room and user details. + # strings - One or more Strings for each message to send. + # + # Returns nothing. + emote: (envelope, strings...) -> + sendHelper @robot, envelope, strings, (string) => + console.log "emote #{envelope.rid}: #{string} (#{envelope.u.username})" if DEBUG + return @priv envelope, "*** #{string} ***" if envelope.message.private + Meteor.call "sendMessage", + u: + username: "rocketbot" + msg: string + rid: envelope.rid + action: true + + # Priv: our extension -- send a PM to user + priv: (envelope, strings...) -> + sendHelper @robot, envelope, strings, (string) -> + console.log "priv #{envelope.rid}: #{string} (#{envelope.u.username})" if DEBUG + Meteor.call "sendMessage", + u: + username: "rocketbot" + to: "#{envelope.u.username}" + msg: string + rid: envelope.rid + + # Public: Raw method for building a reply and sending it back to the chat + # source. Extend this. + # + # envelope - A Object with message, room and user details. + # strings - One or more Strings for each reply to send. + # + # Returns nothing. + reply: (envelope, strings...) -> + if envelope.message.private + @priv envelope, strings... + else + @send envelope, strings.map((str) -> "#{envelope.u.username}: #{str}")... + + # Public: Raw method for setting a topic on the chat source. Extend this. + # + # envelope - A Object with message, room and user details. + # strings - One more more Strings to set as the topic. + # + # Returns nothing. + topic: (envelope, strings...) -> + + # Public: Raw method for playing a sound in the chat source. Extend this. + # + # envelope - A Object with message, room and user details. + # strings - One or more strings for each play message to send. + # + # Returns nothing + play: (envelope, strings...) -> + + # Public: Raw method for invoking the bot to run. Extend this. + # + # Returns nothing. + run: -> + + # Public: Raw method for shutting the bot down. Extend this. + # + # Returns nothing. + close: -> + +class RocketBotReceiver + constructor: (message) -> + RocketBotUser = new Hubot.User(message.u.username, rid: message.rid) + RocketBotTextMessage = new Hubot.TextMessage(RocketBotUser, message.msg, message._id) + RocketBot.adapter.receive RocketBotTextMessage + console.log RocketBot; + return message + +RocketBot = new Robot null, null, false, Meteor.settings?.botname ? 'rocketbot' +RocketBot.alias = 'bot' +RocketBot.adapter = new RocketChatAdapter RocketBot +RocketChat.callbacks.add 'afterSaveMessage', RocketBotReceiver, RocketChat.callbacks.priority.LOW + +# Meteor.startup -> + # console.log RocketBot; + # # what's (the regexp for) my name? + # robot.respond /(?:)/, -> false + # mynameRE = robot.listeners.pop().regex + # # register scripts + # HubotScripts(robot) + # Object.keys(share.hubot).forEach (scriptName) -> + # console.log "Loading hubot script: #{scriptName}" + # share.hubot[scriptName](robot) + # # register our nick + # n = Meteor.call 'newNick', {name: 'rocketbot'} + # Meteor.call 'setTag', {type:'nicks', object:n._id, name:'Gravatar', value:'codex@printf.net', who:n.canon} + # # register our presence in general chat + # keepalive = -> Meteor.call 'setPresence', + # u: + # username: 'rocketbot' + # rid: '57om6EQCcFami9wuT' + # present: true + # foreground: true + # keepalive() + # Meteor.setInterval keepalive, 30*1000 # every 30s refresh presence + # # listen to the chat room, ignoring messages sent before we startup + # startup = true + # ChatMessage.find({}).observe + # added: (message) -> + # return if startup + # return if message.u.username is "rocketbot" or message.u.username is "" + # return if message.system or message.action or message.oplog or message.bodyIsHtml + # console.log "Received from #{message.u.username} in #{message.rid}: #{message.body}"\ + # if DEBUG + # user = new Hubot.User(message.u.username, room: message.rid) + # tm = new Hubot.TextMessage(user, message.body, message._id) + # tm.private = message.to? + # # if private, ensure it's treated as a direct address + # if tm.private and not mynameRE.test(tm.text) + # tm.text = "#{robot.name} #{tm.text}" + # adapter.receive tm + # startup = false + # Meteor.call "sendMessage", + # rid: '57om6EQCcFami9wuT' + # msg: 'wakes up' + # u: + # username: "rocketbot" + # action: true diff --git a/packages/rocketchat-hubot/hubot.coffee.new b/packages/rocketchat-hubot/hubot.coffee.new deleted file mode 100644 index 19e9a52a84d..00000000000 --- a/packages/rocketchat-hubot/hubot.coffee.new +++ /dev/null @@ -1,187 +0,0 @@ -hubot = Npm.require('hubot') - -Hubot = hubot; - -# Start a hubot, connected to our chat room. -'use strict' -model = share.model # import - -# Log messages? -DEBUG = true - -# Monkey-patch Hubot to support private messages -Hubot.Response::priv = (strings...) -> - @robot.adapter.priv @envelope, strings... - -# More monkey-patching -Hubot.Robot::loadAdapter = -> # disable - -# grrrr, Meteor.bindEnvironment doesn't preserve `this` apparently -bind = (f) -> - g = Meteor.bindEnvironment (self, args...) -> f.apply(self, args) - (args...) -> g @, args... - -class Robot extends Hubot.Robot - constructor: (args...) -> - super args... - @hear = bind @hear - @respond = bind @respond - @enter = bind @enter - @leave = bind @leave - @topic = bind @topic - @error = bind @error - @catchAll = bind @catchAll - loadAdapter: -> false - hear: (regex, callback) -> super regex, Meteor.bindEnvironment callback - respond: (regex, callback) -> super regex, Meteor.bindEnvironment callback - enter: (callback) -> super Meteor.bindEnvironment(callback) - leave: (callback) -> super Meteor.bindEnvironment(callback) - topic: (callback) -> super Meteor.bindEnvironment(callback) - error: (callback) -> super Meteor.bindEnvironment(callback) - catchAll: (callback) -> super Meteor.bindEnvironment(callback) - -sendHelper = Meteor.bindEnvironment (robot, envelope, strings, map) -> - # be present in the room - try - Meteor.call 'setPresence', - nick: 'codexbot' - room_name: envelope.room - present: true - foreground: true - while strings.length > 0 - string = strings.shift() - if typeof(string) == 'function' - string() - else - try - map(string) - catch err - console.error "Hubot error: #{err}" if DEBUG - robot.logger.error "Blackboard send error: #{err}" - -class BlackboardAdapter extends Hubot.Adapter - # Public: Raw method for sending data back to the chat source. Extend this. - # - # envelope - A Object with message, room and user details. - # strings - One or more Strings for each message to send. - # - # Returns nothing. - send: (envelope, strings...) -> - sendHelper @robot, envelope, strings, (string) => - console.log "send #{envelope.room}: #{string} (#{envelope.user.id})" if DEBUG - return @priv envelope, string if envelope.message.private - Meteor.call "newMessage", - nick: "codexbot" - body: string - room_name: envelope.room - - # Public: Raw method for sending emote data back to the chat source. - # - # envelope - A Object with message, room and user details. - # strings - One or more Strings for each message to send. - # - # Returns nothing. - emote: (envelope, strings...) -> - sendHelper @robot, envelope, strings, (string) => - console.log "emote #{envelope.room}: #{string} (#{envelope.user.id})" if DEBUG - return @priv envelope, "*** #{string} ***" if envelope.message.private - Meteor.call "newMessage", - nick: "codexbot" - body: string - room_name: envelope.room - action: true - - # Priv: our extension -- send a PM to user - priv: (envelope, strings...) -> - sendHelper @robot, envelope, strings, (string) -> - console.log "priv #{envelope.room}: #{string} (#{envelope.user.id})" if DEBUG - Meteor.call "newMessage", - nick: "codexbot" - to: "#{envelope.user.id}" - body: string - room_name: envelope.room - - # Public: Raw method for building a reply and sending it back to the chat - # source. Extend this. - # - # envelope - A Object with message, room and user details. - # strings - One or more Strings for each reply to send. - # - # Returns nothing. - reply: (envelope, strings...) -> - if envelope.message.private - @priv envelope, strings... - else - @send envelope, strings.map((str) -> "#{envelope.user.id}: #{str}")... - - # Public: Raw method for setting a topic on the chat source. Extend this. - # - # envelope - A Object with message, room and user details. - # strings - One more more Strings to set as the topic. - # - # Returns nothing. - topic: (envelope, strings...) -> - - # Public: Raw method for playing a sound in the chat source. Extend this. - # - # envelope - A Object with message, room and user details. - # strings - One or more strings for each play message to send. - # - # Returns nothing - play: (envelope, strings...) -> - - # Public: Raw method for invoking the bot to run. Extend this. - # - # Returns nothing. - run: -> - - # Public: Raw method for shutting the bot down. Extend this. - # - # Returns nothing. - close: -> - -Meteor.startup -> - robot = new Robot null, null, false, Meteor.settings?.botname ? 'codexbot' - robot.alias = 'bot' - adapter = robot.adapter = new BlackboardAdapter robot - # what's (the regexp for) my name? - robot.respond /(?:)/, -> false - mynameRE = robot.listeners.pop().regex - # register scripts - HubotScripts(robot) - Object.keys(share.hubot).forEach (scriptName) -> - console.log "Loading hubot script: #{scriptName}" - share.hubot[scriptName](robot) - # register our nick - n = Meteor.call 'newNick', {name: 'codexbot'} - Meteor.call 'setTag', {type:'nicks', object:n._id, name:'Gravatar', value:'codex@printf.net', who:n.canon} - # register our presence in general chat - keepalive = -> Meteor.call 'setPresence', - nick: 'codexbot' - room_name: 'general/0' - present: true - foreground: true - keepalive() - Meteor.setInterval keepalive, 30*1000 # every 30s refresh presence - # listen to the chat room, ignoring messages sent before we startup - startup = true - model.Messages.find({}).observe - added: (msg) -> - return if startup - return if msg.nick is "codexbot" or msg.nick is "" - return if msg.system or msg.action or msg.oplog or msg.bodyIsHtml - console.log "Received from #{msg.nick} in #{msg.room_name}: #{msg.body}"\ - if DEBUG - user = new Hubot.User(msg.nick, room: msg.room_name) - tm = new Hubot.TextMessage(user, msg.body, msg._id) - tm.private = msg.to? - # if private, ensure it's treated as a direct address - if tm.private and not mynameRE.test(tm.text) - tm.text = "#{robot.name} #{tm.text}" - adapter.receive tm - startup = false - Meteor.call "newMessage", - nick: "codexbot" - body: 'wakes up' - room_name: 'general/0' - action: true diff --git a/packages/rocketchat-hubot/package.js b/packages/rocketchat-hubot/package.js index 0ce7c635438..e58d9d52e85 100644 --- a/packages/rocketchat-hubot/package.js +++ b/packages/rocketchat-hubot/package.js @@ -8,15 +8,24 @@ Package.describe({ Package.onUse(function(api) { api.versionsFrom('1.0'); - api.use(['coffeescript']); + api.use([ + 'coffeescript', + 'rocketchat:lib@0.0.1' + ]); api.addFiles('hubot.coffee', ['server']); api.export('Hubot', ['server']); + api.export('HubotScripts', ['server']); + api.export('RocketBot', ['server']); + api.export('RocketBotReceiver', ['server']); + api.export('RocketChatAdapter', ['server']); + }); Npm.depends({ "coffee-script": "1.9.3", + "codex-blackboard-hubot-scripts": "https://github.com/cscott/codex-blackboard-hubot-scripts/tarball/f57c178a2faee9b36d07a7905c29093b9824e0b0", "hubot": "2.13.1" }); diff --git a/server/methods/sendMessage.coffee b/server/methods/sendMessage.coffee index 0a0afdf4ffe..f9884d25e40 100644 --- a/server/methods/sendMessage.coffee +++ b/server/methods/sendMessage.coffee @@ -116,3 +116,10 @@ Meteor.methods $unset: t: 1 expireAt: 1 + + Meteor.defer -> + + message._id = Random.id() + RocketChat.callbacks.run 'afterSaveMessage', message + + -- GitLab