Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add at mention removal code and used it in inspection #1032

Merged
merged 5 commits into from
Jul 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 65 additions & 1 deletion libraries/botbuilder-core/src/turnContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { Activity, ActivityTypes, ConversationReference, InputHints, ResourceResponse } from 'botframework-schema';
import { Activity, ActivityTypes, ConversationReference, InputHints, ResourceResponse, Mention } from 'botframework-schema';
import { BotAdapter } from './botAdapter';
import { shallowCopy } from './internal';

Expand Down Expand Up @@ -79,6 +79,70 @@ export class TurnContext {
}
}

/**
* Rewrites the activity text without any at mention.
*
* @remarks
* Some channels, for example Microsoft Teams, add at mention details into the text on a message activity.
* This can interfer with later procsesing. This is a helper function to remove the at mention.
*
* ```JavaScript
* const updatedText = TurnContext.removeRecipientMention(context.request);
* ```
* @param activity The activity to alter the text on
*/
public static removeRecipientMention(activity: Partial<Activity>): string {
return TurnContext.removeMentionText(activity, activity.recipient.id);
}

/**
* Rewrites the activity text without any at mention. Specifying a particular recipient id.
*
* @remarks
* Some channels, for example Microsoft Teams, add at mention details into the text on a message activity.
* This can interfer with later procsesing. This is a helper function to remove the at mention.
*
* ```JavaScript
* const updatedText = TurnContext.removeRecipientMention(context.request);
* ```
* @param activity The activity to alter the text on
* @param id The recipient id of the at mention
*/
public static removeMentionText(activity: Partial<Activity>, id: string): string {
var mentions = TurnContext.getMentions(activity);
for (var i=0; i<mentions.length; i++) {
if (mentions[i].mentioned.id === id) {
var mentionNameMatch = mentions[i].text.match(/(?<=<at.*>)(.*?)(?=<\/at>)/i);
if (mentionNameMatch.length > 0) {
activity.text = activity.text.replace(mentionNameMatch[0], '');
activity.text = activity.text.replace(/<at><\/at>/g, '');
}
}
}
return activity.text;
}

/**
* Returns the mentions on an activity.
*
* ```JavaScript
* const mentions = TurnContext.getMentions(context.request);
* ```
* @param activity The activity to alter the text on
* @param id The recipient id of the at mention
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This docstring is incorrect

*/
public static getMentions(activity: Partial<Activity>): Mention[] {
var result: Mention[] = [];
if (activity.entities !== undefined) {
for (var i=0; i<activity.entities.length; i++) {
if (activity.entities[i].type.toLowerCase() === 'mention') {
result.push(activity.entities[i] as Mention);
}
}
}
return result;
}

