forked from microsoft/BotBuilder-Samples
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ConsoleAdapter.cs
206 lines (183 loc) · 9.25 KB
/
ConsoleAdapter.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
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;
namespace Console_EchoBot
{
/// <summary>
/// Represents a <see cref="BotAdapter"/> that would typically connect a bot to an
/// and external service (For example, Skype, Slack, Teams, etc).
/// This implementation interacts with the console.
/// </summary>
/// <seealso cref="ITurnContext"/>
/// <seealso cref="IActivity"/>
/// <seealso cref="IBot"/>
/// <seealso cref="IMiddleware"/>
public class ConsoleAdapter : BotAdapter
{
/// <summary>
/// Initializes a new instance of the <see cref="ConsoleAdapter"/> class.
/// </summary>
public ConsoleAdapter()
: base()
{
}
/// <summary>
/// Adds middleware to the adapter's pipeline.
/// </summary>
/// <param name="middleware">The <see cref="IMiddleware"/> component to add.</param>
/// <returns>The updated adapter object.</returns>
public new ConsoleAdapter Use(IMiddleware middleware)
{
base.Use(middleware);
return this;
}
/// <summary>
/// Performs the actual translation of input coming from the console
/// into the <see cref="Activity"/> format that the Bot consumes.
/// </summary>
/// <param name="callback">A <see cref="BotCallbackHandler"/> method to run at the end of the pipeline.</param>
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
/// <seealso cref="https://docs.microsoft.com/en-us/azure/bot-service/bot-service-resources-identifiers-guide?view=azure-bot-service-4.0"/>
public async Task ProcessActivityAsync(BotCallbackHandler callback = null)
{
while (true)
{
var msg = Console.ReadLine();
if (msg == null)
{
break;
}
// Performing the conversion from console text to an Activity for
// which the system handles all messages (from all unique services).
// All processing is performed by the broader bot pipeline on the Activity
// object.
var activity = new Activity()
{
Text = msg,
// Note on ChannelId:
// The Bot Framework channel is identified by a unique ID.
// For example, "skype" is a common channel to represent the Skype service.
// We are inventing a new channel here.
ChannelId = "console",
From = new ChannelAccount(id: "user", name: "User1"),
Recipient = new ChannelAccount(id: "bot", name: "Bot"),
Conversation = new ConversationAccount(id: "Convo1"),
Timestamp = DateTime.UtcNow,
Id = Guid.NewGuid().ToString(),
Type = ActivityTypes.Message,
};
using (var context = new TurnContext(this, activity))
{
await this.RunPipelineAsync(context, callback, default(CancellationToken)).ConfigureAwait(false);
}
}
}
/// <summary>
/// Sends activities to the conversation.
/// </summary>
/// <param name="context">The <see cref="ITurnContext"/> object for the turn.</param>
/// <param name="activities">The array of <see cref="Activity"/> objects to send.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>If the activities are successfully sent, the task result contains
/// an array of <see cref="ResourceResponse"/> objects containing the IDs that
/// the receiving channel assigned to the activities.</remarks>
/// <seealso cref="IActivity"/>
/// <seealso cref="ITurnContext.OnSendActivities(SendActivitiesHandler)"/>
public override async Task<ResourceResponse[]> SendActivitiesAsync(ITurnContext context, Activity[] activities, CancellationToken cancellationToken)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (activities == null)
{
throw new ArgumentNullException(nameof(activities));
}
if (activities.Length == 0)
{
throw new ArgumentException("Expecting one or more activities, but the array was empty.", nameof(activities));
}
var responses = new ResourceResponse[activities.Length];
for (var index = 0; index < activities.Length; index++)
{
var activity = activities[index];
switch (activity.Type)
{
case ActivityTypes.Message:
{
IMessageActivity message = activity.AsMessageActivity();
// A message exchange between user and bot can contain media attachments
// (e.g., image, video, audio, file). In this particular example, we are unable
// to create Attachments to messages, but this illustrates processing.
if (message.Attachments != null && message.Attachments.Any())
{
var attachment = message.Attachments.Count == 1 ? "1 attachment" : $"{message.Attachments.Count()} attachments";
Console.WriteLine($"{message.Text} with {attachment} ");
}
else
{
Console.WriteLine($"{message.Text}");
}
}
break;
case ActivityTypesEx.Delay:
{
// The Activity Schema doesn't have a delay type build in, so it's simulated
// here in the Bot. This matches the behavior in the Node connector.
int delayMs = (int)((Activity)activity).Value;
await Task.Delay(delayMs).ConfigureAwait(false);
}
break;
case ActivityTypes.Trace:
// Do not send trace activities unless you know that the client needs them.
// For example: BF protocol only sends Trace Activity when talking to emulator channel.
break;
default:
Console.WriteLine("Bot: activity type: {0}", activity.Type);
break;
}
responses[index] = new ResourceResponse(activity.Id);
}
return responses;
}
/// <summary>
/// Normally, replaces an existing activity in the conversation.
/// Not implemented for this sample.
/// </summary>
/// <param name="turnContext">The context object for the turn.</param>
/// <param name="activity">New replacement activity.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>If the activity is successfully sent, the task result contains
/// a <see cref="ResourceResponse"/> object containing the ID that the receiving
/// channel assigned to the activity.
/// <para>Before calling this, set the ID of the replacement activity to the ID
/// of the activity to replace.</para></remarks>
/// <seealso cref="ITurnContext.OnUpdateActivityAsync(UpdateActivityHandler)"/>
public override Task<ResourceResponse> UpdateActivityAsync(ITurnContext turnContext, Activity activity, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
/// <summary>
/// Deletes an existing activity in the conversation.
/// Not implemented for this sample.
/// </summary>
/// <param name="turnContext">The context object for the turn.</param>
/// <param name="reference">Conversation reference for the activity to delete.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
public override Task DeleteActivityAsync(ITurnContext turnContext, ConversationReference reference, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
}