microsoft/teams.net

Public

mirrored fromhttps://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v2.0.8

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

core/src/Microsoft.Teams.Apps/Schema/MessageActivityExtensions.cs

475lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using System.Diagnostics.CodeAnalysis;
5
6using Microsoft.Teams.Apps.Schema.Entities;
7using Microsoft.Teams.Core.Schema;
8
9namespace Microsoft.Teams.Apps.Schema;
10
11/// <summary>
12/// Fluent extension methods for <see cref="MessageActivity"/> that delegate to <see cref="TeamsActivityBuilder"/> internally.
13/// These methods provide backward compatibility with the old library's <c>message.WithText(...).WithSuggestedActions(...)</c> pattern.
14/// </summary>
15public static class MessageActivityExtensions
16{
17
18 /// <summary>
19 /// Sets the activity id.
20 /// </summary>
21 public static MessageActivity WithId(this MessageActivity message, string value)
22 {
23 ArgumentNullException.ThrowIfNull(message);
24 message.Id = value;
25 return message;
26 }
27
28 /// <summary>
29 /// Sets the channel id.
30 /// </summary>
31 public static MessageActivity WithChannelId(this MessageActivity message, string? value)
32 {
33 ArgumentNullException.ThrowIfNull(message);
34 message.ChannelId = value;
35 return message;
36 }
37
38 /// <summary>
39 /// Sets the sender account.
40 /// </summary>
41 public static MessageActivity WithFrom(this MessageActivity message, ConversationAccount? value)
42 {
43 ArgumentNullException.ThrowIfNull(message);
44 message.From = value is TeamsConversationAccount teamsAccount
45 ? teamsAccount
46 : TeamsConversationAccount.FromConversationAccount(value);
47 return message;
48 }
49
50 /// <summary>
51 /// Sets the recipient account on the message.
52 /// </summary>
53 /// <param name="message">The message activity.</param>
54 /// <param name="account">The recipient account.</param>
55 /// <returns>The message activity for chaining.</returns>
56 public static MessageActivity WithRecipient(this MessageActivity message, ConversationAccount account)
57 {
58 ArgumentNullException.ThrowIfNull(message);
59
60 message.Recipient = account is TeamsConversationAccount teamsAccount
61 ? teamsAccount
62 : TeamsConversationAccount.FromConversationAccount(account);
63 return message;
64 }
65
66 /// <summary>
67 /// Sets the recipient account and targeted flag on the message.
68 /// </summary>
69 /// <param name="message">The message activity.</param>
70 /// <param name="account">The recipient account.</param>
71 /// <param name="isTargeted">Whether the recipient is targeted.</param>
72 /// <returns>The message activity for chaining.</returns>
73 [Experimental("ExperimentalTeamsTargeted")]
74 public static MessageActivity WithRecipient(this MessageActivity message, ConversationAccount account, bool isTargeted = false)
75 {
76 ArgumentNullException.ThrowIfNull(message);
77
78 if (account is not null)
79 {
80 account.IsTargeted = isTargeted ? true : null;
81 message.Recipient = account is TeamsConversationAccount teamsAccount
82 ? teamsAccount
83 : TeamsConversationAccount.FromConversationAccount(account);
84 }
85 return message;
86 }
87
88 /// <summary>
89 /// Sets the conversation information.
90 /// </summary>
91 public static MessageActivity WithConversation(this MessageActivity message, Conversation? value)
92 {
93 ArgumentNullException.ThrowIfNull(message);
94
95 message.Conversation = value is TeamsConversation teamsConversation
96 ? teamsConversation
97 : TeamsConversation.FromConversation(value);
98 return message;
99 }
100
101 /// <summary>
102 /// Sets the service url.
103 /// </summary>
104 public static MessageActivity WithServiceUrl(this MessageActivity message, Uri? value)
105 {
106 ArgumentNullException.ThrowIfNull(message);
107 message.ServiceUrl = value;
108 return message;
109 }
110
111 /// <summary>
112 /// Sets the locale.
113 /// </summary>
114 public static MessageActivity WithLocale(this MessageActivity message, string? value)
115 {
116 ArgumentNullException.ThrowIfNull(message);
117 message.Locale = value;
118 return message;
119 }
120
121 /// <summary>
122 /// Sets the UTC timestamp value.
123 /// </summary>
124 public static MessageActivity WithTimestamp(this MessageActivity message, string? value)
125 {
126 ArgumentNullException.ThrowIfNull(message);
127 message.Timestamp = value;
128 return message;
129 }
130
131 /// <summary>
132 /// Sets the local timestamp value.
133 /// </summary>
134 public static MessageActivity WithLocalTimestamp(this MessageActivity message, string? value)
135 {
136 ArgumentNullException.ThrowIfNull(message);
137 message.LocalTimestamp = value;
138 return message;
139 }
140
141 /// <summary>
142 /// Merges channel data properties into the activity.
143 /// </summary>
144 public static MessageActivity WithData(this MessageActivity message, ChannelData value)
145 {
146 ArgumentNullException.ThrowIfNull(message);
147 ArgumentNullException.ThrowIfNull(value);
148
149 message.ChannelData ??= new TeamsChannelData();
150 foreach (KeyValuePair<string, object?> kv in value.Properties)
151 {
152 message.ChannelData.Properties[kv.Key] = kv.Value;
153 }
154
155 return message;
156 }
157
158 /// <summary>
159 /// Sets a channel data key/value property.
160 /// </summary>
161 public static MessageActivity WithData(this MessageActivity message, string key, object? value)
162 {
163 ArgumentNullException.ThrowIfNull(message);
164 ArgumentException.ThrowIfNullOrWhiteSpace(key);
165
166 message.ChannelData ??= new TeamsChannelData();
167 message.ChannelData.Properties[key] = value;
168 return message;
169 }
170
171 /// <summary>
172 /// Sets the app id inside channel data.
173 /// </summary>
174 public static MessageActivity WithAppId(this MessageActivity message, string value)
175 {
176 ArgumentNullException.ThrowIfNull(message);
177 ArgumentException.ThrowIfNullOrWhiteSpace(value);
178
179 message.ChannelData ??= new TeamsChannelData();
180 message.ChannelData.Properties["app"] = new Dictionary<string, object?> { ["id"] = value };
181 return message;
182 }
183
184 /// <summary>
185 /// Sets the text content of the message.
186 /// </summary>
187 /// <param name="message">The message activity.</param>
188 /// <param name="text">The text to set.</param>
189 /// <param name="textFormat">The text format. Default is "plain".</param>
190 /// <returns>The message activity for chaining.</returns>
191 public static MessageActivity WithText(this MessageActivity message, string text, string textFormat = TextFormats.Plain)
192 {
193 ArgumentNullException.ThrowIfNull(message);
194 message.Text = text;
195 message.TextFormat = textFormat;
196 return message;
197 }
198
199 /// <summary>
200 /// Appends text to the current message text.
201 /// </summary>
202 /// <param name="message">The message activity.</param>
203 /// <param name="text">The text to append.</param>
204 /// <returns>The message activity for chaining.</returns>
205 public static MessageActivity AddText(this MessageActivity message, string text)
206 {
207 ArgumentNullException.ThrowIfNull(message);
208 message.Text = $"{message.Text}{text}";
209 return message;
210 }
211
212 /// <summary>
213 /// Sets the text format for the message.
214 /// </summary>
215 /// <param name="message">The message activity.</param>
216 /// <param name="textFormat">The text format. See <see cref="TextFormats"/> for common values.</param>
217 /// <returns>The message activity for chaining.</returns>
218 public static MessageActivity WithTextFormat(this MessageActivity message, string textFormat)
219 {
220 ArgumentNullException.ThrowIfNull(message);
221 message.TextFormat = textFormat;
222 return message;
223 }
224
225 /// <summary>
226 /// Adds one or more attachments to the message.
227 /// </summary>
228 /// <param name="message">The message activity.</param>
229 /// <param name="attachments">The attachments to add.</param>
230 /// <returns>The message activity for chaining.</returns>
231 public static MessageActivity AddAttachment(this MessageActivity message, params TeamsAttachment[] attachments)
232 {
233 ArgumentNullException.ThrowIfNull(message);
234 ArgumentNullException.ThrowIfNull(attachments);
235 message.Attachments ??= [];
236 foreach (TeamsAttachment attachment in attachments)
237 {
238 message.Attachments.Add(attachment);
239 }
240 return message;
241 }
242
243 /// <summary>
244 /// Sets the attachment layout for the message.
245 /// </summary>
246 /// <param name="message">The message activity.</param>
247 /// <param name="attachmentLayout">The attachment layout (e.g., "list", "carousel").</param>
248 /// <returns>The message activity for chaining.</returns>
249 public static MessageActivity WithAttachmentLayout(this MessageActivity message, string attachmentLayout)
250 {
251 ArgumentNullException.ThrowIfNull(message);
252 message.AttachmentLayout = attachmentLayout;
253 return message;
254 }
255
256 /// <summary>
257 /// Sets the suggested actions for the message.
258 /// </summary>
259 /// <param name="message">The message activity.</param>
260 /// <param name="suggestedActions">The suggested actions to set.</param>
261 /// <returns>The message activity for chaining.</returns>
262 public static MessageActivity WithSuggestedActions(this MessageActivity message, SuggestedActions suggestedActions)
263 {
264 ArgumentNullException.ThrowIfNull(message);
265 message.SuggestedActions = suggestedActions;
266 return message;
267 }
268
269 /// <summary>
270 /// Adds one or more entities to the message.
271 /// </summary>
272 /// <param name="message">The target message.</param>
273 /// <param name="entities">Entities to add.</param>
274 /// <returns>The message for chaining.</returns>
275 public static MessageActivity AddEntity(this MessageActivity message, params Entity[] entities)
276 {
277 ArgumentNullException.ThrowIfNull(message);
278 ArgumentNullException.ThrowIfNull(entities);
279
280 message.Entities ??= [];
281 foreach (Entity entity in entities)
282 {
283 message.Entities.Add(entity);
284 }
285
286 return message;
287 }
288
289 /// <summary>
290 /// Replaces an existing entity with a new entity.
291 /// </summary>
292 /// <param name="message">The target message.</param>
293 /// <param name="oldEntity">The entity to replace.</param>
294 /// <param name="newEntity">The replacement entity.</param>
295 /// <returns>The message for chaining.</returns>
296 public static MessageActivity UpdateEntity(this MessageActivity message, Entity oldEntity, Entity newEntity)
297 {
298 ArgumentNullException.ThrowIfNull(message);
299 ArgumentNullException.ThrowIfNull(oldEntity);
300 ArgumentNullException.ThrowIfNull(newEntity);
301
302 if (message.Entities != null)
303 {
304 message.Entities.Remove(oldEntity);
305 }
306 else
307 {
308 message.Entities = [];
309 }
310
311 message.Entities.Add(newEntity);
312 return message;
313 }
314
315 /// <summary>
316 /// Adds a quoted message reference and appends a placeholder to the message text.
317 /// </summary>
318 /// <param name="message">The message activity.</param>
319 /// <param name="messageId">The ID of the message being quoted.</param>
320 /// <param name="text">Optional text to append after the quote placeholder.</param>
321 /// <returns>The message activity for chaining.</returns>
322 [Experimental("ExperimentalTeamsQuotedReplies")]
323 public static MessageActivity AddQuote(this MessageActivity message, string messageId, string? text = null)
324 {
325 ArgumentNullException.ThrowIfNull(message);
326 ArgumentException.ThrowIfNullOrWhiteSpace(messageId);
327
328 QuotedReplyEntityExtensions.AddToActivity(message, messageId, text);
329
330 return message;
331 }
332
333 /// <summary>
334 /// Prepends a quoted message placeholder before existing text.
335 /// </summary>
336 /// <param name="message">The message activity.</param>
337 /// <param name="messageId">The ID of the message being quoted.</param>
338 /// <returns>The message activity for chaining.</returns>
339 [Experimental("ExperimentalTeamsQuotedReplies")]
340 public static MessageActivity PrependQuote(this MessageActivity message, string messageId)
341 {
342 ArgumentNullException.ThrowIfNull(message);
343 ArgumentException.ThrowIfNullOrWhiteSpace(messageId);
344
345 message.Entities ??= [];
346 message.Entities.Insert(0, new QuotedReplyEntity { QuotedReply = new QuotedReplyData { MessageId = messageId } });
347 var placeholder = QuotedReplyEntityExtensions.QuotedPlaceholder(messageId);
348 var text = message.Text?.Trim() ?? "";
349 message.Text = string.IsNullOrEmpty(text) ? placeholder : $"{placeholder} {text}";
350
351 return message;
352 }
353
354
355 /// <summary>
356 /// Adds targeted message info entity for prompt preview and strips quote placeholders.
357 /// </summary>
358 [Experimental("ExperimentalTeamsTargeted")]
359 public static MessageActivity AddTargetedMessageInfo(this MessageActivity message, string messageId)
360 {
361 ArgumentNullException.ThrowIfNull(message);
362 ArgumentException.ThrowIfNullOrWhiteSpace(messageId);
363
364 TargetedMessageInfoEntityExtensions.AddToActivity(message, messageId);
365
366 return message;
367 }
368
369 /// <summary>
370 /// Adds a mention (@mention) entity and optionally prepends mention text.
371 /// </summary>
372 /// <param name="message">The message activity.</param>
373 /// <param name="account">The account being mentioned.</param>
374 /// <param name="text">Optional mention text. If null, uses account name.</param>
375 /// <param name="addText">Whether mention text should be prepended to message text.</param>
376 /// <returns>The message activity for chaining.</returns>
377 public static MessageActivity AddMention(this MessageActivity message, ConversationAccount account, string? text = null, bool addText = true)
378 {
379 ArgumentNullException.ThrowIfNull(message);
380 ArgumentNullException.ThrowIfNull(account);
381
382 MentionEntityExtensions.AddToActivity(message, account, text, addText);
383
384 return message;
385 }
386
387 /// <summary>
388 /// Marks the message as a final streaming message by adding a <see cref="StreamInfoEntity"/>
389 /// with <see cref="StreamType.Final"/>.
390 /// </summary>
391 /// <param name="message">The message activity.</param>
392 /// <returns>The message activity for chaining.</returns>
393 public static MessageActivity AddStreamFinal(this MessageActivity message)
394 {
395 ArgumentNullException.ThrowIfNull(message);
396
397 StreamInfoEntityExtensions.AddToActivity(message, StreamType.Final);
398 return message;
399 }
400
401 /// <summary>
402 /// Gets the mention entity for a specific account id.
403 /// </summary>
404 /// <param name="message">The message activity.</param>
405 /// <param name="accountId">The account id to match.</param>
406 /// <returns>The matching mention entity, or null if not found.</returns>
407 public static MentionEntity? GetAccountMention(this MessageActivity message, string accountId)
408 {
409 ArgumentNullException.ThrowIfNull(message);
410 ArgumentException.ThrowIfNullOrWhiteSpace(accountId);
411
412 return (MentionEntity?)(message.Entities ?? []).FirstOrDefault(e => e is MentionEntity mention && mention.Mentioned?.Id == accountId);
413 }
414
415 /// <summary>
416 /// Adds the AI-generated content label to the root message entity.
417 /// </summary>
418 public static OMessageEntity AddAIGenerated(this MessageActivity message)
419 {
420 ArgumentNullException.ThrowIfNull(message);
421
422 return OMessageEntityExtensions.AddAIGeneratedContent(message);
423 }
424
425 /// <summary>
426 /// Adds a content sensitivity label to the message.
427 /// </summary>
428 public static MessageActivity AddSensitivityLabel(this MessageActivity message, string name, string? description = null, DefinedTerm? pattern = null)
429 {
430 ArgumentNullException.ThrowIfNull(message);
431 SensitiveUsageEntityExtensions.AddToActivity(message, name, description, pattern);
432 return message;
433 }
434
435 /// <summary>
436 /// Enables/disables feedback loop on the message.
437 /// </summary>
438 public static MessageActivity AddFeedback(this MessageActivity message, bool value = true)
439 {
440 ArgumentNullException.ThrowIfNull(message);
441
442 message.ChannelData ??= new TeamsChannelData();
443 message.ChannelData.FeedbackLoopEnabled = value;
444 return message;
445 }
446
447 /// <summary>
448 /// Configures feedback loop mode on the message.
449 /// </summary>
450 /// <param name="message">The message activity.</param>
451 /// <param name="mode">The feedback loop type. See <see cref="FeedbackType"/> for known values.</param>
452 /// <returns>The message activity for chaining.</returns>
453 public static MessageActivity AddFeedback(this MessageActivity message, string mode)
454 {
455 ArgumentNullException.ThrowIfNull(message);
456
457 message.ChannelData ??= new TeamsChannelData();
458 message.ChannelData.FeedbackLoop = new FeedbackLoop(mode);
459 message.ChannelData.FeedbackLoopEnabled = null;
460 return message;
461 }
462
463 /// <summary>
464 /// Adds a citation claim to the message.
465 /// </summary>
466 public static CitationEntity AddCitation(this MessageActivity message, int position, CitationAppearance appearance)
467 {
468 ArgumentNullException.ThrowIfNull(message);
469 ArgumentNullException.ThrowIfNull(appearance);
470
471 message.Entities ??= [];
472 return CitationEntityExtensions.AddToActivity(message, position, appearance);
473 }
474
475}
476