Adding initial system for middlewares

- This will allow people to write middlewares for different channelIds
 - Adding default TextMiddleware
 - Currently reply looks into channelId for composing a message, this
 might need some more refinement; possibly custom response type will
 solve the problem.

While this lays foundation it is in no way complete and adding some
middlewares will open up all the rough edges. Writing unit test is next.
This commit is contained in:
Zohaib Sibte Hassan 2017-05-22 12:44:57 -07:00
Родитель 518c8571f7
Коммит b89d0900c7
3 изменённых файлов: 256 добавлений и 33 удалений

135
coffeelint.json Normal file
Просмотреть файл

@ -0,0 +1,135 @@
{
"arrow_spacing": {
"level": "ignore"
},
"braces_spacing": {
"level": "ignore",
"spaces": 0,
"empty_object_spaces": 0
},
"camel_case_classes": {
"level": "error"
},
"coffeescript_error": {
"level": "error"
},
"colon_assignment_spacing": {
"level": "ignore",
"spacing": {
"left": 0,
"right": 0
}
},
"cyclomatic_complexity": {
"level": "ignore",
"value": 10
},
"duplicate_key": {
"level": "error"
},
"empty_constructor_needs_parens": {
"level": "ignore"
},
"ensure_comprehensions": {
"level": "warn"
},
"eol_last": {
"level": "ignore"
},
"indentation": {
"value": 4,
"level": "error"
},
"line_endings": {
"level": "ignore",
"value": "unix"
},
"max_line_length": {
"value": 256,
"level": "error",
"limitComments": true
},
"missing_fat_arrows": {
"level": "ignore",
"is_strict": false
},
"newlines_after_classes": {
"value": 3,
"level": "ignore"
},
"no_backticks": {
"level": "error"
},
"no_debugger": {
"level": "warn",
"console": false
},
"no_empty_functions": {
"level": "ignore"
},
"no_empty_param_list": {
"level": "ignore"
},
"no_implicit_braces": {
"level": "ignore",
"strict": true
},
"no_implicit_parens": {
"level": "ignore",
"strict": true
},
"no_interpolation_in_single_quotes": {
"level": "ignore"
},
"no_nested_string_interpolation": {
"level": "warn"
},
"no_plusplus": {
"level": "ignore"
},
"no_private_function_fat_arrows": {
"level": "warn"
},
"no_stand_alone_at": {
"level": "ignore"
},
"no_tabs": {
"level": "error"
},
"no_this": {
"level": "ignore"
},
"no_throwing_strings": {
"level": "error"
},
"no_trailing_semicolons": {
"level": "error"
},
"no_trailing_whitespace": {
"level": "error",
"allowed_in_comments": false,
"allowed_in_empty_lines": true
},
"no_unnecessary_double_quotes": {
"level": "ignore"
},
"no_unnecessary_fat_arrows": {
"level": "warn"
},
"non_empty_constructor_needs_parens": {
"level": "ignore"
},
"prefer_english_operator": {
"level": "ignore",
"doubleNotLevel": "ignore"
},
"space_operators": {
"level": "ignore"
},
"spacing_after_comma": {
"level": "ignore"
},
"transform_messes_up_line_numbers": {
"level": "warn"
}
}

Просмотреть файл