/**
* Returns the conversation reference for an activity.
*
Expand Down
23 changes: 23 additions & 0 deletions libraries/botbuilder-core/tests/turnContext.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,4 +349,27 @@ describe(`TurnContext`, function () {
});
});

it ('should remove at mention from activity', function() {

var activity = {
type: 'message',
text: '<at>TestOAuth619</at> test activity',
recipient: { id: 'TestOAuth619' },
entities: [
{
type: 'mention',
text: `<at>TestOAuth619</at>`,
mentioned: {
name: 'Bot',
id: `TestOAuth619`
}
}
]
};

var text = TurnContext.removeRecipientMention(activity);

assert(text,' test activity');
assert(activity.text,' test activity');
});
});
5 changes: 5 additions & 0 deletions libraries/botbuilder/src/inspectionMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ export class InspectionMiddleware extends InterceptionMiddleware {

if (turnContext.activity.type == ActivityTypes.Message && turnContext.activity.text !== undefined) {

var originalText = turnContext.activity.text;
TurnContext.removeRecipientMention(turnContext.activity);

var command = turnContext.activity.text.trim().split(' ');
if (command.length > 1 && command[0] === InspectionMiddleware.command) {

Expand All @@ -201,6 +204,8 @@ export class InspectionMiddleware extends InterceptionMiddleware {
return true;
}
}

turnContext.activity.text = originalText;
}

return false;
Expand Down
94 changes: 94 additions & 0 deletions libraries/botbuilder/tests/inspectionMiddleware.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,100 @@ describe('InspectionMiddleware', function() {

// verify that all our expectations have been met

assert(inboundExpectation.isDone(), 'The expectation of a trace message for the inbound activity was not met');
assert(outboundExpectation.isDone(), 'The expectation of a trace message for the outbound activity was not met');
assert(stateExpectation.isDone(), 'The expectation of a trace message for the bot state was not met');
});
it('should replicate activity data to listening emulator following open and attach with at mention', async function() {

// set up our expectations in nock - each corresponds to a trace message we expect to receive in the emulator

const inboundExpectation = nock('https://test.com')
.post('/v3/conversations/Convo1/activities', activity => activity.type === 'trace'
&& activity.value.text == 'hi')
.reply(200, { id: 'test' });

const outboundExpectation = nock('https://test.com')
.post('/v3/conversations/Convo1/activities', activity => activity.type === 'trace'
&& activity.value.text == 'echo: hi')
.reply(200, { id: 'test' });

const stateExpectation = nock('https://test.com')
.post('/v3/conversations/Convo1/activities', activity => activity.type === 'trace'
&& activity.value.userState && activity.value.userState.x.property == 'hello'
&& activity.value.conversationState && activity.value.conversationState.y.property == 'world')
.reply(200, { id: 'test' });

// create the various storage and middleware objects we will be using

var storage = new MemoryStorage();
var inspectionState = new InspectionState(storage);
var userState = new UserState(storage);
var conversationState = new ConversationState(storage);
var inspectionMiddleware = new InspectionMiddleware(inspectionState, userState, conversationState);

// the emulator sends an /INSPECT open command - we can use another adapter here

var openActivity = MessageFactory.text('/INSPECT open');

const inspectionAdapter = new TestAdapter(async (turnContext) => {
await inspectionMiddleware.processCommand(turnContext);
}, null, true);

await inspectionAdapter.receiveActivity(openActivity);

var inspectionOpenResultActivity = inspectionAdapter.activityBuffer[0];

var recipientId = 'bot';
var attachCommand = `<at>${ recipientId }</at> ${ inspectionOpenResultActivity.value }`;

// the logic of the bot including replying with a message and updating user and conversation state

var x = userState.createProperty('x');
var y = conversationState.createProperty('y');

var applicationAdapter = new TestAdapter(async (turnContext) => {

await turnContext.sendActivity(MessageFactory.text(`echo: ${ turnContext.activity.text }`));

(await x.get(turnContext, { property: '' })).property = 'hello';
(await y.get(turnContext, { property: '' })).property = 'world';

await userState.saveChanges(turnContext);
await conversationState.saveChanges(turnContext);

}, null, true);

// IMPORTANT add the InspectionMiddleware to the adapter that is running our bot

applicationAdapter.use(inspectionMiddleware);

var attachActivity = {
type: 'message',
text: attachCommand,
recipient: { id: recipientId },
entities: [
{
type: 'mention',
text: `<at>${ recipientId }</at>`,
mentioned: {
name: 'Bot',
id: recipientId
}
}
]
};

await applicationAdapter.receiveActivity(attachActivity);

// the attach command response is a informational message

await applicationAdapter.receiveActivity(MessageFactory.text('hi'));

// trace activities should be sent to the emulator using the connector and the conversation reference

// verify that all our expectations have been met

assert(inboundExpectation.isDone(), 'The expectation of a trace message for the inbound activity was not met');
assert(outboundExpectation.isDone(), 'The expectation of a trace message for the outbound activity was not met');
assert(stateExpectation.isDone(), 'The expectation of a trace message for the bot state was not met');
Expand Down