Adds support for Skype through MicrosoftSkypeMiddleware

This commit is contained in:
Leonardo Chaia 2018-08-28 12:50:57 -03:00
Родитель 8d0cf959ff
Коммит c82cb2e74e
3 изменённых файлов: 414 добавлений и 1 удалений

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

@ -15,6 +15,7 @@ BotBuilder = require 'botbuilder'
{ Robot, Adapter, TextMessage, User } = require 'hubot'
Middleware = require './adapter-middleware'
MicrosoftTeamsMiddleware = require './msteams-middleware'
MicrosoftSkypeMiddleware = require './skype-middleware'
LogPrefix = "hubot-botframework-adapter:"
@ -71,7 +72,8 @@ class BotFrameworkAdapter extends Adapter
module.exports = {
Middleware,
MicrosoftTeamsMiddleware
MicrosoftTeamsMiddleware,
MicrosoftSkypeMiddleware
}
module.exports.use = (robot) ->

148
src/skype-middleware.coffee Normal file
Просмотреть файл

@ -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 MicrosoftSkypeMiddleware 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', MicrosoftSkypeMiddleware
module.exports = MicrosoftSkypeMiddleware

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

@ -0,0 +1,263 @@
chai = require 'chai'
expect = chai.expect
{ TextMessage, Message, User } = require 'hubot'
MockRobot = require './mock-robot'
MicrosoftSkypeMiddleware = require '../src/skype-middleware'
describe 'MicrosoftSkypeMiddleware', ->
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 MicrosoftSkypeMiddleware(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 MicrosoftSkypeMiddleware(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 MicrosoftSkypeMiddleware(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 MicrosoftSkypeMiddleware(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 MicrosoftSkypeMiddleware(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 MicrosoftSkypeMiddleware(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 MicrosoftSkypeMiddleware(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 MicrosoftSkypeMiddleware(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 MicrosoftSkypeMiddleware(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 MicrosoftSkypeMiddleware(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)