• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

microsoft / botbuilder-js / 5285173399

pending completion
5285173399

Pull #4485

github

web-flow
Merge 12d611b67 into 5ee34c0ce
Pull Request #4485: feat: Add support for config auth type (fetch & submit)

9780 of 12809 branches covered (76.35%)

Branch coverage included in aggregate %.

6 of 6 new or added lines in 1 file covered. (100.0%)

20042 of 22482 relevant lines covered (89.15%)

7103.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.01
/libraries/botbuilder/src/teamsActivityHandler.ts
1
/**
2
 * @module botbuilder
3
 */
4
/**
5
 * Copyright (c) Microsoft Corporation. All rights reserved.
6
 * Licensed under the MIT License.
7
 */
8

9
import {
2✔
10
    ActivityHandler,
11
    AppBasedLinkQuery,
12
    BotConfigAuth,
13
    ChannelInfo,
14
    Channels,
15
    ConfigResponse,
16
    ConfigTaskResponse,
17
    FileConsentCardResponse,
18
    InvokeResponse,
19
    MeetingEndEventDetails,
20
    MeetingStartEventDetails,
21
    MessagingExtensionAction,
22
    MessagingExtensionActionResponse,
23
    MessagingExtensionQuery,
24
    MessagingExtensionResponse,
25
    O365ConnectorCardActionQuery,
26
    SigninStateVerificationQuery,
27
    TabRequest,
28
    TabResponse,
29
    TabSubmit,
30
    TaskModuleRequest,
31
    TaskModuleResponse,
32
    TeamInfo,
33
    TeamsChannelAccount,
34
    TeamsChannelData,
35
    tokenExchangeOperationName,
36
    TurnContext,
37
    verifyStateOperationName,
38
} from 'botbuilder-core';
39
import { ReadReceiptInfo } from 'botframework-connector';
2✔
40
import { TeamsInfo } from './teamsInfo';
2✔
41
import * as z from 'zod';
2✔
42

43
const TeamsMeetingStartT = z
2✔
44
    .object({
45
        Id: z.string(),
46
        JoinUrl: z.string(),
47
        MeetingType: z.string(),
48
        Title: z.string(),
49
        StartTime: z.string(),
50
    })
51
    .nonstrict();
52

53
const TeamsMeetingEndT = z
2✔
54
    .object({
55
        Id: z.string(),
56
        JoinUrl: z.string(),
57
        MeetingType: z.string(),
58
        Title: z.string(),
59
        EndTime: z.string(),
60
    })
61
    .nonstrict();
62

63
/**
64
 * Adds support for Microsoft Teams specific events and interactions.
65
 *
66
 * @remarks
67
 * Developers may handle Message Update, Message Delete, and Conversation Update activities sent from Microsoft Teams via two methods:
68
 *  1. Overriding methods starting with `on..` and *not* ending in `..Event()` (e.g. `onTeamsMembersAdded()`), or instead
69
 *  2. Passing callbacks to methods starting with `on..` *and* ending in `...Event()` (e.g. `onTeamsMembersAddedEvent()`),
70
 *      to stay in line with older {@see ActivityHandler} implementation.
71
 *
72
 * Developers should use either #1 or #2, above for all Message Update, Message Delete, and Conversation Update activities and not *both* #1 and #2 for the same activity. Meaning,
73
 *   developers should override `onTeamsMembersAdded()` and not use both `onTeamsMembersAdded()` and `onTeamsMembersAddedEvent()`.
74
 *
75
 * Developers wanting to handle Invoke activities *must* override methods starting with `handle...()` (e.g. `handleTeamsTaskModuleFetch()`).
76
 */
