Merge pull request #36 from leonardochaia/features/support-skype
Adds support for Skype through MicrosoftSkypeMiddleware
This commit is contained in:
Коммит
01d5be9d98
|
@ -15,6 +15,7 @@ BotBuilder = require 'botbuilder'
|
|||
{ Robot, Adapter, TextMessage, User } = require 'hubot'
|
||||
Middleware = require './adapter-middleware'
|
||||
MicrosoftTeamsMiddleware = require './msteams-middleware'
|
||||
SkypeMiddleware = require './skype-middleware'
|
||||
|
||||
LogPrefix = "hubot-botframework-adapter:"
|
||||
|
||||
|
@ -100,7 +101,8 @@ class BotFrameworkAdapter extends Adapter
|
|||
|
||||
module.exports = {
|
||||
Middleware,
|
||||
MicrosoftTeamsMiddleware
|
||||
MicrosoftTeamsMiddleware,
|
||||
SkypeMiddleware
|
||||
}
|
||||
|
||||
module.exports.use = (robot) ->
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
#
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
# Licensed under the MIT license.
|
||||
#
|
||||
# Description:
|
||||
# Middleware to make Hubot work well with Skype
|
||||
#
|
||||
# Configuration:
|
||||
# None
|
||||
#
|
||||
# Commands:
|
||||
# None
|
||||
#
|
||||
# Notes:
|
||||
# 1. Typing indicator support
|
||||
# 2. Properly handles chat vs. channel messages
|
||||
# 3. Properly handles image responses.
|
||||
#
|
||||
# Author:
|
||||
# billbliss
|
||||
#
|
||||
|
||||
{ Robot, TextMessage, Message, User } = require 'hubot'
|
||||
{ BaseMiddleware, registerMiddleware } = require './adapter-middleware'
|
||||
LogPrefix = "hubot-skype:"
|
||||
|
||||
class SkypeMiddleware extends BaseMiddleware
|
||||
|
||||
toReceivable: (activity) ->
|
||||
@robot.logger.info "#{LogPrefix} toReceivable"
|
||||
|
||||
# Get the user
|
||||
user = getUser(activity)
|
||||
user = @robot.brain.userForId(user.id, user)
|
||||
|
||||
# We don't want to save the activity or room in the brain since its something that changes per chat.
|
||||
user.activity = activity
|
||||
user.room = getRoomId(activity)
|
||||
|
||||
if activity.type == 'message'
|
||||
activity = fixActivityForHubot(activity, @robot)
|
||||
message = new TextMessage(user, activity.text, activity.address.id)
|
||||
return message
|
||||
|
||||
return new Message(user)
|
||||
|
||||
toSendable: (context, message) ->
|
||||
@robot.logger.info "#{LogPrefix} toSendable"
|
||||
activity = context?.user?.activity
|
||||
|
||||
response = message
|
||||
if typeof message is 'string'
|
||||
response =
|
||||
type: 'message'
|
||||
text: message
|
||||
address: activity?.address
|
||||
|
||||
imageAttachment = convertToImageAttachment(message)
|
||||
if imageAttachment?
|
||||
delete response.text
|
||||
response.attachments = [imageAttachment]
|
||||
|
||||
typingMessage =
|
||||
type: "typing"
|
||||
address: activity?.address
|
||||
|
||||
return [typingMessage, response]
|
||||
|
||||
#############################################################################
|
||||
# Helper methods for generating richer messages
|
||||
#############################################################################
|
||||
|
||||
imageRegExp = /^(https?:\/\/.+\/(.+)\.(jpg|png|gif|jpeg$))/
|
||||
|
||||
# Generate an attachment object from the first image URL in the message
|
||||
convertToImageAttachment = (message) ->
|
||||
if not typeof message is 'string'
|
||||
return null
|
||||
|
||||
result = imageRegExp.exec(message)
|
||||
if result?
|
||||
attachment =
|
||||
contentUrl: result[1]
|
||||
name: result[2]
|
||||
contentType: "image/#{result[3]}"
|
||||
return attachment
|
||||
|
||||
return null
|
||||
|
||||
# Fetches the user object from the activity
|
||||
getUser = (activity) ->
|
||||
user =
|
||||
id: activity?.address?.user?.id,
|
||||
name: activity?.address?.user?.name,
|
||||
tenant: getTenantId(activity)
|
||||
return user
|
||||
|
||||
# Fetches the room id from the activity
|
||||
getRoomId = (activity) ->
|
||||
return activity?.address?.conversation?.id
|
||||
|
||||
# Fetches the tenant id from the activity
|
||||
getTenantId = (activity) ->
|
||||
return activity?.sourceEvent?.tenant?.id
|
||||
|
||||
# Returns the array of mentions that can be found in the message.
|
||||
getMentions = (activity, userId) ->
|
||||
entities = activity?.entities || []
|
||||
if not Array.isArray(entities)
|
||||
entities = [entities]
|
||||
return entities.filter((entity) -> entity.type == "mention" && (not userId? || userId == entity.mentioned?.id))
|
||||
|
||||
# Fixes the activity to have the proper information for Hubot
|
||||
# 1. Replaces all occurances of the channel's bot at mention name with the configured name in hubot.
|
||||
# The hubot's configured name might not be the same name that is sent from the chat service in
|
||||
# the activity's text.
|
||||
# 2. Prepends hubot's name to the message if this is a direct message.
|
||||
fixActivityForHubot = (activity, robot) ->
|
||||
if not activity?.text? || typeof activity.text isnt 'string'
|
||||
return activity
|
||||
myChatId = activity?.address?.bot?.id
|
||||
if not myChatId?
|
||||
return activity
|
||||
|
||||
# replace all @ mentions with the robot's name
|
||||
mentions = getMentions(activity)
|
||||
for mention in mentions
|
||||
mentionTextRegExp = new RegExp(escapeRegExp(mention.text), "gi")
|
||||
replacement = mention.mentioned.name
|
||||
if mention.mentioned.id == myChatId
|
||||
replacement = robot.name
|
||||
|
||||
activity.text = activity.text.replace(mentionTextRegExp, replacement)
|
||||
|
||||
# prepends the robot's name for direct messages
|
||||
roomId = getRoomId(activity)
|
||||
if roomId? and not roomId.startsWith("19:") and not activity.text.startsWith(robot.name)
|
||||
activity.text = "#{robot.name} #{activity.text}"
|
||||
|
||||
return activity
|
||||
|
||||
escapeRegExp = (str) ->
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")
|
||||
|
||||
|
||||
registerMiddleware 'skype', SkypeMiddleware
|
||||
|
||||
module.exports = SkypeMiddleware
|
|
@ -0,0 +1,263 @@
|
|||
chai = require 'chai'
|
||||
expect = chai.expect
|
||||
{ TextMessage, Message, User } = require 'hubot'
|
||||
MockRobot = require './mock-robot'
|
||||
SkypeMiddleware = require '../src/skype-middleware'
|
||||
|
||||
describe 'SkypeMiddleware', ->
|
||||
describe 'toReceivable', ->
|
||||
robot = null
|
||||
event = null
|
||||
beforeEach ->
|
||||
robot = new MockRobot
|
||||
event =
|
||||
type: 'message'
|
||||
text: '<at>Bot</at> do something <at>Bot</at> and tell <at>User</at> about it'
|
||||
agent: 'tests'
|
||||
source: 'skype'
|
||||
entities: [
|
||||
type: "mention"
|
||||
text: "<at>Bot</at>"
|
||||
mentioned:
|
||||
id: "bot-id"
|
||||
name: "bot-name"
|
||||
,
|
||||
type: "mention"
|
||||
text: "<at>User</at>"
|
||||
mentioned:
|
||||
id: "user-id"
|
||||
name: "user-name"
|
||||
]
|
||||
sourceEvent:
|
||||
tenant:
|
||||
id: "tenant-id"
|
||||
address:
|
||||
conversation:
|
||||
id: "19:conversation-id"
|
||||
bot:
|
||||
id: "bot-id"
|
||||
user:
|
||||
id: "user-id"
|
||||
name: "user-name"
|
||||
|
||||
it 'return generic message when appropriate type is not found', ->
|
||||
# Setup
|
||||
event.type = 'typing'
|
||||
skypeMiddleware = new SkypeMiddleware(robot)
|
||||
|
||||
# Action
|
||||
receivable = null
|
||||
expect(() ->
|
||||
receivable = skypeMiddleware.toReceivable(event)
|
||||
).to.not.throw()
|
||||
|
||||
# Assert
|
||||
expect(receivable).to.be.not.null
|
||||
|
||||
it 'should work when activity text is an object', ->
|
||||
# Setup
|
||||
event.text = event
|
||||
skypeMiddleware = new SkypeMiddleware(robot)
|
||||
|
||||
# Action
|
||||
receivable = null
|
||||
expect(() ->
|
||||
receivable = skypeMiddleware.toReceivable(event)
|
||||
).to.not.throw()
|
||||
|
||||
# Assert
|
||||
expect(receivable.text).to.equal(event)
|
||||
|
||||
it 'should work when mentions not provided', ->
|
||||
# Setup
|
||||
delete event.entities
|
||||
skypeMiddleware = new SkypeMiddleware(robot)
|
||||
|
||||
# Action
|
||||
receivable = null
|
||||
expect(() ->
|
||||
receivable = skypeMiddleware.toReceivable(event)
|
||||
).to.not.throw()
|
||||
|
||||
# Assert
|
||||
expect(receivable.text).to.equal(event.text)
|
||||
|
||||
it 'should replace all @ mentions', ->
|
||||
# Setup
|
||||
skypeMiddleware = new SkypeMiddleware(robot)
|
||||
|
||||
# Action
|
||||
receivable = null
|
||||
expect(() ->
|
||||
receivable = skypeMiddleware.toReceivable(event)
|
||||
).to.not.throw()
|
||||
|
||||
# Assert
|
||||
expected = "#{robot.name} do something #{robot.name} and tell user-name about it"
|
||||
expect(receivable.text).to.equal(expected)
|
||||
|
||||
it 'should replace at mentions even when entities is not an array', ->
|
||||
# Setup
|
||||
event.entities = event.entities[0]
|
||||
skypeMiddleware = new SkypeMiddleware(robot)
|
||||
|
||||
# Action
|
||||
receivable = null
|
||||
expect(() ->
|
||||
receivable = skypeMiddleware.toReceivable(event)
|
||||
).to.not.throw()
|
||||
|
||||
# Assert
|
||||
expected = "#{robot.name} do something #{robot.name} and tell <at>User</at> about it"
|
||||
expect(receivable.text).to.equal(expected)
|
||||
|
||||
it 'should prepend bot name in 1:1 chats', ->
|
||||
# Setup
|
||||
event.address.conversation.id = event.address.user.id
|
||||
event.text = 'do something <at>Bot</at> and tell <at>User</at> about it'
|
||||
skypeMiddleware = new SkypeMiddleware(robot)
|
||||
|
||||
# Action
|
||||
receivable = null
|
||||
expect(() ->
|
||||
receivable = skypeMiddleware.toReceivable(event)
|
||||
).to.not.throw()
|
||||
|
||||
# Assert
|
||||
expected = "#{robot.name} do something #{robot.name} and tell user-name about it"
|
||||
expect(receivable.text).to.equal(expected)
|
||||
|
||||
describe 'toSendable', ->
|
||||
robot = null
|
||||
message = null
|
||||
context = null
|
||||
beforeEach ->
|
||||
robot = new MockRobot
|
||||
context =
|
||||
user:
|
||||
id: 'user-id'
|
||||
name: 'user-name'
|
||||
activity:
|
||||
type: 'message'
|
||||
text: '<at>Bot</at> do something <at>Bot</at> and tell <at>User</at> about it'
|
||||
agent: 'tests'
|
||||
source: 'skype'
|
||||
entities: [
|
||||
type: "mention"
|
||||
text: "<at>Bot</at>"
|
||||
mentioned:
|
||||
id: "bot-id"
|
||||
name: "bot-name"
|
||||
,
|
||||
type: "mention"
|
||||
text: "<at>User</at>"
|
||||
mentioned:
|
||||
id: "user-id"
|
||||
name: "user-name"
|
||||
]
|
||||
sourceEvent:
|
||||
tenant:
|
||||
id: "tenant-id"
|
||||
address:
|
||||
conversation:
|
||||
id: "19:conversation-id"
|
||||
bot:
|
||||
id: "bot-id"
|
||||
user:
|
||||
id: "user-id"
|
||||
name: "user-name"
|
||||
message = "message"
|
||||
|
||||
it 'should create message object for string messages', ->
|
||||
# Setup
|
||||
skypeMiddleware = new SkypeMiddleware(robot)
|
||||
|
||||
# Action
|
||||
sendable = null
|
||||
expect(() ->
|
||||
sendable = skypeMiddleware.toSendable(context, message)
|
||||
).to.not.throw()
|
||||
|
||||
# Verify
|
||||
expected = [
|
||||
type: 'typing',
|
||||
address: context.user.activity.address
|
||||
,
|
||||
type: 'message'
|
||||
text: message
|
||||
address: context.user.activity.address
|
||||
]
|
||||
|
||||
expect(sendable).to.deep.equal(expected)
|
||||
|
||||
it 'should not alter non-string messages', ->
|
||||
# Setup
|
||||
message =
|
||||
type: "some message type"
|
||||
skypeMiddleware = new SkypeMiddleware(robot)
|
||||
|
||||
# Action
|
||||
sendable = null
|
||||
expect(() ->
|
||||
sendable = skypeMiddleware.toSendable(context, message)
|
||||
).to.not.throw()
|
||||
|
||||
# Verify
|
||||
expected = [
|
||||
type: 'typing',
|
||||
address: context.user.activity.address
|
||||
,
|
||||
message
|
||||
]
|
||||
|
||||
expect(sendable).to.deep.equal(expected)
|
||||
|
||||
it 'should convert images', ->
|
||||
# Setup
|
||||
message = "http://test.com/thisisanimage.jpg"
|
||||
skypeMiddleware = new SkypeMiddleware(robot)
|
||||
|
||||
# Action
|
||||
sendable = null
|
||||
expect(() ->
|
||||
sendable = skypeMiddleware.toSendable(context, message)
|
||||
).to.not.throw()
|
||||
|
||||
# Verify
|
||||
expected = [
|
||||
type: 'typing',
|
||||
address: context.user.activity.address
|
||||
,
|
||||
type: 'message'
|
||||
attachments: [
|
||||
contentUrl: message,
|
||||
name: 'thisisanimage',
|
||||
contentType: 'image/jpg'
|
||||
]
|
||||
address: context.user.activity.address
|
||||
]
|
||||
|
||||
expect(sendable).to.deep.equal(expected)
|
||||
|
||||
it 'should not convert other links', ->
|
||||
# Setup
|
||||
message = "http://test.com/thisisanimage.html"
|
||||
skypeMiddleware = new SkypeMiddleware(robot)
|
||||
|
||||
# Action
|
||||
sendable = null
|
||||
expect(() ->
|
||||
sendable = skypeMiddleware.toSendable(context, message)
|
||||
).to.not.throw()
|
||||
|
||||
# Verify
|
||||
expected = [
|
||||
type: 'typing',
|
||||
address: context.user.activity.address
|
||||
,
|
||||
type: 'message'
|
||||
text: "http://test.com/thisisanimage.html"
|
||||
address: context.user.activity.address
|
||||
]
|
||||
|
||||
expect(sendable).to.deep.equal(expected)
|
Загрузка…
Ссылка в новой задаче