@ -1,15 +1,15 @@
#
# Copyright (c) Microsoft. All rights reserved.
# Licensed under the MIT license.
#
#
# Microsoft Bot Framework: http://botframework.com
#
#
# Bot Builder SDK Github:
# https://github.com/Microsoft/BotBuilder
#
#
# Copyright (c) Microsoft Corporation
# All rights reserved.
#
#
# MIT License:
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@ -18,10 +18,10 @@
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
#
# THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@ -31,58 +31,62 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
Util = require 'util'
Timers = require 'timers'
BotBuilder = require 'botbuilder'
{Robot, Adapter, TextMessage, User} = require 'hubot'
{ Robot, Adapter, TextMessage, User } = require 'hubot'
{ registerMiddleware, middlewareFor } = require './middlewares'
LogPrefix = "hubot-botframework:"
LogPrefix = "hubot-botframework-adapter:"
class BotFrameworkAdapter extends Adapter
constructor: (@robot) ->
super @robot
constructor: (robot) ->
super robot
@appId = process.env.BOTBUILDER_APP_ID
@appPassword = process.env.BOTBUILDER_APP_PASSWORD
@endpoint = process.env.BOTBUILDER_ENDPOINT || "/api/messages"
@robot.logger.info "#{LogPrefix} Adapter loaded. Using appId #{@appId}"
@connector = new BotBuilder.ChatConnector
@connector = new BotBuilder.ChatConnector {
appId: @appId
appPassword: @appPassword
}
@connector.onEvent (events, cb) => @onBotEvents events, cb
using: (name) ->
MiddlewareClass = middlewareFor(name)
new MiddlewareClass(@robot)
onBotEvents: (activities, cb) ->
@robot.logger.info "#{LogPrefix} onBotEvents"
activities = [activities] unless Array.isArray activities
@handleActivty activity for activity in activities
handleActivty: (activity) ->
@robot.logger.info "#{LogPrefix} Handling activity Channel: #{activity.source}; type: #{activity.type}"
@robot.receive @using(activity.source).toReceivable(activity)
for activity in activities
address = activity.address
user = @robot.brain.userForId address.user.id, name: address.user.name, room: address.conversation.id
user.activity = activity
if activity.type == 'message'
@robot.receive new TextMessage(user, activity.text, activity.sourceEvent.clientActivityId)
send: (context, messages...) ->
@robot.logger.info "#{LogPrefix} send"
console.log(messages)
@reply context, messages...
send: (context, strings...) ->
@robot.logger.info "#{LogPrefix} Message"
@reply context, strings...
reply: (context, strings...) ->
@robot.logger.info "#{LogPrefix} Sending reply"
for str in strings
msg =
type: 'message'
text: str
address: context.user.activity.address
@connector.send [msg]
reply: (context, messages...) ->
@robot.logger.info "#{LogPrefix} reply"
for msg in messages
channelId = msg.channelId || '*'
payload = [@using(channelId).toSendable(context, msg)]
@connector.send payload, (err, _) -> throw err if err
run: ->
@robot.router.post @endpoint, @connector.listen()
@robot.logger.info "#{LogPrefix} Adapter running."
Timers.setTimeout(=> @emit "connected", 1000)
Timers.setTimeout (=> @emit "connected"), 1000
exports.use = (robot) ->
new BotFrameworkAdapter robot
new BotFrameworkAdapter robot
exports.middlewares = require './middlewares'

84
src/middlewares.coffee Normal file
Просмотреть файл

@ -0,0 +1,84 @@
#
# Copyright (c) Microsoft. All rights reserved.
# Licensed under the MIT license.
#
# Microsoft Bot Framework: http://botframework.com
#
# Bot Builder SDK Github:
# https://github.com/Microsoft/BotBuilder
#
# Copyright (c) Microsoft Corporation
# All rights reserved.
#
# MIT License:
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
{ Robot, TextMessage, Message, User } = require 'hubot'
LogPrefix = "hubot-botframework-middleware:"
class BaseMiddleware
constructor: (@robot) ->
@robot.logger.info "#{LogPrefix} creating middleware..."
toReceivable: (activity) ->
throw new Error('toReceivable not implemented')
toSendable: (context, message) ->
throw new Error('toSendable not implemented')
class TextMiddleware extends BaseMiddleware
toReceivable: (activity) ->
@robot.logger.info "#{LogPrefix} TextMiddleware toReceivable"
address = activity.address
user = @robot.brain.userForId address.user.id, name: address.user.name, room: address.conversation.id
user.activity = activity
if activity.type == 'message'
return new TextMessage(user, activity.text, activity.sourceEvent.clientActivityId)
return new Message(user)
toSendable: (context, message) ->
@robot.logger.info "#{LogPrefix} TextMiddleware toSendable"
if typeof message is 'string'
return {
type: 'message'
text: message
address: context.user.activity.address
}
message
Middlewares = {
'*': TextMiddleware
}
module.exports = {
registerMiddleware: (name, middleware) ->
Middlewares[name] = middleware
middlewareFor: (name) ->
Middlewares[name] || Middlewares['*']
BaseMiddleware
TextMiddleware
}