77
export class TeamsActivityHandler extends ActivityHandler {
2✔
78
    /**
79
     * Invoked when an invoke activity is received from the connector.
80
     * Invoke activities can be used to communicate many different things.
81
     *
82
     * @param context A context object for this turn.
83
     * @returns An Invoke Response for the activity.
84
     */
85
    protected async onInvokeActivity(context: TurnContext): Promise<InvokeResponse> {
86
        let runEvents = true;
98✔
87
        try {
98✔
88
            if (!context.activity.name && context.activity.channelId === 'msteams') {
98✔
89
                return await this.handleTeamsCardActionInvoke(context);
2✔
90
            } else {
91
                switch (context.activity.name) {
94✔
92
                    case 'config/fetch':
94✔
93
                        return ActivityHandler.createInvokeResponse(
2✔
94
                            await this.handleTeamsConfigFetch(context, context.activity.value)
95
                        );
96
                    case 'config/submit':
97
                        return ActivityHandler.createInvokeResponse(
2✔
98
                            await this.handleTeamsConfigSubmit(context, context.activity.value)
99
                        );
100
                    case 'fileConsent/invoke':
101
                        return ActivityHandler.createInvokeResponse(
18✔
102
                            await this.handleTeamsFileConsent(context, context.activity.value)
103
                        );
104

105
                    case 'actionableMessage/executeAction':
106
                        await this.handleTeamsO365ConnectorCardAction(context, context.activity.value);
4✔
107
                        return ActivityHandler.createInvokeResponse();
2✔
108

109
                    case 'composeExtension/queryLink':
110
                        return ActivityHandler.createInvokeResponse(
4✔
111
                            await this.handleTeamsAppBasedLinkQuery(context, context.activity.value)
112
                        );
113

114
                    case 'composeExtension/anonymousQueryLink':
115
                        return ActivityHandler.createInvokeResponse(
2✔
116
                            await this.handleTeamsAnonymousAppBasedLinkQuery(context, context.activity.value)
117
                        );
118

119
                    case 'composeExtension/query':
120
                        return ActivityHandler.createInvokeResponse(
4✔
121
                            await this.handleTeamsMessagingExtensionQuery(context, context.activity.value)
122
                        );
123

124
                    case 'composeExtension/selectItem':
125
                        return ActivityHandler.createInvokeResponse(
4✔
126
                            await this.handleTeamsMessagingExtensionSelectItem(context, context.activity.value)
127
                        );
128

129
                    case 'composeExtension/submitAction':
130
                        return ActivityHandler.createInvokeResponse(
10✔
131
                            await this.handleTeamsMessagingExtensionSubmitActionDispatch(
132
                                context,
133
                                context.activity.value
134
                            )
135
                        );
136

137
                    case 'composeExtension/fetchTask':
138
                        return ActivityHandler.createInvokeResponse(
4✔
139
                            await this.handleTeamsMessagingExtensionFetchTask(context, context.activity.value)
140
                        );
141

142
                    case 'composeExtension/querySettingUrl':
143
                        return ActivityHandler.createInvokeResponse(
6✔
144
                            await this.handleTeamsMessagingExtensionConfigurationQuerySettingUrl(
145
                                context,
146
                                context.activity.value
147
                            )
148
                        );
149

150
                    case 'composeExtension/setting':
151
                        await this.handleTeamsMessagingExtensionConfigurationSetting(context, context.activity.value);
4✔
152
                        return ActivityHandler.createInvokeResponse();
2✔
153

154
                    case 'composeExtension/onCardButtonClicked':
155
                        await this.handleTeamsMessagingExtensionCardButtonClicked(context, context.activity.value);
4✔
156
                        return ActivityHandler.createInvokeResponse();
2✔
157

158
                    case 'task/fetch':
159
                        return ActivityHandler.createInvokeResponse(
4✔
160
                            await this.handleTeamsTaskModuleFetch(context, context.activity.value)
161
                        );
162

163
                    case 'task/submit':
164
                        return ActivityHandler.createInvokeResponse(
4✔
165
                            await this.handleTeamsTaskModuleSubmit(context, context.activity.value)
166
                        );
167

168
                    case 'tab/fetch':
169
                        return ActivityHandler.createInvokeResponse(
4✔
170
                            await this.handleTeamsTabFetch(context, context.activity.value)
171
                        );
172

173
                    case 'tab/submit':
174
                        return ActivityHandler.createInvokeResponse(
4✔
175
                            await this.handleTeamsTabSubmit(context, context.activity.value)
176
                        );
177

178
                    default:
179
                        runEvents = false;
10✔
180
                        return super.onInvokeActivity(context);
10✔
181
                }
182
            }
183
        } catch (err: any) {
184
            if (err.message === 'NotImplemented') {
52✔
185
                return { status: 501 };
42✔
186
            } else if (err.message === 'BadRequest') {
10✔
187
                return { status: 400 };
8✔
188
            }
189
            throw err;
2✔
190
        } finally {
191
            if (runEvents) {
98✔
192
                this.defaultNextEvent(context)();
88✔
193
            }
194
        }
195
    }
196

197
    /**
198
     * Handles a Teams Card Action Invoke activity.
199
     *
200
     * @param _context A context object for this turn.
201
     * @returns An Invoke Response for the activity.
202
     */
203
    protected async handleTeamsCardActionInvoke(_context: TurnContext): Promise<InvokeResponse> {
204
        throw new Error('NotImplemented');
2✔
205
    }
206

207
    /**
208
     * Handles a config/fetch invoke activity.
209
     *
210
     * @param _context A context object for this turn.
211
     * @param _configData The object representing the configuration.
212
     * @returns An Invoke Response for the activity.
213
     */
214
    protected async handleTeamsConfigFetch(
215
        _context: TurnContext,
216
        _configData: any
217
    ): Promise<ConfigResponse<BotConfigAuth | ConfigTaskResponse>> {
218
        throw new Error('NotImplemented');
2✔
219
    }
220

221
    /**
222
     * Handles a config/submit invoke activity.
223
     *
224
     * @param _context A context object for this turn.
225
     * @param _configData The object representing the configuration.
226
     * @returns An Invoke Response for the activity.
227
     */
228
    protected async handleTeamsConfigSubmit(
229
        _context: TurnContext,
230
        _configData: any
231
    ): Promise<ConfigResponse<BotConfigAuth | ConfigTaskResponse>> {
232
        throw new Error('NotImplemented');
2✔
233
    }
234

235
    /**
236
     * Receives invoke activities with Activity name of 'fileConsent/invoke'. Handlers registered here run before
237
     * `handleTeamsFileConsentAccept` and `handleTeamsFileConsentDecline`.
238
     * Developers are not passed a pointer to the next `handleTeamsFileConsent` handler because the _wrapper_ around
239
     * the handler will call `onDialogs` handlers after delegating to `handleTeamsFileConsentAccept` or `handleTeamsFileConsentDecline`.
240
     *
241
     * @param context A context object for this turn.
242
     * @param fileConsentCardResponse Represents the value of the invoke activity sent when the user acts on a file consent card.
243
     * @returns A promise that represents the work queued.
244
     */
245
    protected async handleTeamsFileConsent(
246
        context: TurnContext,
247
        fileConsentCardResponse: FileConsentCardResponse
248
    ): Promise<void> {
249
        switch (fileConsentCardResponse.action) {
16✔
250
            case 'accept':
16✔
251
                return await this.handleTeamsFileConsentAccept(context, fileConsentCardResponse);
6✔
252
            case 'decline':
253
                return await this.handleTeamsFileConsentDecline(context, fileConsentCardResponse);
6✔
254
            default:
255
                throw new Error('BadRequest');
4✔
256
        }
257
    }
258

259
    /**
260
     * Receives invoke activities with Activity name of 'fileConsent/invoke' with confirmation from user
261
     *
262
     * @remarks
263
     * This type of invoke activity occur during the File Consent flow.
264
     * @param _context A context object for this turn.
265
     * @param _fileConsentCardResponse Represents the value of the invoke activity sent when the user acts on a file consent card.
266
     * @returns A promise that represents the work queued.
267
     */
268
    protected async handleTeamsFileConsentAccept(
269
        _context: TurnContext,
270
        _fileConsentCardResponse: FileConsentCardResponse
271
    ): Promise<void> {
272
        throw new Error('NotImplemented');
2✔
273
    }
274

275
    /**
276
     * Receives invoke activities with Activity name of 'fileConsent/invoke' with decline from user
277
     *
278
     * @remarks
279
     * This type of invoke activity occur during the File Consent flow.
280
     * @param _context A context object for this turn.
281
     * @param _fileConsentCardResponse Represents the value of the invoke activity sent when the user acts on a file consent card.
282
     * @returns A promise that represents the work queued.
283
     */
284
    protected async handleTeamsFileConsentDecline(
285
        _context: TurnContext,
286
        _fileConsentCardResponse: FileConsentCardResponse
287
    ): Promise<void> {
288
        throw new Error('NotImplemented');
2✔
289
    }
290

291
    /**
292
     * Receives invoke activities with Activity name of 'actionableMessage/executeAction'.
293
     *
294
     * @param _context A context object for this turn.
295
     * @param _query The O365 connector card HttpPOST invoke query.
296
     * @returns A promise that represents the work queued.
297
     */
298
    protected async handleTeamsO365ConnectorCardAction(
299
        _context: TurnContext,
300
        _query: O365ConnectorCardActionQuery
301
    ): Promise<void> {
302
        throw new Error('NotImplemented');
2✔
303
    }
304

305
    /**
306
     * Invoked when a signIn invoke activity is received from the connector.
307
     *
308
     * @param context A context object for this turn.
309
     * @returns A promise that represents the work queued.
310
     */
311
    protected async onSignInInvoke(context: TurnContext): Promise<void> {
312
        switch (context.activity.name) {
8✔
313
            case verifyStateOperationName:
8✔
314
                return await this.handleTeamsSigninVerifyState(context, context.activity.value);
4✔
315
            case tokenExchangeOperationName:
316
                return await this.handleTeamsSigninTokenExchange(context, context.activity.value);
4✔
317
        }
318
    }
319

320
    /**
321
     * Receives invoke activities with Activity name of 'signin/verifyState'.
322
     *
323
     * @param _context A context object for this turn.
324
     * @param _query Signin state (part of signin action auth flow) verification invoke query.
325
     * @returns A promise that represents the work queued.
326
     */
327
    protected async handleTeamsSigninVerifyState(
328
        _context: TurnContext,
329
        _query: SigninStateVerificationQuery
330
    ): Promise<void> {
331
        throw new Error('NotImplemented');
2✔
332
    }
333

334
    /**
335
     * Receives invoke activities with Activity name of 'signin/tokenExchange'
336
     *
337
     * @param _context A context object for this turn.
338
     * @param _query Signin state (part of signin action auth flow) verification invoke query
339
     * @returns A promise that represents the work queued.
340
     */
341
    protected async handleTeamsSigninTokenExchange(
342
        _context: TurnContext,
343
        _query: SigninStateVerificationQuery
344
    ): Promise<void> {
345
        throw new Error('NotImplemented');
2✔
346
    }
347

348
    /**
349
     * Receives invoke activities with Activity name of 'composeExtension/onCardButtonClicked'
350
     *
351
     * @param _context A context object for this turn.
352
     * @param _cardData Object representing the card data.
353
     * @returns A promise that represents the work queued.
354
     */
355
    protected async handleTeamsMessagingExtensionCardButtonClicked(
356
        _context: TurnContext,
357
        _cardData: any
358
    ): Promise<void> {
359
        throw new Error('NotImplemented');
2✔
360
    }
361

362
    /**
363
     * Receives invoke activities with Activity name of 'task/fetch'
364
     *
365
     * @param _context A context object for this turn.
366
     * @param _taskModuleRequest The task module invoke request value payload.
367
     * @returns A Task Module Response for the request.
368
     */
369
    protected async handleTeamsTaskModuleFetch(
370
        _context: TurnContext,
371
        _taskModuleRequest: TaskModuleRequest
372
    ): Promise<TaskModuleResponse> {
373
        throw new Error('NotImplemented');
2✔
374
    }
375

376
    /**
377
     * Receives invoke activities with Activity name of 'task/submit'
378
     *
379
     * @param _context A context object for this turn.
380
     * @param _taskModuleRequest The task module invoke request value payload.
381
     * @returns A Task Module Response for the request.
382
     */
383
    protected async handleTeamsTaskModuleSubmit(
384
        _context: TurnContext,
385
        _taskModuleRequest: TaskModuleRequest
386
    ): Promise<TaskModuleResponse> {
387
        throw new Error('NotImplemented');
2✔
388
    }
389

390
    /**
391
     * Receives invoke activities with Activity name of 'tab/fetch'
392
     *
393
     * @param _context A context object for this turn.
394
     * @param _tabRequest The tab invoke request value payload.
395
     * @returns A Tab Response for the request.
396
     */
397
    protected async handleTeamsTabFetch(_context: TurnContext, _tabRequest: TabRequest): Promise<TabResponse> {
398
        throw new Error('NotImplemented');
2✔
399
    }
400

401
    /**
402
     * Receives invoke activities with Activity name of 'tab/submit'
403
     *
404
     * @param _context A context object for this turn.
405
     * @param _tabSubmit The tab submit invoke request value payload.
406
     * @returns A Tab Response for the request.
407
     */
408
    protected async handleTeamsTabSubmit(_context: TurnContext, _tabSubmit: TabSubmit): Promise<TabResponse> {
409
        throw new Error('NotImplemented');
2✔
410
    }
411

412
    /**
413
     * Receives invoke activities with Activity name of 'composeExtension/queryLink'
414
     *
415
     * @remarks
416
     * Used in creating a Search-based Message Extension.
417
     * @param _context A context object for this turn.
418
     * @param _query he invoke request body type for app-based link query.
419
     * @returns The Messaging Extension Response for the query.
420
     */
421
    protected async handleTeamsAppBasedLinkQuery(
422
        _context: TurnContext,
423
        _query: AppBasedLinkQuery
424
    ): Promise<MessagingExtensionResponse> {
425
        throw new Error('NotImplemented');
2✔
426
    }
427

428
    /**
429
     * Receives invoke activities with Activity name of 'composeExtension/anonymousQueryLink'
430
     *
431
     * @remarks
432
     * Used in creating a Search-based Message Extension.
433
     * @param _context A context object for this turn.
434
     * @param _query he invoke request body type for app-based link query.
435
     * @returns The Messaging Extension Response for the query.
436
     */
437
    protected async handleTeamsAnonymousAppBasedLinkQuery(
438
        _context: TurnContext,
439
        _query: AppBasedLinkQuery
440
    ): Promise<MessagingExtensionResponse> {
441
        throw new Error('NotImplemented');
×
442
    }
443

444
    /**
445
     * Receives invoke activities with the name 'composeExtension/query'.
446
     *
447
     * @remarks
448
     * Used in creating a Search-based Message Extension.
449
     * @param _context A context object for this turn.
450
     * @param _query The query for the search command.
451
     * @returns The Messaging Extension Response for the query.
452
     */
453
    protected async handleTeamsMessagingExtensionQuery(
454
        _context: TurnContext,
455
        _query: MessagingExtensionQuery
456
    ): Promise<MessagingExtensionResponse> {
457
        throw new Error('NotImplemented');
2✔
458
    }
459

460
    /**
461
     * Receives invoke activities with the name 'composeExtension/selectItem'.
462
     *
463
     * @remarks
464
     * Used in creating a Search-based Message Extension.
465
     * @param _context A context object for this turn.
466
     * @param _query The object representing the query.
467
     * @returns The Messaging Extension Response for the query.
468
     */
469
    protected async handleTeamsMessagingExtensionSelectItem(
470
        _context: TurnContext,
471
        _query: any
472
    ): Promise<MessagingExtensionResponse> {
473
        throw new Error('NotImplemented');
2✔
474
    }
475

476
    /**
477
     * Receives invoke activities with the name 'composeExtension/submitAction' and dispatches to botMessagePreview-flows as applicable.
478
     *
479
     * @remarks
480
     * A handler registered through this method does not dispatch to the next handler (either `handleTeamsMessagingExtensionSubmitAction`, `handleTeamsMessagingExtensionBotMessagePreviewEdit`, or `handleTeamsMessagingExtensionBotMessagePreviewSend`).
481
     * This method exists for developers to optionally add more logic before the TeamsActivityHandler routes the activity to one of the
482
     * previously mentioned handlers.
483
     * @param context A context object for this turn.
484
     * @param action The messaging extension action.
485
     * @returns The Messaging Extension Action Response for the action.
486
     */
487
    protected async handleTeamsMessagingExtensionSubmitActionDispatch(
488
        context: TurnContext,
489
        action: MessagingExtensionAction
490
    ): Promise<MessagingExtensionActionResponse> {
491
        if (action.botMessagePreviewAction) {
10✔
492
            switch (action.botMessagePreviewAction) {
8✔
493
                case 'edit':
8✔
494
                    return await this.handleTeamsMessagingExtensionBotMessagePreviewEdit(context, action);
2✔
495
                case 'send':
496
                    return await this.handleTeamsMessagingExtensionBotMessagePreviewSend(context, action);
2✔
497
                default:
498
                    throw new Error('BadRequest');
4✔
499
            }
500
        } else {
501
            return await this.handleTeamsMessagingExtensionSubmitAction(context, action);
2✔
502
        }
503
    }
504

505
    /**
506
     * Receives invoke activities with the name 'composeExtension/submitAction'.
507
     *
508
     * @param _context A context object for this turn.
509
     * @param _action The messaging extension action.
510
     * @returns The Messaging Extension Action Response for the action.
511
     */
512
    protected async handleTeamsMessagingExtensionSubmitAction(
513
        _context: TurnContext,
514
        _action: MessagingExtensionAction
515
    ): Promise<MessagingExtensionActionResponse> {
516
        throw new Error('NotImplemented');
2✔
517
    }
518

519
    /**
520
     * Receives invoke activities with the name 'composeExtension/submitAction' with the 'botMessagePreview' property present on activity.value.
521
     * The value for 'botMessagePreview' is 'edit'.
522
     *
523
     * @param _context A context object for this turn.
524
     * @param _action The messaging extension action.
525
     * @returns The Messaging Extension Action Response for the action.
526
     */
527
    protected async handleTeamsMessagingExtensionBotMessagePreviewEdit(
528
        _context: TurnContext,
529
        _action: MessagingExtensionAction
530
    ): Promise<MessagingExtensionActionResponse> {
531
        throw new Error('NotImplemented');
2✔
532
    }
533

534
    /**
535
     * Receives invoke activities with the name 'composeExtension/submitAction' with the 'botMessagePreview' property present on activity.value.
536
     * The value for 'botMessagePreview' is 'send'.
537
     *
538
     * @param _context A context object for this turn.
539
     * @param _action The messaging extension action.
540
     * @returns The Messaging Extension Action Response for the action.
541
     */
542
    protected async handleTeamsMessagingExtensionBotMessagePreviewSend(
543
        _context: TurnContext,
544
        _action: MessagingExtensionAction
545
    ): Promise<MessagingExtensionActionResponse> {
546
        throw new Error('NotImplemented');
2✔
547
    }
548

549
    /**
550
     * Receives invoke activities with the name 'composeExtension/fetchTask'
551
     *
552
     * @param _context A context object for this turn.
553
     * @param _action The messaging extension action.
554
     * @returns The Messaging Extension Action Response for the action.
555
     */
556
    protected async handleTeamsMessagingExtensionFetchTask(
557
        _context: TurnContext,
558
        _action: MessagingExtensionAction
559
    ): Promise<MessagingExtensionActionResponse> {
560
        throw new Error('NotImplemented');
2✔
561
    }
562

563
    /**
564
     * Receives invoke activities with the name 'composeExtension/querySettingUrl'
565
     *
566
     * @param _context A context object for this turn.
567
     * @param _query The Messaging extension query.
568
     * @returns The Messaging Extension Action Response for the query.
569
     */
570
    protected async handleTeamsMessagingExtensionConfigurationQuerySettingUrl(
571
        _context: TurnContext,
572
        _query: MessagingExtensionQuery
573
    ): Promise<MessagingExtensionResponse> {
574
        throw new Error('NotImplemented');
4✔
575
    }
576

577
    /**
578
     * Receives invoke activities with the name 'composeExtension/setting'
579
     *
580
     * @param _context A context object for this turn.
581
     * @param _settings Object representing the configuration settings.
582
     */
583
    protected handleTeamsMessagingExtensionConfigurationSetting(_context: TurnContext, _settings: any): Promise<void> {
584
        throw new Error('NotImplemented');
2✔
585
    }
586

587
    /**
588
     * Override this method to change the dispatching of ConversationUpdate activities.
589
     *
590
     * @param context A context object for this turn.
591
     * @returns A promise that represents the work queued.
592
     */
593
    protected async dispatchConversationUpdateActivity(context: TurnContext): Promise<void> {
594
        if (context.activity.channelId == 'msteams') {
42✔
595
            const channelData = context.activity.channelData as TeamsChannelData;
40✔
596

597
            if (context.activity.membersAdded && context.activity.membersAdded.length > 0) {
40✔
598
                return await this.onTeamsMembersAdded(context);
10✔
599
            }
600

601
            if (context.activity.membersRemoved && context.activity.membersRemoved.length > 0) {
30✔
602
                return await this.onTeamsMembersRemoved(context);
4✔
603
            }
604

605
            if (!channelData || !channelData.eventType) {
26✔
606
                return await super.dispatchConversationUpdateActivity(context);
4✔
607
            }
608

609
            switch (channelData.eventType) {
22✔
610
                case 'channelCreated':
22✔
611
                    return await this.onTeamsChannelCreated(context);
2✔
612

613
                case 'channelDeleted':
614
                    return await this.onTeamsChannelDeleted(context);
2✔
615

616
                case 'channelRenamed':
617
                    return await this.onTeamsChannelRenamed(context);
2✔
618

619
                case 'teamArchived':
620
                    return await this.onTeamsTeamArchived(context);
2✔
621

622
                case 'teamDeleted':
623
                    return await this.onTeamsTeamDeleted(context);
2✔
624

625
                case 'teamHardDeleted':
626
                    return await this.onTeamsTeamHardDeleted(context);
2✔
627

628
                case 'channelRestored':
629
                    return await this.onTeamsChannelRestored(context);
2✔
630

631
                case 'teamRenamed':
632
                    return await this.onTeamsTeamRenamed(context);
2✔
633

634
                case 'teamRestored':
635
                    return await this.onTeamsTeamRestored(context);
2✔
636

637
                case 'teamUnarchived':
638
                    return await this.onTeamsTeamUnarchived(context);
2✔
639

640
                default:
641
                    return await super.dispatchConversationUpdateActivity(context);
2✔
642
            }
643
        } else {
644
            return await super.dispatchConversationUpdateActivity(context);
2✔
645
        }
646
    }
647

648
    /**
649
     * Override this method to change the dispatching of MessageUpdate activities.
650
     *
651
     * @param context A context object for this turn.
652
     * @returns A promise that represents the work queued.
653
     */
654
    protected async dispatchMessageUpdateActivity(context: TurnContext): Promise<void> {
655
        if (context.activity.channelId == 'msteams') {
12✔
656
            const channelData = context.activity.channelData as TeamsChannelData;
10✔
657

658
            switch (channelData.eventType) {
10✔
659
                case 'undeleteMessage':
10✔
660
                    return await this.onTeamsMessageUndelete(context);
4✔
661

662
                case 'editMessage':
663
                    return await this.onTeamsMessageEdit(context);
4✔
664

665
                default:
666
                    return super.dispatchMessageUpdateActivity(context);
2✔
667
            }
668
        } else {
669
            return await super.dispatchMessageUpdateActivity(context);
2✔
670
        }
671
    }
672

673
    /**
674
     * Override this method to change the dispatching of MessageDelete activities.
675
     *
676
     * @param context A context object for this turn.
677
     * @returns A promise that represents the work queued.
678
     */
679
    protected async dispatchMessageDeleteActivity(context: TurnContext): Promise<void> {
680
        if (context.activity.channelId == 'msteams') {
8✔
681
            const channelData = context.activity.channelData as TeamsChannelData;
6✔
682

683
            switch (channelData.eventType) {
6✔
684
                case 'softDeleteMessage':
6✔
685
                    return await this.onTeamsMessageSoftDelete(context);
4✔
686

687
                default:
688
                    return super.dispatchMessageDeleteActivity(context);
2✔
689
            }
690
        } else {
691
            return await super.dispatchMessageDeleteActivity(context);
2✔
692
        }
693
    }
694

695
    /**
696
     * Called in `dispatchMessageUpdateActivity()` to trigger the `'TeamsMessageUndelete'` handlers.
697
     * Override this in a derived class to provide logic for when a deleted message in a conversation is undeleted.
698
     * For example, when the user decides to "undo" a deleted message.
699
     *
700
     * @param context A context object for this turn.
701
     * @returns A promise that represents the work queued.
702
     */
703
    protected async onTeamsMessageUndelete(context: TurnContext): Promise<void> {
704
        await this.handle(context, 'TeamsMessageUndelete', this.defaultNextEvent(context));
2✔
705
    }
706

707
    /**
708
     * Called in `dispatchMessageUpdateActivity()` to trigger the `'TeamsMessageEdit'` handlers.
709
     * Override this in a derived class to provide logic for when a message in a conversation is edited.
710
     *
711
     * @param context A context object for this turn.
712
     * @returns A promise that represents the work queued.
713
     */
714
    protected async onTeamsMessageEdit(context: TurnContext): Promise<void> {
715
        await this.handle(context, 'TeamsMessageEdit', this.defaultNextEvent(context));
2✔
716
    }
717

718
    /**
719
     * Called in `dispatchMessageDeleteActivity()` to trigger the `'TeamsMessageEdit'` handlers.
720
     * Override this in a derived class to provide logic for when a message in a conversation is soft deleted.
721
     * This means that the message as the option of being undeleted.
722
     *
723
     * @param context A context object for this turn.
724
     * @returns A promise that represents the work queued.
725
     */
726
    protected async onTeamsMessageSoftDelete(context: TurnContext): Promise<void> {
727
        await this.handle(context, 'onTeamsMessageSoftDelete', this.defaultNextEvent(context));
2✔
728
    }
729

730
    /**
731
     * Called in `dispatchConversationUpdateActivity()` to trigger the `'TeamsMembersAdded'` handlers.
732
     * Override this in a derived class to provide logic for when members other than the bot
733
     * join the channel, such as your bot's welcome logic.
734
     *
735
     * @remarks
736
     * If no handlers are registered for the `'TeamsMembersAdded'` event, the `'MembersAdded'` handlers will run instead.
737
     * @param context A context object for this turn.
738
     * @returns A promise that represents the work queued.
739
     */
740
    protected async onTeamsMembersAdded(context: TurnContext): Promise<void> {
741
        if ('TeamsMembersAdded' in this.handlers && this.handlers['TeamsMembersAdded'].length > 0) {
10✔
742
            if (!context.activity || !context.activity.membersAdded) {
8!
743
                throw new Error('OnTeamsMemberAdded: context.activity is undefined');
×
744
            }
745
            for (let i = 0; i < context.activity.membersAdded.length; i++) {
8✔
746
                const channelAccount = context.activity.membersAdded[i];
10✔
747

748
                // check whether we have a TeamChannelAccount, or the member is the bot
749
                if (
10✔
750
                    'givenName' in channelAccount ||
50✔
751
                    'surname' in channelAccount ||
752
                    'email' in channelAccount ||
753
                    'userPrincipalName' in channelAccount ||
754
                    context.activity.recipient.id === channelAccount.id
755
                ) {
756
                    // we must have a TeamsChannelAccount, or a bot so skip to the next one
757
                    continue;
2✔
758
                }
759

760
                try {
8✔
761
                    context.activity.membersAdded[i] = await TeamsInfo.getMember(context, channelAccount.id);
8✔
762
                } catch (err: any) {
763
                    const errCode: string = err.body && err.body.error && err.body.error.code;
4✔
764
                    if (errCode === 'ConversationNotFound') {
4✔
765
                        // unable to find the member added in ConversationUpdate Activity in the response from the getMember call
766
                        const teamsChannelAccount: TeamsChannelAccount = {
2✔
767
                            id: channelAccount.id,
768
                            name: channelAccount.name,
769
                            aadObjectId: channelAccount.aadObjectId,
770
                            role: channelAccount.role,
771
                        };
772

773
                        context.activity.membersAdded[i] = teamsChannelAccount;
2✔
774
                    } else {
775
                        throw err;
2✔
776
                    }
777
                }
778
            }
779

780
            await this.handle(context, 'TeamsMembersAdded', this.defaultNextEvent(context));
6✔
781
        } else {
782
            await this.handle(context, 'MembersAdded', this.defaultNextEvent(context));
2✔
783
        }
784
    }
785

786
    /**
787
     * Called in `dispatchConversationUpdateActivity()` to trigger the `'TeamsMembersRemoved'` handlers.
788
     * Override this in a derived class to provide logic for when members other than the bot
789
     * leave the channel, such as your bot's good-bye logic.
790
     *
791
     * @remarks
792
     * If no handlers are registered for the `'TeamsMembersRemoved'` event, the `'MembersRemoved'` handlers will run instead.
793
     * @param context A context object for this turn.
794
     * @returns A promise that represents the work queued.
795
     */
796
    protected async onTeamsMembersRemoved(context: TurnContext): Promise<void> {
797
        if ('TeamsMembersRemoved' in this.handlers && this.handlers['TeamsMembersRemoved'].length > 0) {
4✔
798
            await this.handle(context, 'TeamsMembersRemoved', this.defaultNextEvent(context));
2✔
799
        } else {
800
            await this.handle(context, 'MembersRemoved', this.defaultNextEvent(context));
2✔
801
        }
802
    }
803

804
    /**
805
     * Invoked when a Channel Created event activity is received from the connector.
806
     * Channel Created corresponds to the user creating a new channel.
807
     * Override this in a derived class to provide logic for when a channel is created.
808
     *
809
     * @param context A context object for this turn.
810
     * @returns A promise that represents the work queued.
811
     */
812
    protected async onTeamsChannelCreated(context: TurnContext): Promise<void> {
813
        await this.handle(context, 'TeamsChannelCreated', this.defaultNextEvent(context));
2✔
814
    }
815

816
    /**
817
     * Invoked when a Channel Deleted event activity is received from the connector.
818
     * Channel Deleted corresponds to the user deleting a channel.
819
     * Override this in a derived class to provide logic for when a channel is deleted.
820
     *
821
     * @param context A context object for this turn.
822
     * @returns A promise that represents the work queued.
823
     */
824
    protected async onTeamsChannelDeleted(context: TurnContext): Promise<void> {
825
        await this.handle(context, 'TeamsChannelDeleted', this.defaultNextEvent(context));
2✔
826
    }
827

828
    /**
829
     * Invoked when a Channel Renamed event activity is received from the connector.
830
     * Channel Renamed corresponds to the user renaming a new channel.
831
     * Override this in a derived class to provide logic for when a channel is renamed.
832
     *
833
     * @param context A context object for this turn.
834
     * @returns A promise that represents the work queued.
835
     */
836
    protected async onTeamsChannelRenamed(context: TurnContext): Promise<void> {
837
        await this.handle(context, 'TeamsChannelRenamed', this.defaultNextEvent(context));
2✔
838
    }
839

840
    /**
841
     * Invoked when a Team Archived event activity is received from the connector.
842
     * Team Archived corresponds to the user archiving a team.
843
     * Override this in a derived class to provide logic for when a team is archived.
844
     *
845
     * @param context The context for this turn.
846
     * @returns A promise that represents the work queued.
847
     */
848
    protected async onTeamsTeamArchived(context: TurnContext): Promise<void> {
849
        await this.handle(context, 'TeamsTeamArchived', this.defaultNextEvent(context));
2✔
850
    }
851

852
    /**
853
     * Invoked when a Team Deleted event activity is received from the connector.
854
     * Team Deleted corresponds to the user deleting a team.
855
     * Override this in a derived class to provide logic for when a team is deleted.
856
     *
857
     * @param context The context for this turn.
858
     * @returns A promise that represents the work queued.
859
     */
860
    protected async onTeamsTeamDeleted(context: TurnContext): Promise<void> {
861
        await this.handle(context, 'TeamsTeamDeleted', this.defaultNextEvent(context));
2✔
862
    }
863

864
    /**
865
     * Invoked when a Team Hard Deleted event activity is received from the connector.
866
     * Team Hard Deleted corresponds to the user hard-deleting a team.
867
     * Override this in a derived class to provide logic for when a team is hard-deleted.
868
     *
869
     * @param context The context for this turn.
870
     * @returns A promise that represents the work queued.
871
     */
872
    protected async onTeamsTeamHardDeleted(context: TurnContext): Promise<void> {
873
        await this.handle(context, 'TeamsTeamHardDeleted', this.defaultNextEvent(context));
2✔
874
    }
875

876
    /**
877
     *
878
     * Invoked when a Channel Restored event activity is received from the connector.
879
     * Channel Restored corresponds to the user restoring a previously deleted channel.
880
     * Override this in a derived class to provide logic for when a channel is restored.
881
     *
882
     * @param context The context for this turn.
883
     * @returns A promise that represents the work queued.
884
     */
885
    protected async onTeamsChannelRestored(context: TurnContext): Promise<void> {
886
        await this.handle(context, 'TeamsChannelRestored', this.defaultNextEvent(context));
2✔
887
    }
888

889
    /**
890
     * Invoked when a Team Renamed event activity is received from the connector.
891
     * Team Renamed corresponds to the user renaming a team.
892
     * Override this in a derived class to provide logic for when a team is renamed.
893
     *
894
     * @param context The context for this turn.
895
     * @returns A promise that represents the work queued.
896
     */
897
    protected async onTeamsTeamRenamed(context: TurnContext): Promise<void> {
898
        await this.handle(context, 'TeamsTeamRenamed', this.defaultNextEvent(context));
2✔
899
    }
900

901
    /**
902
     * Invoked when a Team Restored event activity is received from the connector.
903
     * Team Restored corresponds to the user restoring a team.
904
     * Override this in a derived class to provide logic for when a team is restored.
905
     *
906
     * @param context The context for this turn.
907
     * @returns A promise that represents the work queued.
908
     */
909
    protected async onTeamsTeamRestored(context: TurnContext): Promise<void> {
910
        await this.handle(context, 'TeamsTeamRestored', this.defaultNextEvent(context));
2✔
911
    }
912

913
    /**
914
     * Invoked when a Team Unarchived event activity is received from the connector.
915
     * Team Unarchived corresponds to the user unarchiving a team.
916
     * Override this in a derived class to provide logic for when a team is unarchived.
917
     *
918
     * @param context The context for this turn.
919
     * @returns A promise that represents the work queued.
920
     */
921
    protected async onTeamsTeamUnarchived(context: TurnContext): Promise<void> {
922
        await this.handle(context, 'TeamsTeamUnarchived', this.defaultNextEvent(context));
2✔
923
    }
924

925
    /**
926
     * Registers a handler for TeamsMessageUndelete events, such as for when a message in a conversation that is
927
     * observed by the bot goes from a soft delete state to the normal state.
928
     *
929
     * @param handler A callback to handle the teams undelete message event.
930
     * @returns A promise that represents the work queued.
931
     */
932
    onTeamsMessageUndeleteEvent(handler: (context: TurnContext, next: () => Promise<void>) => Promise<void>): this {
933
        return this.on('TeamsMessageUndelete', async (context, next) => {
2✔
934
            await handler(context, next);
2✔
935
        });
936
    }
937

938
    /**
939
     * Registers a handler for TeamsMessageEdit events, such as for when a message in a conversation that is
940
     * observed by the bot is edited.
941
     *
942
     * @param handler A callback to handle the teams edit message event.
943
     * @returns A promise that represents the work queued.
944
     */
945
    onTeamsMessageEditEvent(handler: (context: TurnContext, next: () => Promise<void>) => Promise<void>): this {
946
        return this.on('TeamsMessageEdit', async (context, next) => {
2✔
947
            await handler(context, next);
2✔
948
        });
949
    }
950

951
    /**
952
     * Registers a handler for TeamsMessageSoftDelete events, such as for when a message in a conversation that is
953
     * observed by the bot is soft deleted. This means that the deleted message, up to a certain time period,
954
     * can be undoed.
955
     *
956
     * @param handler A callback to handle the teams edit message event.
957
     * @returns A promise that represents the work queued.
958
     */
959
    onTeamsMessageSoftDeleteEvent(handler: (context: TurnContext, next: () => Promise<void>) => Promise<void>): this {
960
        return this.on('onTeamsMessageSoftDelete', async (context, next) => {
2✔
961
            await handler(context, next);
2✔
962
        });
963
    }
964

965
    /**
966
     * Registers a handler for TeamsMembersAdded events, such as for when members other than the bot
967
     * join the channel, such as your bot's welcome logic.
968
     *
969
     * @param handler A callback to handle the teams members added event.
970
     * @returns A promise that represents the work queued.
971
     */
972
    onTeamsMembersAddedEvent(
973
        handler: (
974
            membersAdded: TeamsChannelAccount[],
975
            teamInfo: TeamInfo,
976
            context: TurnContext,
977
            next: () => Promise<void>
978
        ) => Promise<void>
979
    ): this {
980
        return this.on('TeamsMembersAdded', async (context, next) => {
8✔
981
            const teamsChannelData = context.activity.channelData as TeamsChannelData;
6✔
982
            await handler(context.activity.membersAdded, teamsChannelData.team, context, next);
6✔
983
        });
984
    }
985

986
    /**
987
     * Registers a handler for TeamsMembersRemoved events, such as for when members other than the bot
988
     * leave the channel, such as your bot's good-bye logic.
989
     *
990
     * @param handler A callback to handle the teams members removed event.
991
     * @returns A promise that represents the work queued.
992
     */
993
    onTeamsMembersRemovedEvent(
994
        handler: (
995
            membersRemoved: TeamsChannelAccount[],
996
            teamInfo: TeamInfo,
997
            context: TurnContext,
998
            next: () => Promise<void>
999
        ) => Promise<void>
1000
    ): this {
1001
        return this.on('TeamsMembersRemoved', async (context, next) => {
2✔
1002
            const teamsChannelData = context.activity.channelData as TeamsChannelData;
2✔
1003
            await handler(context.activity.membersRemoved, teamsChannelData.team, context, next);
2✔
1004
        });
1005
    }
1006

1007
    /**
1008
     * Registers a handler for TeamsChannelCreated events, such as for when a channel is created.
1009
     *
1010
     * @param handler A callback to handle the teams channel created event.
1011
     * @returns A promise that represents the work queued.
1012
     */
1013
    onTeamsChannelCreatedEvent(
1014
        handler: (
1015
            channelInfo: ChannelInfo,
1016
            teamInfo: TeamInfo,
1017
            context: TurnContext,
1018
            next: () => Promise<void>
1019
        ) => Promise<void>
1020
    ): this {
1021
        return this.on('TeamsChannelCreated', async (context, next) => {
2✔
1022
            const teamsChannelData = context.activity.channelData as TeamsChannelData;
2✔
1023
            await handler(teamsChannelData.channel, teamsChannelData.team, context, next);
2✔
1024
        });
1025
    }
1026

1027
    /**
1028
     * Registers a handler for TeamsChannelDeleted events, such as for when a channel is deleted.
1029
     *
1030
     * @param handler A callback to handle the teams channel deleted event.
1031
     * @returns A promise that represents the work queued.
1032
     */
1033
    onTeamsChannelDeletedEvent(
1034
        handler: (
1035
            channelInfo: ChannelInfo,
1036
            teamInfo: TeamInfo,
1037
            context: TurnContext,
1038
            next: () => Promise<void>
1039
        ) => Promise<void>
1040
    ): this {
1041
        return this.on('TeamsChannelDeleted', async (context, next) => {
2✔
1042
            const teamsChannelData = context.activity.channelData as TeamsChannelData;
2✔
1043
            await handler(teamsChannelData.channel, teamsChannelData.team, context, next);
2✔
1044
        });
1045
    }
1046

1047
    /**
1048
     * Registers a handler for TeamsChannelRenamed events, such as for when a channel is renamed.
1049
     *
1050
     * @param handler A callback to handle the teams channel renamed event.
1051
     * @returns A promise that represents the work queued.
1052
     */
1053
    onTeamsChannelRenamedEvent(
1054
        handler: (
1055
            channelInfo: ChannelInfo,
1056
            teamInfo: TeamInfo,
1057
            context: TurnContext,
1058
            next: () => Promise<void>
1059
        ) => Promise<void>
1060
    ): this {
1061
        return this.on('TeamsChannelRenamed', async (context, next) => {
2✔
1062
            const teamsChannelData = context.activity.channelData as TeamsChannelData;
2✔
1063
            await handler(teamsChannelData.channel, teamsChannelData.team, context, next);
2✔
1064
        });
1065
    }
1066

1067
    /**
1068
     * Registers a handler for TeamsTeamArchived events, such as for when a team is archived.
1069
     *
1070
     * @param handler A callback to handle the teams team archived event.
1071
     * @returns A promise that represents the work queued.
1072
     */
1073
    onTeamsTeamArchivedEvent(
1074
        handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise<void>) => Promise<void>
1075
    ): this {
1076
        return this.on('TeamsTeamArchived', async (context, next) => {
2✔
1077
            const teamsChannelData = context.activity.channelData as TeamsChannelData;
2✔
1078
            await handler(teamsChannelData.team, context, next);
2✔
1079
        });
1080
    }
