-
Notifications
You must be signed in to change notification settings - Fork 920
Expand file tree
/
Copy pathActivityBot.cs
More file actions
216 lines (197 loc) · 9.96 KB
/
Copy pathActivityBot.cs
File metadata and controls
216 lines (197 loc) · 9.96 KB
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
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Concurrent;
using AdaptiveCards;
using BotDailyTaskReminder.Models;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Teams;
using Microsoft.Bot.Schema;
using Microsoft.Bot.Schema.Teams;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace BotDailyTaskReminder.Bots
{
/// <summary>
/// Handles incoming bot activities such as messages, task module fetch, and task module submission.
/// </summary>
public class ActivityBot : TeamsActivityHandler
{
private readonly string _applicationBaseUrl;
protected readonly BotState _conversationState;
private readonly ConcurrentDictionary<string, ConversationReference> _conversationReferences;
private readonly ConcurrentDictionary<string, List<SaveTaskDetail>> _taskDetails;
/// <summary>
/// Initializes a new instance of the <see cref="ActivityBot"/> class.
/// </summary>
public ActivityBot(IConfiguration configuration,
ConversationState conversationState,
ConcurrentDictionary<string, ConversationReference> conversationReferences,
ConcurrentDictionary<string, List<SaveTaskDetail>> taskDetails)
{
_conversationReferences = conversationReferences;
_conversationState = conversationState;
_taskDetails = taskDetails;
_applicationBaseUrl = configuration["ApplicationBaseUrl"] ?? throw new NullReferenceException("ApplicationBaseUrl");
}
/// <summary>
/// Handles when a message is addressed to the bot.
/// </summary>
/// <param name="turnContext">The turn context of the message activity.</param>
/// <param name="cancellationToken">Cancellation token for the asynchronous task.</param>
/// <returns>A task that represents the work queued to execute.</returns>
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
if (turnContext.Activity.Text.ToLower().Trim() == "create-reminder")
{
// Adds the current conversation reference and sends the task scheduling adaptive card
AddConversationReference(turnContext.Activity as Activity);
await turnContext.SendActivityAsync(MessageFactory.Attachment(GetAdaptiveCardForTaskModule()), cancellationToken);
}
}
/// <summary>
/// Handles the completion of a turn, saving any state changes.
/// </summary>
/// <param name="turnContext">The context of the current turn.</param>
/// <param name="cancellationToken">Cancellation token for the asynchronous task.</param>
/// <returns>A task that represents the work queued to execute.</returns>
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
{
await base.OnTurnAsync(turnContext, cancellationToken);
// Save any changes made to conversation state during this turn.
await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
}
/// <summary>
/// Invoked when the bot is added to a conversation.
/// Sends a welcome message to new members.
/// </summary>
/// <param name="membersAdded">The members added to the conversation.</param>
/// <param name="turnContext">The context of the current turn.</param>
/// <param name="cancellationToken">Cancellation token for the asynchronous task.</param>
/// <returns>A task that represents the work queued to execute.</returns>
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
foreach (var member in turnContext.Activity.MembersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
// Sends a greeting message when a new user is added
await turnContext.SendActivityAsync(MessageFactory.Text("Hello and welcome! Use the command 'create-reminder' to schedule a recurring task and receive reminders."), cancellationToken);
}
}
}
/// <summary>
/// Handles task module fetch requests.
/// </summary>
/// <param name="turnContext">The context of the turn.</param>
/// <param name="taskModuleRequest">The request payload for the task module.</param>
/// <param name="cancellationToken">Cancellation token for the asynchronous task.</param>
/// <returns>The task module response to send back.</returns>
protected override Task<TaskModuleResponse> OnTeamsTaskModuleFetchAsync(ITurnContext<IInvokeActivity> turnContext, TaskModuleRequest taskModuleRequest, CancellationToken cancellationToken)
{
var asJobject = JObject.FromObject(taskModuleRequest.Data);
var buttonType = (string)asJobject.ToObject<CardTaskFetchValue>()?.Id;
var taskModuleResponse = new TaskModuleResponse();
if (buttonType == "schedule")
{
taskModuleResponse.Task = new TaskModuleContinueResponse
{
Type = "continue",
Value = new TaskModuleTaskInfo
{
Url = _applicationBaseUrl + "/ScheduleTask",
Height = 450,
Width = 450,
Title = "Schedule a task",
},
};
}
return Task.FromResult(taskModuleResponse);
}
/// <summary>
/// Handles task module submission requests.
/// </summary>
/// <param name="turnContext">The context of the turn.</param>
/// <param name="taskModuleRequest">The request payload for the task module.</param>
/// <param name="cancellationToken">Cancellation token for the asynchronous task.</param>
/// <returns>The task module response to send back.</returns>
protected override async Task<TaskModuleResponse> OnTeamsTaskModuleSubmitAsync(ITurnContext<IInvokeActivity> turnContext, TaskModuleRequest taskModuleRequest, CancellationToken cancellationToken)
{
var asJobject = JObject.FromObject(taskModuleRequest.Data);
var taskData = asJobject.ToObject<TaskDetails>();
var title = (string)taskData?.Title;
var description = (string)taskData?.Description;
var dateTime = (DateTime)taskData?.DateTime;
var selectedDaysObject = (JArray)taskData?.SelectedDays;
var selectedDays = selectedDaysObject.ToObject<DayOfWeek[]>();
var date = dateTime.ToLocalTime();
// Prepare task details
var taskDetails = new SaveTaskDetail
{
Description = description,
Title = title,
DateTime = new DateTimeOffset(date.Year, date.Month, date.Day, date.Hour, date.Minute, 0, TimeSpan.Zero),
SelectedDays = selectedDays
};
// Add the task to the task list
_taskDetails.AddOrUpdate("taskDetails", new List<SaveTaskDetail> { taskDetails }, (key, currentTaskList) =>
{
currentTaskList.Add(taskDetails);
return currentTaskList;
});
// Schedule the task
var taskScheduler = new TaskScheduler();
taskScheduler.Start(date.Hour, date.Minute, _applicationBaseUrl, selectedDays);
// Send a success message to the user
await turnContext.SendActivityAsync("Task submitted successfully, you will get a recurring reminder for the task at a scheduled time");
return null;
}
/// <summary>
/// Creates and returns the adaptive card for scheduling a task.
/// </summary>
/// <returns>The adaptive card as an attachment.</returns>
private Attachment GetAdaptiveCardForTaskModule()
{
var card = new AdaptiveCard(new AdaptiveSchemaVersion("1.2"))
{
Body = new List<AdaptiveElement>
{
new AdaptiveTextBlock
{
Text = "Please click here to schedule a recurring task reminder",
Weight = AdaptiveTextWeight.Bolder,
Spacing = AdaptiveSpacing.Medium,
}
},
Actions = new List<AdaptiveAction>
{
new AdaptiveSubmitAction
{
Title = "Schedule task",
Data = new AdaptiveCardAction
{
MsteamsCardAction = new CardAction
{
Type = "task/fetch",
},
Id = "schedule"
},
}
},
};
return new Attachment
{
ContentType = AdaptiveCard.ContentType,
Content = JsonConvert.DeserializeObject(card.ToJson()),
};
}
/// <summary>
/// Adds a conversation reference for the current activity.
/// </summary>
/// <param name="activity">The bot activity.</param>
private void AddConversationReference(Activity activity)
{
var conversationReference = activity.GetConversationReference();
_conversationReferences.AddOrUpdate(conversationReference.User.Id, conversationReference, (key, newValue) => conversationReference);
}
}
}