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

microsoft / botbuilder-dotnet / 351887

16 May 2023 06:37PM UTC coverage: 78.983% (-0.03%) from 79.011%
351887

push

CI-PR build

Microsoft.VisualStudio.Services.TFS
[#6086] Teams is adding support for suggested actions in 1-1 chats (#6607)

25915 of 32811 relevant lines covered (78.98%)

0.79 hits per line

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

87.23
/libraries/Microsoft.Bot.Builder/MessageFactory.cs
1
// Copyright (c) Microsoft Corporation. All rights reserved.
2
// Licensed under the MIT License.
3

4
using System;
5
using System.Collections.Generic;
6
using System.Linq;
7
using Microsoft.Bot.Schema;
8

9
namespace Microsoft.Bot.Builder
10
{
11
    /// <summary>
12
    /// Contains utility methods for various message types a bot can return.
13
    /// </summary>
14
    /// <example>
15
    /// <code>
16
    /// // Create and send a message.
17
    /// var message = MessageFactory.Text("Hello World");
18
    /// await context.SendActivity(message);
19
    /// </code>
20
    /// </example>
21
    /// <remarks>The following apply to message actions in general.
22
    /// <para>See the channel's documentation for limits imposed upon the contents of
23
    /// the text of the message to send.</para>
24
    /// <para>To control various characteristics of your bot's speech such as voice,
25
    /// rate, volume, pronunciation, and pitch, specify test to speak in
26
    /// Speech Synthesis Markup Language (SSML) format.</para>
27
    /// <para>
28
    /// Channels decide how each card action manifests in their user experience.
29
    /// In most cases, the cards are clickable. In others, they may be selected by speech
30
    /// input. In cases where the channel does not offer an interactive activation
31
    /// experience (e.g., when interacting over SMS), the channel may not support
32
    /// activation whatsoever. The decision about how to render actions is controlled by
33
    /// normative requirements elsewhere in this document (e.g. within the card format,
34
    /// or within the suggested actions definition).</para>
35
    /// </remarks>
36
    public static class MessageFactory
37
    {
38
        /// <summary>
39
        /// Returns a simple text message.
40
        /// </summary>
41
        /// <example>
42
        /// <code>
43
        /// // Create and send a message.
44
        /// var message = MessageFactory.Text("Hello World");
45
        /// await context.SendActivity(message);
46
        /// </code>
47
        /// </example>
48
        /// <param name="text">The text of the message to send.</param>
49
        /// <param name="ssml">Optional, text to be spoken by your bot on a speech-enabled
50
        /// channel.</param>
51
        /// <param name="inputHint">Optional, indicates whether your bot is accepting,
52
        /// expecting, or ignoring user input after the message is delivered to the client.
53
        /// One of: "acceptingInput", "ignoringInput", or "expectingInput".
54
        /// Default is "acceptingInput".</param>
55
        /// <returns>A message activity containing the text.</returns>
56
        public static Activity Text(string text, string ssml = null, string inputHint = null)
57
        {
58
            var ma = Activity.CreateMessageActivity();
1✔
59
            SetTextAndSpeak(ma, text, ssml, inputHint);
1✔
60
            return (Activity)ma;
1✔
61
        }
62

63
        /// <summary>
64
        /// Returns a message that includes a set of suggested actions and optional text.
65
        /// </summary>
66
        /// <example>
67
        /// <code>
68
        /// // Create the activity and add suggested actions.
69
        /// var activity = MessageFactory.SuggestedActions(
70
        ///     new string[] { "red", "green", "blue" },
71
        ///     text: "Choose a color");
72
        ///
73
        /// // Send the activity as a reply to the user.
74
        /// await context.SendActivity(activity);
75
        /// </code>
76
        /// </example>
77
        /// <param name="actions">
78
        /// The text of the actions to create.
79
        /// </param>
80
        /// <param name="text">The text of the message to send.</param>
81
        /// <param name="ssml">Optional, text to be spoken by your bot on a speech-enabled
82
        /// channel.</param>
83
        /// <param name="inputHint">Optional, indicates whether your bot is accepting,
84
        /// expecting, or ignoring user input after the message is delivered to the client.
85
        /// One of: "acceptingInput", "ignoringInput", or "expectingInput".
86
        /// Default is "acceptingInput".</param>
87
        /// <returns>A message activity containing the suggested actions.</returns>
88
        /// <exception cref="ArgumentNullException">
89
        /// <paramref name="actions"/> is <c>null</c>.</exception>
90
        /// <remarks>This method creates a suggested action for each string in <paramref name="actions"/>.
91
        /// The created action uses the text for the <see cref="CardAction.Value"/> and
92
        /// <see cref="CardAction.Title"/> and sets the <see cref="CardAction.Type"/> to
93
        /// <see cref="Microsoft.Bot.Schema.ActionTypes.ImBack"/>.
94
        /// </remarks>
95
        /// <seealso cref="SuggestedActions(IEnumerable{CardAction}, string, string, string)"/>
96
        public static IMessageActivity SuggestedActions(IEnumerable<string> actions, string text = null, string ssml = null, string inputHint = null)
97
        {
98
            return SuggestedActions(actions, text, ssml, inputHint, null);
1✔
99
        }
100

101
        /// <summary>
102
        /// Returns a message that includes a set of suggested actions and optional text.
103
        /// </summary>
104
        /// <example>
105
        /// <code>
106
        /// // Create the activity and add suggested actions.
107
        /// var activity = MessageFactory.SuggestedActions(
108
        ///     new string[] { "red", "green", "blue" },
109
        ///     text: "Choose a color");
110
        ///
111
        /// // Send the activity as a reply to the user.
112
        /// await context.SendActivity(activity);
113
        /// </code>
114
        /// </example>
115
        /// <param name="actions">
116
        /// The text of the actions to create.
117
        /// </param>
118
        /// <param name="text">The text of the message to send.</param>
119
        /// <param name="ssml">Optional, text to be spoken by your bot on a speech-enabled
120
        /// channel.</param>
121
        /// <param name="inputHint">Optional, indicates whether your bot is accepting,
122
        /// expecting, or ignoring user input after the message is delivered to the client.
123
        /// One of: "acceptingInput", "ignoringInput", or "expectingInput".
124
        /// Default is "acceptingInput".</param>
125
        /// <param name="toList">Optional, the list of recipients.</param>
126
        /// <returns>A message activity containing the suggested actions.</returns>
127
        /// <exception cref="ArgumentNullException">
128
        /// <paramref name="actions"/> is <c>null</c>.</exception>
129
        /// <remarks>This method creates a suggested action for each string in <paramref name="actions"/>.
130
        /// The created action uses the text for the <see cref="CardAction.Value"/> and
131
        /// <see cref="CardAction.Title"/> and sets the <see cref="CardAction.Type"/> to
132
        /// <see cref="Microsoft.Bot.Schema.ActionTypes.ImBack"/>.
133
        /// </remarks>
134
        /// <seealso cref="SuggestedActions(IEnumerable{CardAction}, string, string, string, IList{string})"/>
135
        public static IMessageActivity SuggestedActions(IEnumerable<string> actions, string text = null, string ssml = null, string inputHint = null, IList<string> toList = default)
136
        {
137
            actions = actions ?? throw new ArgumentNullException(nameof(actions));
1✔
138

139
            var cardActions = new List<CardAction>();
1✔
140
            foreach (var s in actions)
1✔
141
            {
142
                var ca = new CardAction
1✔
143
                {
1✔
144
                    Type = ActionTypes.ImBack,
1✔
145
                    Value = s,
1✔
146
                    Title = s,
1✔
147
                };
1✔
148

149
                cardActions.Add(ca);
1✔
150
            }
151

152
            return SuggestedActions(cardActions, text, ssml, inputHint, toList);
1✔
153
        }
154

155
        /// <summary>
156
        /// Returns a message that includes a set of suggested actions and optional text.
157
        /// </summary>
158
        /// <example>
159
        /// <code>
160
        /// // Create the activity and add suggested actions.
161
        /// var activity = MessageFactory.SuggestedActions(
162
        ///     new CardAction[]
163
        ///     {
164
        ///         new CardAction(title: "red", type: ActionTypes.ImBack, value: "red"),
165
        ///         new CardAction( title: "green", type: ActionTypes.ImBack, value: "green"),
166
        ///         new CardAction(title: "blue", type: ActionTypes.ImBack, value: "blue")
167
        ///     }, text: "Choose a color");
168
        ///
169
        /// // Send the activity as a reply to the user.
170
        /// await context.SendActivity(activity);
171
        /// </code>
172
        /// </example>
173
        /// <param name="cardActions">
174
        /// The card actions to include.
175
        /// </param>
176
        /// <param name="text">Optional, the text of the message to send.</param>
177
        /// <param name="ssml">Optional, text to be spoken by your bot on a speech-enabled
178
        /// channel.</param>
179
        /// <param name="inputHint">Optional, indicates whether your bot is accepting,
180
        /// expecting, or ignoring user input after the message is delivered to the client.
181
        /// One of: "acceptingInput", "ignoringInput", or "expectingInput".
182
        /// Default is "acceptingInput".</param>
183
        /// <returns>A message activity that contains the suggested actions.</returns>
184
        /// <exception cref="ArgumentNullException">
185
        /// <paramref name="cardActions"/> is <c>null</c>.</exception>
186
        public static IMessageActivity SuggestedActions(IEnumerable<CardAction> cardActions, string text = null, string ssml = null, string inputHint = null)
187
        {
188
            return SuggestedActions(cardActions, text, ssml, inputHint, null);
1✔
189
        }
190

191
        /// <summary>
192
        /// Returns a message that includes a set of suggested actions and optional text.
193
        /// </summary>
194
        /// <example>
195
        /// <code>
196
        /// // Create the activity and add suggested actions.
197
        /// var activity = MessageFactory.SuggestedActions(
198
        ///     new CardAction[]
199
        ///     {
200
        ///         new CardAction(title: "red", type: ActionTypes.ImBack, value: "red"),
201
        ///         new CardAction( title: "green", type: ActionTypes.ImBack, value: "green"),
202
        ///         new CardAction(title: "blue", type: ActionTypes.ImBack, value: "blue")
203
        ///     }, text: "Choose a color");
204
        ///
205
        /// // Send the activity as a reply to the user.
206
        /// await context.SendActivity(activity);
207
        /// </code>
208
        /// </example>
209
        /// <param name="cardActions">
210
        /// The card actions to include.
211
        /// </param>
212
        /// <param name="text">Optional, the text of the message to send.</param>
213
        /// <param name="ssml">Optional, text to be spoken by your bot on a speech-enabled
214
        /// channel.</param>
215
        /// <param name="inputHint">Optional, indicates whether your bot is accepting,
216
        /// expecting, or ignoring user input after the message is delivered to the client.
217
        /// One of: "acceptingInput", "ignoringInput", or "expectingInput".
218
        /// Default is "acceptingInput".</param>
219
        /// <param name="toList">Optional, the list of recipients.</param>
220
        /// <returns>A message activity that contains the suggested actions.</returns>
221
        /// <exception cref="ArgumentNullException">
222
        /// <paramref name="cardActions"/> is <c>null</c>.</exception>
223
        public static IMessageActivity SuggestedActions(IEnumerable<CardAction> cardActions, string text = null, string ssml = null, string inputHint = null, IList<string> toList = default)
224
        {
225
            cardActions = cardActions ?? throw new ArgumentNullException(nameof(cardActions));
1✔
226

227
            var ma = Activity.CreateMessageActivity();
1✔
228
            SetTextAndSpeak(ma, text, ssml, inputHint);
1✔
229

230
            ma.SuggestedActions = new SuggestedActions { Actions = cardActions.ToList(), To = toList };
1✔
231

232
            return ma;
1✔
233
        }
234

235
        /// <summary>
236
        /// Returns a message activity that contains an attachment.
237
        /// </summary>
238
        /// <param name="attachment">Attachment to include in the message.</param>
239
        /// <param name="text">Optional, the text of the message to send.</param>
240
        /// <param name="ssml">Optional, text to be spoken by your bot on a speech-enabled
241
        /// channel.</param>
242
        /// <param name="inputHint">Optional, indicates whether your bot is accepting,
243
        /// expecting, or ignoring user input after the message is delivered to the client.
244
        /// One of: "acceptingInput", "ignoringInput", or "expectingInput".
245
        /// Default is "acceptingInput".</param>
246
        /// <returns>A message activity containing the attachment.</returns>
247
        /// <exception cref="ArgumentNullException">
248
        /// <paramref name="attachment"/> is <c>null</c>.</exception>
249
        /// <seealso cref="Attachment(IEnumerable{Attachment}, string, string, string)"/>
250
        /// <seealso cref="Carousel(IEnumerable{Attachment}, string, string, string)"/>
251
        public static IMessageActivity Attachment(Attachment attachment, string text = null, string ssml = null, string inputHint = null)
252
        {
253
            attachment = attachment ?? throw new ArgumentNullException(nameof(attachment));
1✔
254

255
            return Attachment(new List<Attachment> { attachment }, text, ssml, inputHint);
1✔
256
        }
257

258
        /// <summary>
259
        /// Returns a message activity that contains a collection of attachments, in a list.
260
        /// </summary>
261
        /// <param name="attachments">The attachments to include in the message.</param>
262
        /// <param name="text">Optional, the text of the message to send.</param>
263
        /// <param name="ssml">Optional, text to be spoken by your bot on a speech-enabled
264
        /// channel.</param>
265
        /// <param name="inputHint">Optional, indicates whether your bot is accepting,
266
        /// expecting, or ignoring user input after the message is delivered to the client.
267
        /// One of: "acceptingInput", "ignoringInput", or "expectingInput".
268
        /// Default is "acceptingInput".</param>
269
        /// <returns>A message activity containing the attachment.</returns>
270
        /// <exception cref="ArgumentNullException">
271
        /// <paramref name="attachments"/> is <c>null</c>.</exception>
272
        /// <seealso cref="Carousel(IEnumerable{Attachment}, string, string, string)"/>
273
        /// <seealso cref="Attachment(Schema.Attachment, string, string, string)"/>
274
        public static IMessageActivity Attachment(IEnumerable<Attachment> attachments, string text = null, string ssml = null, string inputHint = null)
275
        {
276
            attachments = attachments ?? throw new ArgumentNullException(nameof(attachments));
1✔
277

278
            return AttachmentActivity(AttachmentLayoutTypes.List, attachments, text, ssml, inputHint);
1✔
279
        }
280

281
        /// <summary>
282
        /// Returns a message activity that contains a collection of attachments, as a carousel.
283
        /// </summary>
284
        /// <param name="attachments">The attachments to include in the message.</param>
285
        /// <param name="text">Optional, the text of the message to send.</param>
286
        /// <param name="ssml">Optional, text to be spoken by your bot on a speech-enabled
287
        /// channel.</param>
288
        /// <param name="inputHint">Optional, indicates whether your bot is accepting,
289
        /// expecting, or ignoring user input after the message is delivered to the client.
290
        /// One of: "acceptingInput", "ignoringInput", or "expectingInput".
291
        /// Default is "acceptingInput".</param>
292
        /// <returns>A message activity containing the attachment.</returns>
293
        /// <exception cref="ArgumentNullException">
294
        /// <paramref name="attachments"/> is <c>null</c>.</exception>
295
        /// <example>This code creates and sends a carousel of HeroCards.
296
        /// <code>
297
        /// // Create the activity and attach a set of Hero cards.
298
        /// var activity = MessageFactory.Carousel(
299
        /// new Attachment[]
300
        /// {
301
        ///     new HeroCard(
302
        ///         title: "title1",
303
        ///         images: new CardImage[] { new CardImage(url: "imageUrl1.png") },
304
        ///         buttons: new CardAction[]
305
        ///         {
306
        ///             new CardAction(title: "button1", type: ActionTypes.ImBack, value: "item1")
307
        ///         })
308
        ///     .ToAttachment(),
309
        ///     new HeroCard(
310
        ///         title: "title2",
311
        ///         images: new CardImage[] { new CardImage(url: "imageUrl2.png") },
312
        ///         buttons: new CardAction[]
313
        ///         {
314
        ///             new CardAction(title: "button2", type: ActionTypes.ImBack, value: "item2")
315
        ///         })
316
        ///     .ToAttachment(),
317
        ///     new HeroCard(
318
        ///         title: "title3",
319
        ///         images: new CardImage[] { new CardImage(url: "imageUrl3.png") },
320
        ///         buttons: new CardAction[]
321
        ///         {
322
        ///             new CardAction(title: "button3", type: ActionTypes.ImBack, value: "item3")
323
        ///         })
324
        ///     .ToAttachment()
325
        /// });
326
        ///
327
        /// // Send the activity as a reply to the user.
328
        /// await context.SendActivity(activity);
329
        /// </code>
330
        /// </example>
331
        /// <seealso cref="Attachment(IEnumerable{Attachment}, string, string, string)"/>
332
        public static IMessageActivity Carousel(IEnumerable<Attachment> attachments, string text = null, string ssml = null, string inputHint = null)
333
        {
334
            attachments = attachments ?? throw new ArgumentNullException(nameof(attachments));
1✔
335

336
            return AttachmentActivity(AttachmentLayoutTypes.Carousel, attachments, text, ssml, inputHint);
1✔
337
        }
338

339
        /// <summary>
340
        /// Returns a message activity that contains a single image or video.
341
        /// </summary>
342
        /// <param name="url">The URL of the image or video to send.</param>
343
        /// <param name="contentType">The MIME type of the image or video.</param>
344
        /// <param name="name">Optional, the name of the image or video file.</param>
345
        /// <param name="text">Optional, the text of the message to send.</param>
346
        /// <param name="ssml">Optional, text to be spoken by your bot on a speech-enabled
347
        /// channel.</param>
348
        /// <param name="inputHint">Optional, indicates whether your bot is accepting,
349
        /// expecting, or ignoring user input after the message is delivered to the client.
350
        /// One of: "acceptingInput", "ignoringInput", or "expectingInput".
351
        /// Default is null.</param>
352
        /// <returns>A message activity containing the attachment.</returns>
353
        /// <exception cref="ArgumentNullException">
354
        /// <paramref name="url"/> or <paramref name="contentType"/> is <c>null</c>,
355
        /// empty, or white space.</exception>
356
        /// <example>This code creates a message activity that contains an image.
357
        /// <code>
358
        /// IMessageActivity message =
359
        ///     MessageFactory.ContentUrl("https://{domainName}/cat.jpg", MediaTypeNames.Image.Jpeg, "Cat Picture");
360
        /// </code>
361
        /// </example>
362
        public static IMessageActivity ContentUrl(string url, string contentType, string name = null, string text = null, string ssml = null, string inputHint = null)
363
        {
364
            if (string.IsNullOrWhiteSpace(url))
1✔
365
            {
366
                throw new ArgumentNullException(nameof(url));
1✔
367
            }
368

369
            if (string.IsNullOrWhiteSpace(contentType))
1✔
370
            {
371
                throw new ArgumentNullException(nameof(contentType));
1✔
372
            }
373

374
            var a = new Attachment
×
375
            {
×
376
                ContentType = contentType,
×
377
                ContentUrl = url,
×
378
                Name = !string.IsNullOrWhiteSpace(name) ? name : string.Empty,
×
379
            };
×
380

381
            return AttachmentActivity(AttachmentLayoutTypes.List, new List<Attachment> { a }, text, ssml, inputHint);
1✔
382
        }
383

384
        private static IMessageActivity AttachmentActivity(string attachmentLayout, IEnumerable<Attachment> attachments, string text = null, string ssml = null, string inputHint = null)
385
        {
386
            var ma = Activity.CreateMessageActivity();
1✔
387
            ma.AttachmentLayout = attachmentLayout;
1✔
388
            ma.Attachments = attachments.ToList();
1✔
389
            SetTextAndSpeak(ma, text, ssml, inputHint);
1✔
390
            return ma;
1✔
391
        }
392

393
        private static void SetTextAndSpeak(IMessageActivity ma, string text = null, string ssml = null, string inputHint = null)
394
        {
395
            // Note: we must put NULL in the fields, as the clients will happily render
396
            // an empty string, which is not the behavior people expect to see.
397
            ma.Text = !string.IsNullOrWhiteSpace(text) ? text : null;
1✔
398
            ma.Speak = !string.IsNullOrWhiteSpace(ssml) ? ssml : null;
1✔
399
            ma.InputHint = inputHint ?? InputHints.AcceptingInput;
1✔
400
        }
1✔
401
    }
402
}
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