1081

1082
    /**
1083
     * Registers a handler for TeamsTeamDeleted events, such as for when a team is deleted.
1084
     *
1085
     * @param handler A callback to handle the teams team deleted event.
1086
     * @returns A promise that represents the work queued.
1087
     */
1088
    onTeamsTeamDeletedEvent(
1089
        handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise<void>) => Promise<void>
1090
    ): this {
1091
        return this.on('TeamsTeamDeleted', async (context, next) => {
2✔
1092
            const teamsChannelData = context.activity.channelData as TeamsChannelData;
2✔
1093
            await handler(teamsChannelData.team, context, next);
2✔
1094
        });
1095
    }
1096

1097
    /**
1098
     * Registers a handler for TeamsTeamHardDeleted events, such as for when a team is hard-deleted.
1099
     *
1100
     * @param handler A callback to handle the teams team hard deleted event.
1101
     * @returns A promise that represents the work queued.
1102
     */
1103
    onTeamsTeamHardDeletedEvent(
1104
        handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise<void>) => Promise<void>
1105
    ): this {
1106
        return this.on('TeamsTeamHardDeleted', async (context, next) => {
2✔
1107
            const teamsChannelData = context.activity.channelData as TeamsChannelData;
2✔
1108
            await handler(teamsChannelData.team, context, next);
2✔
1109
        });
1110
    }
