-
Notifications
You must be signed in to change notification settings - Fork 726
/
TeamsConversationBot.cs
515 lines (457 loc) · 23.9 KB
/
TeamsConversationBot.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Teams;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.Bot.Schema.Teams;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json.Linq;
using AdaptiveCards.Templating;
using Newtonsoft.Json;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using System.Collections.Concurrent;
using System.Collections;
namespace Microsoft.BotBuilderSamples.Bots
{
public class TeamsConversationBot : TeamsActivityHandler
{
private string _appId;
private string _appPassword;
private static int _counter = 0;
private static List<string> users = new List<string>();
private static ConcurrentDictionary<string, TeamsChannelAccount> teamMemberDetails = new ConcurrentDictionary<string, TeamsChannelAccount>();
private static ConcurrentDictionary<string, string> teamMemberMessageIdDetails = new ConcurrentDictionary<string, string>();
public TeamsConversationBot(IConfiguration config)
{
_appId = config["MicrosoftAppId"];
_appPassword = config["MicrosoftAppPassword"];
}
private readonly string _adaptiveCardTemplate = Path.Combine(".", "Resources", "UserMentionCardTemplate.json");
private readonly string _immersiveReaderCardTemplate = Path.Combine(".", "Resources", "ImmersiveReaderCard.json");
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
turnContext.Activity.RemoveRecipientMention();
var text = turnContext.Activity.Text.Trim().ToLower();
if (text.Contains("mention me"))
await MentionAdaptiveCardActivityAsync(turnContext, cancellationToken);
else if (text.Contains("mention"))
await MentionActivityAsync(turnContext, cancellationToken);
else if (text.Contains("who"))
await GetSingleMemberAsync(turnContext, cancellationToken);
else if (text.Contains("update"))
await CardActivityAsync(turnContext, true, cancellationToken);
else if (text.Contains("aadid"))
await MessageAllMembersAsync(turnContext, cancellationToken, true);
else if (text.Contains("message"))
await MessageAllMembersAsync(turnContext, cancellationToken, false);
else if (text.Contains("immersivereader"))
await SendImmersiveReaderCardAsync(turnContext, cancellationToken);
else if (text.Contains("delete"))
await DeleteCardActivityAsync(turnContext, cancellationToken);
else if (text.Contains("check"))
await CheckReadUserCount(turnContext, cancellationToken);
else if (text.Contains("reset"))
await ResetReadUserCount(turnContext, cancellationToken);
else
await CardActivityAsync(turnContext, false, cancellationToken);
}
protected override async Task OnTeamsMembersAddedAsync(IList<TeamsChannelAccount> membersAdded, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
foreach (var teamMember in membersAdded)
{
if (teamMember.Id != turnContext.Activity.Recipient.Id && turnContext.Activity.Conversation.ConversationType != "personal")
{
await turnContext.SendActivityAsync(MessageFactory.Text($"Welcome to the team {teamMember.GivenName} {teamMember.Surname}."), cancellationToken);
}
}
}
/// <summary>
/// Checks the count of members who have read the message sent by MessageAllMembers command
/// </summary>
/// <param name="turnContext"></param>
/// <param name="cancellationToken"></param>
/// <returns>A task that represents the work queued to execute.</returns>
private async Task CheckReadUserCount(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
if (teamMemberDetails.Count != 0)
{
var userList = string.Join(", ", users);
await turnContext.SendActivityAsync(MessageFactory.Text($"Number of members read the message : {_counter} \n\n Members : {userList}"), cancellationToken);
}
else
{
await turnContext.SendActivityAsync(MessageFactory.Text("Read count is zero. Please make sure to send message to all members firstly to check the count of members who have read your message."), cancellationToken);
}
}
/// <summary>
/// Resets the check count of members who have read the message sent by MessageAllMembers command
/// </summary>
/// <param name="turnContext"></param>
/// <param name="cancellationToken"></param>
/// <returns>A task that represents the work queued to execute.</returns>
private async Task ResetReadUserCount(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
teamMemberDetails = new ConcurrentDictionary<string, TeamsChannelAccount>();
_counter = 0;
users = new List<string>();
teamMemberMessageIdDetails = new ConcurrentDictionary<string, string>();
}
/// <summary>
/// Invoked when user read the message sent by bot in personal scope
/// </summary>
/// <param name="turnContext"></param>
/// <param name="cancellationToken"></param>
/// <returns>A task that represents the work queued to execute.</returns>
protected override async Task OnTeamsReadReceiptAsync(ReadReceiptInfo readReceiptInfo, ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
{
teamMemberMessageIdDetails.TryGetValue(turnContext.Activity.From.AadObjectId, out string messageId);
if (readReceiptInfo.IsMessageRead(messageId))
{
teamMemberDetails.TryGetValue(turnContext.Activity.From.AadObjectId, out TeamsChannelAccount memberDetails);
_counter++;
users.Add(memberDetails.Name);
teamMemberMessageIdDetails.TryRemove(turnContext.Activity.From.AadObjectId, out string valueRemoved);
}
}
protected override async Task OnInstallationUpdateActivityAsync(ITurnContext<IInstallationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
if (turnContext.Activity.Conversation.ConversationType == "channel")
{
await turnContext.SendActivityAsync($"Welcome to Microsoft Teams conversationUpdate events demo bot. This bot is configured in {turnContext.Activity.Conversation.Name}");
}
else
{
await turnContext.SendActivityAsync("Welcome to Microsoft Teams conversationUpdate events demo bot.");
}
}
private async Task CardActivityAsync(ITurnContext<IMessageActivity> turnContext, bool update, CancellationToken cancellationToken)
{
var card = new HeroCard
{
Buttons = new List<CardAction>
{
new CardAction
{
Type = ActionTypes.MessageBack,
Title = "Message all members",
Text = "MessageAllMembers"
},
new CardAction
{
Type = ActionTypes.MessageBack,
Title = "Message all members using AADId",
Text = "MessageAllMembersUsingAADId"
},
new CardAction
{
Type = ActionTypes.MessageBack,
Title = "Who am I?",
Text = "whoami"
},
new CardAction
{
Type = ActionTypes.MessageBack,
Title = "Send Immersive Reader Card",
Text = "ImmersiveReader"
},
new CardAction
{
Type = ActionTypes.MessageBack,
Title = "Find me in Adaptive Card",
Text = "mention me"
},
new CardAction
{
Type = ActionTypes.MessageBack,
Title = "Delete card",
Text = "Delete"
},
new CardAction
{
Type = ActionTypes.MessageBack,
Title = "Check read count",
Text = "check"
},
new CardAction
{
Type = ActionTypes.MessageBack,
Title = "Reset read count",
Text = "reset"
}
}
};
if (update)
{
await SendUpdatedCard(turnContext, card, cancellationToken);
}
else
{
await SendWelcomeCard(turnContext, card, cancellationToken);
}
}
private async Task GetSingleMemberAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
var member = new TeamsChannelAccount();
try
{
member = await TeamsInfo.GetMemberAsync(turnContext, turnContext.Activity.From.Id, cancellationToken);
}
catch (ErrorResponseException e)
{
if (e.Body.Error.Code.Equals("MemberNotFoundInConversation", StringComparison.OrdinalIgnoreCase))
{
await turnContext.SendActivityAsync("Member not found.");
return;
}
else
{
throw e;
}
}
var message = MessageFactory.Text($"You are: {member.Name}.");
var res = await turnContext.SendActivityAsync(message);
}
private async Task DeleteCardActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
await turnContext.DeleteActivityAsync(turnContext.Activity.ReplyToId, cancellationToken);
}
/// <summary>
/// Using proactive messaging, messages sent to all members of the chat.
/// </summary>
/// <param name="turnContext"></param>
/// <param name="cancellationToken"></param>
/// <param name="isAadId"></param>
/// <returns>A task that represents the work queued to execute.</returns>
private async Task MessageAllMembersAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken, bool isAadId)
{
await ResetReadUserCount(turnContext, cancellationToken);
var teamsChannelId = turnContext.Activity.TeamsGetChannelId();
var serviceUrl = turnContext.Activity.ServiceUrl;
var credentials = new MicrosoftAppCredentials(_appId, _appPassword);
ConversationReference conversationReference = null;
var members = await GetPagedMembers(turnContext, cancellationToken);
foreach (var teamMember in members)
{
var proactiveMessage = MessageFactory.Text($"Hello {teamMember.GivenName} {teamMember.Surname}. I'm a Teams conversation bot.");
var conversationParameters = new ConversationParameters
{
IsGroup = false,
Bot = turnContext.Activity.Recipient,
Members = isAadId ? new ChannelAccount[] { new ChannelAccount(teamMember.AadObjectId) } : new ChannelAccount[] { teamMember },
TenantId = turnContext.Activity.Conversation.TenantId,
};
try
{
await ((CloudAdapter)turnContext.Adapter).CreateConversationAsync(
credentials.MicrosoftAppId,
teamsChannelId,
serviceUrl,
credentials.OAuthScope,
conversationParameters,
async (t1, c1) =>
{
conversationReference = t1.Activity.GetConversationReference();
await ((CloudAdapter)turnContext.Adapter).ContinueConversationAsync(
_appId,
conversationReference,
async (t2, c2) =>
{
var message = await t2.SendActivityAsync(proactiveMessage, c2);
teamMemberDetails.TryAdd(teamMember.AadObjectId, teamMember);
teamMemberMessageIdDetails.TryAdd(teamMember.AadObjectId, message.Id);
},
cancellationToken);
},
cancellationToken);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
await turnContext.SendActivityAsync(MessageFactory.Text($"All messages have been sent to {teamMemberDetails.Count} members."), cancellationToken);
}
private static async Task<List<TeamsChannelAccount>> GetPagedMembers(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
var members = new List<TeamsChannelAccount>();
string continuationToken = null;
do
{
var currentPage = await TeamsInfo.GetPagedMembersAsync(turnContext, 100, continuationToken, cancellationToken);
continuationToken = currentPage.ContinuationToken;
members = members.Concat(currentPage.Members).ToList();
}
while (continuationToken != null);
return members;
}
private static async Task SendWelcomeCard(ITurnContext<IMessageActivity> turnContext, HeroCard card, CancellationToken cancellationToken)
{
var initialValue = new JObject { { "count", 0 } };
card.Title = "Welcome!";
card.Buttons.Add(new CardAction
{
Type = ActionTypes.MessageBack,
Title = "Update Card",
Text = "UpdateCardAction",
Value = initialValue
});
var activity = MessageFactory.Attachment(card.ToAttachment());
await turnContext.SendActivityAsync(activity, cancellationToken);
}
private static async Task SendUpdatedCard(ITurnContext<IMessageActivity> turnContext, HeroCard card, CancellationToken cancellationToken)
{
card.Title = "I've been updated";
var data = turnContext.Activity.Value as JObject;
data = JObject.FromObject(data);
data["count"] = data["count"].Value<int>() + 1;
card.Text = $"Update count - {data["count"].Value<int>()}";
card.Buttons.Add(new CardAction
{
Type = ActionTypes.MessageBack,
Title = "Update Card",
Text = "UpdateCardAction",
Value = data
});
var activity = MessageFactory.Attachment(card.ToAttachment());
activity.Id = turnContext.Activity.ReplyToId;
await turnContext.UpdateActivityAsync(activity, cancellationToken);
}
private async Task MentionAdaptiveCardActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
var member = new TeamsChannelAccount();
try
{
member = await TeamsInfo.GetMemberAsync(turnContext, turnContext.Activity.From.Id, cancellationToken);
}
catch (ErrorResponseException e)
{
if (e.Body.Error.Code.Equals("MemberNotFoundInConversation", StringComparison.OrdinalIgnoreCase))
{
await turnContext.SendActivityAsync("Member not found.");
return;
}
else
{
throw e;
}
}
var templateJSON = File.ReadAllText(_adaptiveCardTemplate);
AdaptiveCardTemplate template = new AdaptiveCardTemplate(templateJSON);
var memberData = new
{
userName = member.Name,
userUPN = member.UserPrincipalName,
userAAD = member.AadObjectId
};
string cardJSON = template.Expand(memberData);
var adaptiveCardAttachment = new Attachment
{
ContentType = "application/vnd.microsoft.card.adaptive",
Content = JsonConvert.DeserializeObject(cardJSON),
};
await turnContext.SendActivityAsync(MessageFactory.Attachment(adaptiveCardAttachment), cancellationToken);
}
private async Task SendImmersiveReaderCardAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
var cardJSON = File.ReadAllText(_immersiveReaderCardTemplate);
var adaptiveCardAttachment = new Attachment
{
ContentType = "application/vnd.microsoft.card.adaptive",
Content = JsonConvert.DeserializeObject(cardJSON),
};
await turnContext.SendActivityAsync(MessageFactory.Attachment(adaptiveCardAttachment), cancellationToken);
}
private async Task MentionActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
var mention = new Mention
{
Mentioned = turnContext.Activity.From,
Text = $"<at>{XmlConvert.EncodeName(turnContext.Activity.From.Name)}</at>",
};
var replyActivity = MessageFactory.Text($"Hello {mention.Text}.");
replyActivity.Entities = new List<Entity> { mention };
await turnContext.SendActivityAsync(replyActivity, cancellationToken);
}
//-----Subscribe to Conversation Events in Bot integration
protected override async Task OnTeamsChannelCreatedAsync(ChannelInfo channelInfo, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var heroCard = new HeroCard(text: $"{channelInfo.Name} is the Channel created");
await turnContext.SendActivityAsync(MessageFactory.Attachment(heroCard.ToAttachment()), cancellationToken);
}
protected override async Task OnTeamsChannelRenamedAsync(ChannelInfo channelInfo, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var heroCard = new HeroCard(text: $"{channelInfo.Name} is the new Channel name");
await turnContext.SendActivityAsync(MessageFactory.Attachment(heroCard.ToAttachment()), cancellationToken);
}
protected override async Task OnTeamsChannelDeletedAsync(ChannelInfo channelInfo, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var heroCard = new HeroCard(text: $"{channelInfo.Name} is the Channel deleted");
await turnContext.SendActivityAsync(MessageFactory.Attachment(heroCard.ToAttachment()), cancellationToken);
}
protected override async Task OnTeamsMembersRemovedAsync(IList<TeamsChannelAccount> membersRemoved, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
foreach (TeamsChannelAccount member in membersRemoved)
{
if (member.Id == turnContext.Activity.Recipient.Id)
{
// The bot was removed
// You should clear any cached data you have for this team
}
else
{
var heroCard = new HeroCard(text: $"{member.Name} was removed from {teamInfo.Name}");
await turnContext.SendActivityAsync(MessageFactory.Attachment(heroCard.ToAttachment()), cancellationToken);
}
}
}
protected override async Task OnTeamsTeamRenamedAsync(TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var heroCard = new HeroCard(text: $"{teamInfo.Name} is the new Team name");
await turnContext.SendActivityAsync(MessageFactory.Attachment(heroCard.ToAttachment()), cancellationToken);
}
protected override async Task OnReactionsAddedAsync(IList<MessageReaction> messageReactions, ITurnContext<IMessageReactionActivity> turnContext, CancellationToken cancellationToken)
{
foreach (var reaction in messageReactions)
{
var newReaction = $"You reacted with '{reaction.Type}' to the following message: '{turnContext.Activity.ReplyToId}'";
var replyActivity = MessageFactory.Text(newReaction);
await turnContext.SendActivityAsync(replyActivity, cancellationToken);
}
}
protected override async Task OnReactionsRemovedAsync(IList<MessageReaction> messageReactions, ITurnContext<IMessageReactionActivity> turnContext, CancellationToken cancellationToken)
{
foreach (var reaction in messageReactions)
{
var newReaction = $"You removed the reaction '{reaction.Type}' from the following message: '{turnContext.Activity.ReplyToId}'";
var replyActivity = MessageFactory.Text(newReaction);
await turnContext.SendActivityAsync(replyActivity, cancellationToken);
}
}
// This method is invoked when message sent by user is updated in chat.
protected override async Task OnTeamsMessageEditAsync(ITurnContext<IMessageUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var replyActivity = MessageFactory.Text("Message is updated");
await turnContext.SendActivityAsync(replyActivity, cancellationToken);
}
// This method is invoked when message sent by user is undeleted in chat.
protected override async Task OnTeamsMessageUndeleteAsync(ITurnContext<IMessageUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var replyActivity = MessageFactory.Text("Message is undeleted");
await turnContext.SendActivityAsync(replyActivity, cancellationToken);
}
// This method is invoked when message sent by user is soft deleted in chat.
protected override async Task OnTeamsMessageSoftDeleteAsync(ITurnContext<IMessageDeleteActivity> turnContext, CancellationToken cancellationToken)
{
var replyActivity = MessageFactory.Text("Message is soft deleted");
await turnContext.SendActivityAsync(replyActivity, cancellationToken);
}
}
}