microsoft/teams.net

Public

mirrored from https://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v2.1.0-preview-0007

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

458lines · 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 /// Sets a channel data key/value property.
143 /// </summary>
144 public static MessageActivity WithData(this MessageActivity message, string key, object? value)
145 {
146 ArgumentNullException.ThrowIfNull(message);
147 ArgumentException.ThrowIfNullOrWhiteSpace(key);
148
149 message.ChannelData ??= new TeamsChannelData();
150 message.ChannelData.Properties[key] = value;
151 return message;
152 }
153
154 /// <summary>
155 /// Sets the app id inside channel data.
156 /// </summary>
157 public static MessageActivity WithAppId(this MessageActivity message, string value)
158 {
159 ArgumentNullException.ThrowIfNull(message);
160 ArgumentException.ThrowIfNullOrWhiteSpace(value);
161
162 message.ChannelData ??= new TeamsChannelData();
163 message.ChannelData.Properties["app"] = new Dictionary<string, object?> { ["id"] = value };
164 return message;
165 }
166
167 /// <summary>
168 /// Sets the text content of the message.
169 /// </summary>
170 /// <param name="message">The message activity.</param>
171 /// <param name="text">The text to set.</param>
172 /// <param name="textFormat">The text format. Default is "plain".</param>
173 /// <returns>The message activity for chaining.</returns>
174 public static MessageActivity WithText(this MessageActivity message, string text, string textFormat = TextFormats.Plain)
175 {
176 ArgumentNullException.ThrowIfNull(message);
177 message.Text = text;
178 message.TextFormat = textFormat;
179 return message;
180 }
181
182 /// <summary>
183 /// Appends text to the current message text.
184 /// </summary>
185 /// <param name="message">The message activity.</param>
186 /// <param name="text">The text to append.</param>
187 /// <returns>The message activity for chaining.</returns>
188 public static MessageActivity AddText(this MessageActivity message, string text)
189 {
190 ArgumentNullException.ThrowIfNull(message);
191 message.Text = $"{message.Text}{text}";
192 return message;
193 }
194
195 /// <summary>
196 /// Sets the text format for the message.
197 /// </summary>
198 /// <param name="message">The message activity.</param>
199 /// <param name="textFormat">The text format. See <see cref="TextFormats"/> for common values.</param>
200 /// <returns>The message activity for chaining.</returns>
201 public static MessageActivity WithTextFormat(this MessageActivity message, string textFormat)
202 {
203 ArgumentNullException.ThrowIfNull(message);
204 message.TextFormat = textFormat;
205 return message;
206 }
207
208 /// <summary>
209 /// Adds one or more attachments to the message.
210 /// </summary>
211 /// <param name="message">The message activity.</param>
212 /// <param name="attachments">The attachments to add.</param>
213 /// <returns>The message activity for chaining.</returns>
214 public static MessageActivity AddAttachment(this MessageActivity message, params TeamsAttachment[] attachments)
215 {
216 ArgumentNullException.ThrowIfNull(message);
217 ArgumentNullException.ThrowIfNull(attachments);
218 message.Attachments ??= [];
219 foreach (TeamsAttachment attachment in attachments)
220 {
221 message.Attachments.Add(attachment);
222 }
223 return message;
224 }
225
226 /// <summary>
227 /// Sets the attachment layout for the message.
228 /// </summary>
229 /// <param name="message">The message activity.</param>
230 /// <param name="attachmentLayout">The attachment layout (e.g., "list", "carousel").</param>
231 /// <returns>The message activity for chaining.</returns>
232 public static MessageActivity WithAttachmentLayout(this MessageActivity message, string attachmentLayout)
233 {
234 ArgumentNullException.ThrowIfNull(message);
235 message.AttachmentLayout = attachmentLayout;
236 return message;
237 }
238
239 /// <summary>
240 /// Sets the suggested actions for the message.
241 /// </summary>
242 /// <param name="message">The message activity.</param>
243 /// <param name="suggestedActions">The suggested actions to set.</param>
244 /// <returns>The message activity for chaining.</returns>
245 public static MessageActivity WithSuggestedActions(this MessageActivity message, SuggestedActions suggestedActions)
246 {
247 ArgumentNullException.ThrowIfNull(message);
248 message.SuggestedActions = suggestedActions;
249 return message;
250 }
251
252 /// <summary>
253 /// Adds one or more entities to the message.
254 /// </summary>
255 /// <param name="message">The target message.</param>
256 /// <param name="entities">Entities to add.</param>
257 /// <returns>The message for chaining.</returns>
258 public static MessageActivity AddEntity(this MessageActivity message, params Entity[] entities)
259 {
260 ArgumentNullException.ThrowIfNull(message);
261 ArgumentNullException.ThrowIfNull(entities);
262
263 message.Entities ??= [];
264 foreach (Entity entity in entities)
265 {
266 message.Entities.Add(entity);
267 }
268
269 return message;
270 }
271
272 /// <summary>
273 /// Replaces an existing entity with a new entity.
274 /// </summary>
275 /// <param name="message">The target message.</param>
276 /// <param name="oldEntity">The entity to replace.</param>
277 /// <param name="newEntity">The replacement entity.</param>
278 /// <returns>The message for chaining.</returns>
279 public static MessageActivity UpdateEntity(this MessageActivity message, Entity oldEntity, Entity newEntity)
280 {
281 ArgumentNullException.ThrowIfNull(message);
282 ArgumentNullException.ThrowIfNull(oldEntity);
283 ArgumentNullException.ThrowIfNull(newEntity);
284
285 if (message.Entities != null)
286 {
287 message.Entities.Remove(oldEntity);
288 }
289 else
290 {
291 message.Entities = [];
292 }
293
294 message.Entities.Add(newEntity);
295 return message;
296 }
297
298 /// <summary>
299 /// Adds a quoted message reference and appends a placeholder to the message text.
300 /// </summary>
301 /// <param name="message">The message activity.</param>
302 /// <param name="messageId">The ID of the message being quoted.</param>
303 /// <param name="text">Optional text to append after the quote placeholder.</param>
304 /// <returns>The message activity for chaining.</returns>
305 [Experimental("ExperimentalTeamsQuotedReplies")]
306 public static MessageActivity AddQuote(this MessageActivity message, string messageId, string? text = null)
307 {
308 ArgumentNullException.ThrowIfNull(message);
309 ArgumentException.ThrowIfNullOrWhiteSpace(messageId);
310
311 QuotedReplyEntityExtensions.AddToActivity(message, messageId, text);
312
313 return message;
314 }
315
316 /// <summary>
317 /// Prepends a quoted message placeholder before existing text.
318 /// </summary>
319 /// <param name="message">The message activity.</param>
320 /// <param name="messageId">The ID of the message being quoted.</param>
321 /// <returns>The message activity for chaining.</returns>
322 [Experimental("ExperimentalTeamsQuotedReplies")]
323 public static MessageActivity PrependQuote(this MessageActivity message, string messageId)
324 {
325 ArgumentNullException.ThrowIfNull(message);
326 ArgumentException.ThrowIfNullOrWhiteSpace(messageId);
327
328 message.Entities ??= [];
329 message.Entities.Insert(0, new QuotedReplyEntity { QuotedReply = new QuotedReplyData { MessageId = messageId } });
330 string placeholder = QuotedReplyEntityExtensions.QuotedPlaceholder(messageId);
331 string text = message.Text?.Trim() ?? "";
332 message.Text = string.IsNullOrEmpty(text) ? placeholder : $"{placeholder} {text}";
333
334 return message;
335 }
336
337
338 /// <summary>
339 /// Adds targeted message info entity for prompt preview and strips quote placeholders.
340 /// </summary>
341 [Experimental("ExperimentalTeamsTargeted")]
342 public static MessageActivity AddTargetedMessageInfo(this MessageActivity message, string messageId)
343 {
344 ArgumentNullException.ThrowIfNull(message);
345 ArgumentException.ThrowIfNullOrWhiteSpace(messageId);
346
347 TargetedMessageInfoEntityExtensions.AddToActivity(message, messageId);
348
349 return message;
350 }
351
352 /// <summary>
353 /// Adds a mention (@mention) entity and optionally prepends mention text.
354 /// </summary>
355 /// <param name="message">The message activity.</param>
356 /// <param name="account">The account being mentioned.</param>
357 /// <param name="text">Optional mention text. If null, uses account name.</param>
358 /// <param name="addText">Whether mention text should be prepended to message text.</param>
359 /// <returns>The message activity for chaining.</returns>
360 public static MessageActivity AddMention(this MessageActivity message, ConversationAccount account, string? text = null, bool addText = true)
361 {
362 ArgumentNullException.ThrowIfNull(message);
363 ArgumentNullException.ThrowIfNull(account);
364
365 MentionEntityExtensions.AddToActivity(message, account, text, addText);
366
367 return message;
368 }
369
370 /// <summary>
371 /// Marks the message as a final streaming message by adding a <see cref="StreamInfoEntity"/>
372 /// with <see cref="StreamType.Final"/>.
373 /// </summary>
374 /// <param name="message">The message activity.</param>
375 /// <returns>The message activity for chaining.</returns>
376 public static MessageActivity AddStreamFinal(this MessageActivity message)
377 {
378 ArgumentNullException.ThrowIfNull(message);
379
380 StreamInfoEntityExtensions.AddToActivity(message, StreamType.Final);
381 return message;
382 }
383
384 /// <summary>
385 /// Gets the mention entity for a specific account id.
386 /// </summary>
387 /// <param name="message">The message activity.</param>
388 /// <param name="accountId">The account id to match.</param>
389 /// <returns>The matching mention entity, or null if not found.</returns>
390 public static MentionEntity? GetAccountMention(this MessageActivity message, string accountId)
391 {
392 ArgumentNullException.ThrowIfNull(message);
393 ArgumentException.ThrowIfNullOrWhiteSpace(accountId);
394
395 return (MentionEntity?)(message.Entities ?? []).FirstOrDefault(e => e is MentionEntity mention && mention.Mentioned?.Id == accountId);
396 }
397
398 /// <summary>
399 /// Adds the AI-generated content label to the root message entity.
400 /// </summary>
401 public static OMessageEntity AddAIGenerated(this MessageActivity message)
402 {
403 ArgumentNullException.ThrowIfNull(message);
404
405 return OMessageEntityExtensions.AddAIGeneratedContent(message);
406 }
407
408 /// <summary>
409 /// Adds a content sensitivity label to the message.
410 /// </summary>
411 public static MessageActivity AddSensitivityLabel(this MessageActivity message, string name, string? description = null, DefinedTerm? pattern = null)
412 {
413 ArgumentNullException.ThrowIfNull(message);
414 SensitiveUsageEntityExtensions.AddToActivity(message, name, description, pattern);
415 return message;
416 }
417
418 /// <summary>
419 /// Enables/disables feedback loop on the message.
420 /// </summary>
421 public static MessageActivity AddFeedback(this MessageActivity message, bool value = true)
422 {
423 ArgumentNullException.ThrowIfNull(message);
424
425 message.ChannelData ??= new TeamsChannelData();
426 message.ChannelData.FeedbackLoopEnabled = value;
427 return message;
428 }
429
430 /// <summary>
431 /// Configures feedback loop mode on the message.
432 /// </summary>
433 /// <param name="message">The message activity.</param>
434 /// <param name="mode">The feedback loop type. See <see cref="FeedbackType"/> for known values.</param>
435 /// <returns>The message activity for chaining.</returns>
436 public static MessageActivity AddFeedback(this MessageActivity message, string mode)
437 {
438 ArgumentNullException.ThrowIfNull(message);
439
440 message.ChannelData ??= new TeamsChannelData();
441 message.ChannelData.FeedbackLoop = new FeedbackLoop(mode);
442 message.ChannelData.FeedbackLoopEnabled = null;
443 return message;
444 }
445
446 /// <summary>
447 /// Adds a citation claim to the message.
448 /// </summary>
449 public static CitationEntity AddCitation(this MessageActivity message, int position, CitationAppearance appearance)
450 {
451 ArgumentNullException.ThrowIfNull(message);
452 ArgumentNullException.ThrowIfNull(appearance);
453
454 message.Entities ??= [];
455 return CitationEntityExtensions.AddToActivity(message, position, appearance);
456 }
457
458}
459