1111

1112
    /**
1113
     * Registers a handler for TeamsChannelRestored events, such as for when a channel is restored.
1114
     *
1115
     * @param handler A callback to handle the teams channel restored event.
1116
     * @returns A promise that represents the work queued.
1117
     */
1118
    onTeamsChannelRestoredEvent(
1119
        handler: (
1120
            channelInfo: ChannelInfo,
1121
            teamInfo: TeamInfo,
1122
            context: TurnContext,
1123
            next: () => Promise<void>
1124
        ) => Promise<void>
1125
    ): this {
1126
        return this.on('TeamsChannelRestored', async (context, next) => {
2✔
1127
            const teamsChannelData = context.activity.channelData as TeamsChannelData;
2✔
1128
            await handler(teamsChannelData.channel, teamsChannelData.team, context, next);
2✔
1129
        });
1130
    }
1131

1132
    /**
1133
     * Registers a handler for TeamsTeamRenamed events, such as for when a team is renamed.
1134
     *
1135
     * @param handler A callback to handle the teams team renamed event.
1136
     * @returns A promise that represents the work queued.
1137
     */
1138
    onTeamsTeamRenamedEvent(
1139
        handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise<void>) => Promise<void>
1140
    ): this {
1141
        return this.on('TeamsTeamRenamed', async (context, next) => {
2✔
1142
            const teamsChannelData = context.activity.channelData as TeamsChannelData;
2✔
1143
            await handler(teamsChannelData.team, context, next);
2✔
1144
        });
1145
    }
