Skip to content

Commit 1f933f0

Browse files
authored
add EndOfConversation and Typing activities (#1706)
1 parent 2bb1d63 commit 1f933f0

File tree

4 files changed

+175
-36
lines changed

4 files changed

+175
-36
lines changed

libraries/botbuilder-core/src/activityHandler.ts

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,38 @@ export class ActivityHandler extends ActivityHandlerBase {
250250
return this.on('Event', handler);
251251
}
252252

253+
/**
254+
* Registers an activity event handler for the _end of conversation_ activity.
255+
*
256+
* @param handler The event handler.
257+
*
258+
* @remarks
259+
* Returns a reference to the [ActivityHandler](xref:botbuilder-core.ActivityHandler) object.
260+
*
261+
* This activity is typically send from a Skill to a Skill caller indicating the end of that particular child conversation.
262+
*
263+
* To handle an End of Conversation, use the
264+
* [onEndOfConversation](xref:botbuilder-core.ActivityHandler.onEndOfConversation) type-specific event handler.
265+
*/
266+
public onEndOfConversation(handler: BotHandler): this {
267+
return this.on('EndOfConversation', handler);
268+
}
269+
270+
/**
271+
* Registers an activity event handler for the _typing_ activity.
272+
*
273+
* @param handler The event handler.
274+
*
275+
* @remarks
276+
* Returns a reference to the [ActivityHandler](xref:botbuilder-core.ActivityHandler) object.
277+
*
278+
* To handle a Typing event, use the
279+
* [onTyping](xref:botbuilder-core.ActivityHandler.onTyping) type-specific event handler.
280+
*/
281+
public onTyping(handler: BotHandler): this {
282+
return this.on('Typing', handler);
283+
}
284+
253285
/**
254286
* Registers an activity event handler for the _tokens-response_ event, emitted for any incoming
255287
* `tokens/response` event activity. These are generated as part of the OAuth authentication flow.
@@ -270,7 +302,7 @@ export class ActivityHandler extends ActivityHandlerBase {
270302
public onTokenResponseEvent(handler: BotHandler): this {
271303
return this.on('TokenResponseEvent', handler);
272304
}
273-
305+
274306
/**
275307
* Registers an activity event handler for the _unrecognized activity type_ event, emitted for an
276308
* incoming activity with a type for which the [ActivityHandler](xref:botbuilder-core.ActivityHandler)
@@ -377,6 +409,38 @@ export class ActivityHandler extends ActivityHandlerBase {
377409
await this.handle(context, 'Message', this.defaultNextEvent(context));
378410
}
379411

412+
/**
413+
* Runs all registered _endOfConversation_ handlers and then continues the event emission process.
414+
*
415+
* @param context The context object for the current turn.
416+
*
417+
* @remarks
418+
* Overwrite this method to support channel-specific behavior across multiple channels.
419+
*
420+
* The default logic is to call any handlers registered via
421+
* [onEndOfConversationActivity](xref:botbuilder-core.ActivityHandler.onMessage),
422+
* and then continue by calling [defaultNextEvent](xref:botbuilder-core.ActivityHandler.defaultNextEvent).
423+
*/
424+
protected async onEndOfConversationActivity(context: TurnContext): Promise<void> {
425+
await this.handle(context, 'EndOfConversation', this.defaultNextEvent(context));
426+
}
427+
428+
/**
429+
* Runs all registered _typing_ handlers and then continues the event emission process.
430+
*
431+
* @param context The context object for the current turn.
432+
*
433+
* @remarks
434+
* Overwrite this method to support channel-specific behavior across multiple channels.
435+
*
436+
* The default logic is to call any handlers registered via
437+
* [onTypingActivity](xref:botbuilder-core.ActivityHandler.onTypingActivity),
438+
* and then continue by calling [defaultNextEvent](xref:botbuilder-core.ActivityHandler.defaultNextEvent).
439+
*/
440+
protected async onTypingActivity(context: TurnContext): Promise<void> {
441+
await this.handle(context, 'Typing', this.defaultNextEvent(context));
442+
}
443+
380444
/**
381445
* Runs all registered _unrecognized activity type_ handlers and then continues the event emission process.
382446
*

libraries/botbuilder-core/src/activityHandlerBase.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ export class ActivityHandlerBase {
6060
case ActivityTypes.Event:
6161
await this.onEventActivity(context);
6262
break;
63+
case ActivityTypes.EndOfConversation:
64+
await this.onEndOfConversationActivity(context);
65+
break;
66+
case ActivityTypes.Typing:
67+
await this.onTypingActivity(context);
68+
break;
6369
default:
6470
// handler for unknown or unhandled types
6571
await this.onUnrecognizedActivity(context);
@@ -143,6 +149,32 @@ export class ActivityHandlerBase {
143149
return;
144150
}
145151

152+
/**
153+
* Provides a hook for emitting the _end of conversation_ event.
154+
*
155+
* @param context The context object for the current turn.
156+
*
157+
* @remarks
158+
* Overwrite this method to run registered _end of conversation_ handlers and then continue the event
159+
* emission process.
160+
*/
161+
protected async onEndOfConversationActivity(context: TurnContext): Promise<void> {
162+
return;
163+
}
164+
165+
/**
166+
* Provides a hook for emitting the _typing_ event.
167+
*
168+
* @param context The context object for the current turn.
169+
*
170+
* @remarks
171+
* Overwrite this method to run registered _typing_ handlers and then continue the event
172+
* emission process.
173+
*/
174+
protected async onTypingActivity(context: TurnContext): Promise<void> {
175+
return;
176+
}
177+
146178
/**
147179
* Provides a hook for emitting the _unrecognized_ event.
148180
*

libraries/botbuilder-core/tests/ActivityHandler.test.js

Lines changed: 60 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ describe('ActivityHandler', function() {
2222
await bot.run(context).catch(error => done(error));
2323
}
2424

25-
it(`should fire onTurn for any inbound activity`, async function (done) {
25+
it(`should fire onTurn for any inbound activity`, async function(done) {
2626

2727
const bot = new ActivityHandler();
2828

29-
bot.onTurn(async(context, next) => {
29+
bot.onTurn(async (context, next) => {
3030
assert(true, 'onTurn not called');
3131
done();
3232
await next();
@@ -35,66 +35,66 @@ describe('ActivityHandler', function() {
3535
processActivity({type: 'any'}, bot, done);
3636
});
3737

38-
it(`should fire onMessage for any message activities`, async function (done) {
38+
it(`should fire onMessage for any message activities`, async function(done) {
3939

4040
const bot = new ActivityHandler();
4141

42-
bot.onMessage(async(context, next) => {
42+
bot.onMessage(async (context, next) => {
4343
assert(true, 'onMessage not called');
4444
done();
4545
await next();
4646
});
4747

48-
processActivity({type: 'message'}, bot, done);
48+
processActivity({type: ActivityTypes.Message}, bot, done);
4949
});
5050

51-
it(`calling next allows following events to firing`, async function (done) {
51+
it(`calling next allows following events to firing`, async function(done) {
5252

5353
const bot = new ActivityHandler();
5454

55-
bot.onTurn(async(context, next) => {
55+
bot.onTurn(async (context, next) => {
5656
assert(true, 'onTurn not called');
5757
await next();
5858
});
5959

60-
bot.onMessage(async(context, next) => {
60+
bot.onMessage(async (context, next) => {
6161
assert(true, 'onMessage not called');
6262
done();
6363
await next();
6464
});
6565

66-
processActivity({type: 'message'}, bot, done);
66+
processActivity({type: ActivityTypes.Message}, bot, done);
6767
});
6868

69-
it(`omitting call to next prevents following events from firing`, async function (done) {
69+
it(`omitting call to next prevents following events from firing`, async function(done) {
7070

7171
const bot = new ActivityHandler();
7272

73-
bot.onTurn(async(context, next) => {
73+
bot.onTurn(async (context, next) => {
7474
assert(true, 'onTurn not called');
7575
done();
7676
});
7777

78-
bot.onMessage(async(context, next) => {
78+
bot.onMessage(async (context, next) => {
7979
assert(false, 'onMessage called improperly!');
8080
await next();
8181
});
8282

83-
processActivity({type: 'message'}, bot, done);
83+
processActivity({type: ActivityTypes.Message}, bot, done);
8484
});
8585

86-
it(`binding 2 methods to the same event both fire`, async function (done) {
86+
it(`binding 2 methods to the same event both fire`, async function(done) {
8787

8888
const bot = new ActivityHandler();
8989
let count = 0;
9090

91-
bot.onMessage(async(context, next) => {
91+
bot.onMessage(async (context, next) => {
9292
assert(true, 'event 1 did not fire');
9393
count++;
9494
await next();
9595
});
9696

97-
bot.onMessage(async(context, next) => {
97+
bot.onMessage(async (context, next) => {
9898
assert(true, 'event 2 did not fire');
9999
count++;
100100

@@ -103,14 +103,14 @@ describe('ActivityHandler', function() {
103103
await next();
104104
});
105105

106-
processActivity({type: 'message'}, bot, done);
106+
processActivity({type: ActivityTypes.Message}, bot, done);
107107
});
108108

109-
it(`should fire onConversationUpdate`, async function (done) {
109+
it(`should fire onConversationUpdate`, async function(done) {
110110

111111
const bot = new ActivityHandler();
112112

113-
bot.onConversationUpdate(async(context, next) => {
113+
bot.onConversationUpdate(async (context, next) => {
114114
assert(true, 'onConversationUpdate not called');
115115
done();
116116
await next();
@@ -119,11 +119,11 @@ describe('ActivityHandler', function() {
119119
processActivity({type: ActivityTypes.ConversationUpdate}, bot, done);
120120
});
121121

122-
it(`should fire onMembersAdded`, async function (done) {
122+
it(`should fire onMembersAdded`, async function(done) {
123123

124124
const bot = new ActivityHandler();
125125

126-
bot.onMembersAdded(async(context, next) => {
126+
bot.onMembersAdded(async (context, next) => {
127127
assert(true, 'onConversationUpdate not called');
128128
done();
129129
await next();
@@ -132,11 +132,11 @@ describe('ActivityHandler', function() {
132132
processActivity({type: ActivityTypes.ConversationUpdate, membersAdded: [{id: 1}]}, bot, done);
133133
});
134134

135-
it(`should fire onMembersRemoved`, async function (done) {
135+
it(`should fire onMembersRemoved`, async function(done) {
136136

137137
const bot = new ActivityHandler();
138138

139-
bot.onMembersRemoved(async(context, next) => {
139+
bot.onMembersRemoved(async (context, next) => {
140140
assert(true, 'onMembersRemoved not called');
141141
done();
142142
await next();
@@ -145,11 +145,11 @@ describe('ActivityHandler', function() {
145145
processActivity({type: ActivityTypes.ConversationUpdate, membersRemoved: [{id: 1}]}, bot, done);
146146
});
147147

148-
it(`should fire onMessageReaction`, async function (done) {
148+
it(`should fire onMessageReaction`, async function(done) {
149149

150150
const bot = new ActivityHandler();
151151

152-
bot.onMessageReaction(async(context, next) => {
152+
bot.onMessageReaction(async (context, next) => {
153153
assert(true, 'onMessageReaction not called');
154154
done();
155155
await next();
@@ -158,11 +158,11 @@ describe('ActivityHandler', function() {
158158
processActivity({type: ActivityTypes.MessageReaction}, bot, done);
159159
});
160160

161-
it(`should fire onReactionsAdded`, async function (done) {
161+
it(`should fire onReactionsAdded`, async function(done) {
162162

163163
const bot = new ActivityHandler();
164164

165-
bot.onReactionsAdded(async(context, next) => {
165+
bot.onReactionsAdded(async (context, next) => {
166166
assert(true, 'onReactionsAdded not called');
167167
done();
168168
await next();
@@ -171,11 +171,11 @@ describe('ActivityHandler', function() {
171171
processActivity({type: ActivityTypes.MessageReaction, reactionsAdded: [{type: 'like'}]}, bot, done);
172172
});
173173

174-
it(`should fire onReactionsRemoved`, async function (done) {
174+
it(`should fire onReactionsRemoved`, async function(done) {
175175

176176
const bot = new ActivityHandler();
177177

178-
bot.onReactionsRemoved(async(context, next) => {
178+
bot.onReactionsRemoved(async (context, next) => {
179179
assert(true, 'onReactionsRemoved not called');
180180
done();
181181
await next();
@@ -184,11 +184,11 @@ describe('ActivityHandler', function() {
184184
processActivity({type: ActivityTypes.MessageReaction, reactionsRemoved: [{type: 'like'}]}, bot, done);
185185
});
186186

187-
it(`should fire onEvent`, async function (done) {
187+
it(`should fire onEvent`, async function(done) {
188188

189189
const bot = new ActivityHandler();
190190

191-
bot.onEvent(async(context, next) => {
191+
bot.onEvent(async (context, next) => {
192192
assert(true, 'onEvent not called');
193193
done();
194194
await next();
@@ -197,12 +197,37 @@ describe('ActivityHandler', function() {
197197
processActivity({type: ActivityTypes.Event}, bot, done);
198198
});
199199

200+
it(`should fire onEndOfConversation`, async function(done) {
200201

201-
it(`should fire onUnrecognizedActivityType`, async function (done) {
202+
const bot = new ActivityHandler();
203+
204+
bot.onEndOfConversation(async (context, next) => {
205+
assert(true, 'onEndOfConversation not called');
206+
done();
207+
await next();
208+
});
209+
210+
processActivity({type: ActivityTypes.EndOfConversation}, bot, done);
211+
});
212+
213+
it(`should fire onTyping`, async function(done) {
214+
215+
const bot = new ActivityHandler();
216+
217+
bot.onTyping(async (context, next) => {
218+
assert(true, 'onTyping not called');
219+
done();
220+
await next();
221+
});
222+
223+
processActivity({type: ActivityTypes.Typing}, bot, done);
224+
});
225+
226+
it(`should fire onUnrecognizedActivityType`, async function(done) {
202227

203228
const bot = new ActivityHandler();
204229

205-
bot.onUnrecognizedActivityType(async(context, next) => {
230+
bot.onUnrecognizedActivityType(async (context, next) => {
206231
assert(true, 'onUnrecognizedActivityType not called');
207232
done();
208233
await next();
@@ -211,11 +236,11 @@ describe('ActivityHandler', function() {
211236
processActivity({type: 'foo'}, bot, done);
212237
});
213238

214-
it(`should fire onDialog`, async function (done) {
239+
it(`should fire onDialog`, async function(done) {
215240

216241
const bot = new ActivityHandler();
217242

218-
bot.onDialog(async(context, next) => {
243+
bot.onDialog(async (context, next) => {
219244
assert(true, 'onDialog not called');
220245
done();
221246
await next();

0 commit comments

Comments
 (0)