BotToUser.cs
1 //
2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license.
4 //
5 // Microsoft Bot Framework: http://botframework.com
6 //
7 // Bot Builder SDK GitHub:
8 // https://github.com/Microsoft/BotBuilder
9 //
10 // Copyright (c) Microsoft Corporation
11 // All rights reserved.
12 //
13 // MIT License:
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 
34 using System;
36 using Microsoft.Bot.Connector;
37 using System.Collections.Generic;
38 
39 using System.IO;
40 using System.Linq;
41 using System.Text;
42 using System.Threading;
43 using System.Threading.Tasks;
45 
46 namespace Microsoft.Bot.Builder.Dialogs.Internals
47 {
51  public interface IBotToUser
52  {
59  Task PostAsync(IMessageActivity message, CancellationToken cancellationToken = default(CancellationToken));
60 
65  IMessageActivity MakeMessage();
66  }
67 
68  public sealed class NullBotToUser : IBotToUser
69  {
70  private readonly IMessageActivity toBot;
72  {
73  SetField.NotNull(out this.toBot, nameof(toBot), toBot);
74  }
75 
77  {
78  var toBotActivity = (Activity)this.toBot;
79  return toBotActivity.CreateReply();
80  }
81 
82  Task IBotToUser.PostAsync(IMessageActivity message, CancellationToken cancellationToken)
83  {
84  return Task.CompletedTask;
85  }
86  }
87 
88  public sealed class AlwaysSendDirect_BotToUser : IBotToUser
89  {
90  private readonly IMessageActivity toBot;
91  private readonly IConnectorClient client;
93  {
94  SetField.NotNull(out this.toBot, nameof(toBot), toBot);
95  SetField.NotNull(out this.client, nameof(client), client);
96  }
97 
99  {
100  var toBotActivity = (Activity)this.toBot;
101  return toBotActivity.CreateReply();
102  }
103 
104  async Task IBotToUser.PostAsync(IMessageActivity message, CancellationToken cancellationToken)
105  {
106  await this.client.Conversations.ReplyToActivityAsync((Activity)message, cancellationToken);
107  }
108  }
109 
110  public interface IMessageActivityMapper
111  {
112  IMessageActivity Map(IMessageActivity message);
113  }
114 
116  {
118  {
119  if (message.Attachments.Any())
120  {
121  var keyboards = message.Attachments.Where(t => t.ContentType == KeyboardCard.ContentType).ToList();
122  if (keyboards.Count > 1)
123  {
124  throw new ArgumentException("Each message can only have one keyboard card!");
125  }
126 
127  var keyboard = keyboards.FirstOrDefault();
128  if (keyboard != null)
129  {
130  message.Attachments.Remove(keyboard);
131  var keyboardCard = (KeyboardCard)keyboard.Content;
132  if (message.ChannelId == "facebook" && keyboardCard.Buttons.Count <= 10)
133  {
134  message.ChannelData = keyboardCard.ToFacebookMessage();
135  }
136  else
137  {
138  message.Attachments.Add(keyboardCard.ToHeroCard().ToAttachment());
139  }
140  }
141  }
142 
143  return message;
144  }
145  }
146 
147 
149  {
150  private readonly IBotToUser inner;
151  private readonly IEnumerable<IMessageActivityMapper> mappers;
152 
153  public MapToChannelData_BotToUser(IBotToUser inner, IEnumerable<IMessageActivityMapper> mappers)
154  {
155  SetField.NotNull(out this.inner, nameof(inner), inner);
156  SetField.NotNull(out this.mappers, nameof(mappers), mappers);
157  }
158 
159  public async Task PostAsync(IMessageActivity message, CancellationToken cancellationToken = new CancellationToken())
160  {
161  foreach (var mapper in mappers)
162  {
163  message = mapper.Map(message);
164  }
165  await this.inner.PostAsync(message, cancellationToken);
166  }
167 
169  {
170  return this.inner.MakeMessage();
171  }
172  }
173 
174  public sealed class BotToUserQueue : IBotToUser
175  {
176  private readonly IMessageActivity toBot;
177  private readonly Queue<IMessageActivity> queue;
178  public BotToUserQueue(IMessageActivity toBot, Queue<IMessageActivity> queue)
179  {
180  SetField.NotNull(out this.toBot, nameof(toBot), toBot);
181  SetField.NotNull(out this.queue, nameof(queue), queue);
182  }
183 
185  {
186  var toBotActivity = (Activity)this.toBot;
187  return toBotActivity.CreateReply();
188  }
189 
190  async Task IBotToUser.PostAsync(IMessageActivity message, CancellationToken cancellationToken)
191  {
192  this.queue.Enqueue(message);
193  }
194  }
195 
196  public sealed class BotToUserTextWriter : IBotToUser
197  {
198  private readonly IBotToUser inner;
199  private readonly TextWriter writer;
200  public BotToUserTextWriter(IBotToUser inner, TextWriter writer)
201  {
202  SetField.NotNull(out this.inner, nameof(inner), inner);
203  SetField.NotNull(out this.writer, nameof(writer), writer);
204  }
205 
207  {
208  return this.inner.MakeMessage();
209  }
210 
211  async Task IBotToUser.PostAsync(IMessageActivity message, CancellationToken cancellationToken)
212  {
213  await this.inner.PostAsync(message, cancellationToken);
214  await this.writer.WriteLineAsync($"{message.Text}{ButtonsToText(message.Attachments)}");
215  }
216 
217  private static string ButtonsToText(IList<Attachment> attachments)
218  {
219  var cardAttachments = attachments?.Where(attachment => attachment.ContentType.StartsWith("application/vnd.microsoft.card"));
220  var builder = new StringBuilder();
221  if (cardAttachments != null && cardAttachments.Any())
222  {
223  builder.AppendLine();
224  foreach (var attachment in cardAttachments)
225  {
226  string type = attachment.ContentType.Split('.').Last();
227  if (type == "hero" || type == "thumbnail")
228  {
229  var card = (HeroCard)attachment.Content;
230  if (!string.IsNullOrEmpty(card.Title))
231  {
232  builder.AppendLine(card.Title);
233  }
234  if (!string.IsNullOrEmpty(card.Subtitle))
235  {
236  builder.AppendLine(card.Subtitle);
237  }
238  if (!string.IsNullOrEmpty(card.Text))
239  {
240  builder.AppendLine(card.Text);
241  }
242  if (card.Buttons != null)
243  {
244  foreach (var button in card.Buttons)
245  {
246  builder.AppendLine($"* {button.Title}");
247  }
248  }
249  }
250  }
251  }
252  return builder.ToString();
253  }
254  }
255 }
Namespace for Microsoft Bot Builder SDK extensions to the Microsoft Bot Connector SDK...
Definition: KeyboardCard.cs:40
Namespace for the Microsoft Bot Connector SDK.
async Task PostAsync(IMessageActivity message, CancellationToken cancellationToken=new CancellationToken())
Post a message to be sent to the user.
Definition: BotToUser.cs:159
AlwaysSendDirect_BotToUser(IMessageActivity toBot, IConnectorClient client)
Definition: BotToUser.cs:92
MapToChannelData_BotToUser(IBotToUser inner, IEnumerable< IMessageActivityMapper > mappers)
Definition: BotToUser.cs:153
string ChannelId
Channel this activity is associated with
Definition: IActivity.cs:39
Namespace for internal machinery that is not useful for most developers and may change in the future...
BotToUserTextWriter(IBotToUser inner, TextWriter writer)
Definition: BotToUser.cs:200
dynamic ChannelData
Channel specific payload
Definition: IActivity.cs:77
Task PostAsync(IMessageActivity message, CancellationToken cancellationToken=default(CancellationToken))
Post a message to be sent to the user.
BotToUserQueue(IMessageActivity toBot, Queue< IMessageActivity > queue)
Definition: BotToUser.cs:178
const string ContentType
Content type of keyboard card for Attachment.ContentType.
Definition: KeyboardCard.cs:55
An Activity is the basic communication type for the Bot Framework 3.0 protocol
Definition: ActivityEx.cs:18
IList< Attachment > Attachments
Attachments
IMessageActivity Map(IMessageActivity message)
Definition: BotToUser.cs:117
IMessageActivity MakeMessage()
Make a message.
Namespace for the internal fibers machinery that is not useful for most developers and may change in ...
Methods to send a message from the bot to the user.
Definition: BotToUser.cs:51
A Hero card (card with a single, large image)
Root namespace for the Microsoft Bot Builder SDK.
´╗┐The Bot Connector REST API allows your bot to send and receive messages to channels configured in t...