1146

1147
    /**
1148
     * Registers a handler for TeamsTeamRestored events, such as for when a team is restored.
1149
     *
1150
     * @param handler A callback to handle the teams team restored event.
1151
     * @returns A promise that represents the work queued.
1152
     */
1153
    onTeamsTeamRestoredEvent(
1154
        handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise<void>) => Promise<void>
1155
    ): this {
1156
        return this.on('TeamsTeamRestored', async (context, next) => {
2✔
1157
            const teamsChannelData = context.activity.channelData as TeamsChannelData;
2✔
1158
            await handler(teamsChannelData.team, context, next);
2✔
1159
        });
1160
    }
1161

1162
    /**
1163
     * Registers a handler for TeamsTeamUnarchived events, such as for when a team is unarchived.
1164
     *
1165
     * @param handler A callback to handle the teams team unarchived event.
1166
     * @returns A promise that represents the work queued.
1167
     */
1168
    onTeamsTeamUnarchivedEvent(
1169
        handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise<void>) => Promise<void>
1170
    ): this {
1171
        return this.on('TeamsTeamUnarchived', async (context, next) => {
2✔
1172
            const teamsChannelData = context.activity.channelData as TeamsChannelData;
2✔
1173
            await handler(teamsChannelData.team, context, next);
2✔
1174
        });
1175
    }
1176

1177
    /**
1178
     * Runs the _event_ sub-type handlers, as appropriate, and then continues the event emission process.
1179
     *
1180
     * @param context The context object for the current turn.
1181
     * @returns A promise that represents the work queued.
1182
     *
1183
     * @remarks
1184
     * Override this method to support channel-specific behavior across multiple channels or to add
1185
     * custom event sub-type events.
1186
     */
1187
    protected async dispatchEventActivity(context: TurnContext): Promise<void> {
1188
        if (context.activity.channelId === Channels.Msteams) {
10✔
1189
            switch (context.activity.name) {
8✔
1190
                case 'application/vnd.microsoft.readReceipt':
8✔
1191
                    return this.onTeamsReadReceipt(context);
2✔
1192
                case 'application/vnd.microsoft.meetingStart':
1193
                    return this.onTeamsMeetingStart(context);
2✔
1194
                case 'application/vnd.microsoft.meetingEnd':
1195
                    return this.onTeamsMeetingEnd(context);
4✔
1196
            }
1197
        }
1198

1199
        return super.dispatchEventActivity(context);
2✔
1200
    }
1201

1202
    /**
1203
     * Invoked when a Meeting Started event activity is received from the connector.
1204
     * Override this in a derived class to provide logic for when a meeting is started.
1205
     *
1206
     * @param context The context for this turn.
1207
     * @returns A promise that represents the work queued.
1208
     */
1209
    protected async onTeamsMeetingStart(context: TurnContext): Promise<void> {
1210
        await this.handle(context, 'TeamsMeetingStart', this.defaultNextEvent(context));
2✔
1211
    }
1212

1213
    /**
1214
     * Invoked when a Meeting End event activity is received from the connector.
1215
     * Override this in a derived class to provide logic for when a meeting is ended.
1216
     *
1217
     * @param context The context for this turn.
1218
     * @returns A promise that represents the work queued.
1219
     */
1220
    protected async onTeamsMeetingEnd(context: TurnContext): Promise<void> {
1221
        await this.handle(context, 'TeamsMeetingEnd', this.defaultNextEvent(context));
4✔
1222
    }
1223

1224
    /**
1225
     * Invoked when a read receipt for a previously sent message is received from the connector.
1226
     * Override this in a derived class to provide logic for when the bot receives a read receipt event.
1227
     *
1228
     * @param context The context for this turn.
1229
     * @returns A promise that represents the work queued.
1230
     */
1231
    protected async onTeamsReadReceipt(context: TurnContext): Promise<void> {
1232
        await this.handle(context, 'TeamsReadReceipt', this.defaultNextEvent(context));
2✔
1233
    }
1234

1235
    /**
1236
     * Registers a handler for when a Teams meeting starts.
1237
     *
1238
     * @param handler A callback that handles Meeting Start events.
1239
     * @returns A promise that represents the work queued.
1240
     */
1241
    onTeamsMeetingStartEvent(
1242
        handler: (meeting: MeetingStartEventDetails, context: TurnContext, next: () => Promise<void>) => Promise<void>
1243
    ): this {
1244
        return this.on('TeamsMeetingStart', async (context, next) => {
2✔
1245
            const meeting = TeamsMeetingStartT.parse(context.activity.value);
2✔
1246
            await handler(
2✔
1247
                {
1248
                    id: meeting.Id,
1249
                    joinUrl: meeting.JoinUrl,
1250
                    meetingType: meeting.MeetingType,
1251
                    startTime: new Date(meeting.StartTime),
1252
                    title: meeting.Title,
1253
                },
1254
                context,
1255
                next
1256
            );
1257
        });
1258
    }
1259

1260
    /**
1261
     * Registers a handler for when a Teams meeting ends.
1262
     *
1263
     * @param handler A callback that handles Meeting End events.
1264
     * @returns A promise that represents the work queued.
1265
     */
1266
    onTeamsMeetingEndEvent(
1267
        handler: (meeting: MeetingEndEventDetails, context: TurnContext, next: () => Promise<void>) => Promise<void>
1268
    ): this {
1269
        return this.on('TeamsMeetingEnd', async (context, next) => {
2✔
1270
            const meeting = TeamsMeetingEndT.parse(context.activity.value);
2✔
1271
            await handler(
2✔
1272
                {
1273
                    id: meeting.Id,
1274
                    joinUrl: meeting.JoinUrl,
1275
                    meetingType: meeting.MeetingType,
1276
                    endTime: new Date(meeting.EndTime),
1277
                    title: meeting.Title,
1278
                },
1279
                context,
1280
                next
1281
            );
1282
        });
1283
    }
1284

1285
    /**
1286
     * Registers a handler for when a Read Receipt is sent.
1287
     *
1288
     * @param handler A callback that handles Read Receipt events.
1289
     * @returns A promise that represents the work queued.
1290
     */
1291
    onTeamsReadReceiptEvent(
1292
        handler: (receiptInfo: ReadReceiptInfo, context: TurnContext, next: () => Promise<void>) => Promise<void>
1293
    ): this {
1294
        return this.on('TeamsReadReceipt', async (context, next) => {
2✔
1295
            const receiptInfo = context.activity.value;
2✔
1296
            await handler(new ReadReceiptInfo(receiptInfo.lastReadMessageId), context, next);
2✔
1297
        });
1298
    }
1299
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc