microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
samples/migration-bot

Branches

Tags

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

Clone

HTTPS

Download ZIP

Libraries/Microsoft.Teams.Api/Activities/Activity.cs

496lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT License.
3
4using System.Diagnostics.CodeAnalysis;
5using System.Text.Json;
6using System.Text.Json.Serialization;
7
8using Microsoft.Teams.Api.Entities;
9using Microsoft.Teams.Common;
10
11namespace Microsoft.Teams.Api.Activities;
12
13[JsonConverter(typeof(JsonConverter<ActivityType>))]
14public partial class ActivityType(string value) : StringEnum(value)
15{
16 public Type ToType()
17 {
18 if (IsTyping) return typeof(TypingActivity);
19 if (IsCommand) return typeof(CommandActivity);
20 if (IsCommandResult) return typeof(CommandResultActivity);
21 if (IsConversationUpdate) return typeof(ConversationUpdateActivity);
22 if (IsEndOfConversation) return typeof(EndOfConversationActivity);
23 if (IsInstallUpdate) return typeof(InstallUpdateActivity);
24 if (IsMessage) return typeof(MessageActivity);
25 if (IsMessageUpdate) return typeof(MessageUpdateActivity);
26 if (IsMessageDelete) return typeof(MessageDeleteActivity);
27 if (IsMessageReaction) return typeof(MessageReactionActivity);
28 if (IsEvent) return typeof(EventActivity);
29 if (IsInvoke) return typeof(InvokeActivity);
30 return typeof(Activity);
31 }
32
33 public string ToPrettyString()
34 {
35 var value = ToString();
36 return $"{value.First().ToString().ToUpper()}{value.AsSpan(1).ToString()}";
37 }
38}
39
40[JsonConverter(typeof(ActivityJsonConverter))]
41public partial interface IActivity : IConvertible, ICloneable
42{
43 public string Id { get; set; }
44
45 public ActivityType Type { get; set; }
46
47 public string? ReplyToId { get; set; }
48
49 public ChannelId ChannelId { get; set; }
50
51 public Account From { get; set; }
52
53 public Account Recipient { get; set; }
54
55 public Conversation Conversation { get; set; }
56
57 public ConversationReference? RelatesTo { get; set; }
58
59 public string? ServiceUrl { get; set; }
60
61 public string? Locale { get; set; }
62
63 public DateTime? Timestamp { get; set; }
64
65 public DateTime? LocalTimestamp { get; set; }
66
67 public IList<IEntity>? Entities { get; set; }
68
69 public ChannelData? ChannelData { get; set; }
70
71 public IDictionary<string, object?> Properties { get; set; }
72
73 /// <summary>
74 /// is this a streaming activity
75 /// </summary>
76 [JsonIgnore]
77 public bool IsStreaming { get; }
78
79 /// <summary>
80 /// get the activity type/name path
81 /// </summary>
82 public string GetPath();
83
84 /// <summary>
85 /// get the quote reply string form of this activity
86 /// </summary>
87 public string ToQuoteReply();
88}
89
90[JsonConverter(typeof(ActivityJsonConverter))]
91public partial class Activity : IActivity
92{
93 [JsonPropertyName("id")]
94 [JsonPropertyOrder(0)]
95 public string Id { get; set; }
96
97 [JsonPropertyName("type")]
98 [JsonPropertyOrder(10)]
99 public ActivityType Type { get; set; }
100
101 [JsonPropertyName("replyToId")]
102 [JsonPropertyOrder(20)]
103 public string? ReplyToId { get; set; }
104
105 [JsonPropertyName("channelId")]
106 [JsonPropertyOrder(30)]
107 public ChannelId ChannelId { get; set; } = ChannelId.MsTeams;
108
109 [JsonPropertyName("from")]
110 [JsonPropertyOrder(40)]
111 public Account From { get; set; }
112
113 [JsonPropertyName("recipient")]
114 [JsonPropertyOrder(50)]
115 public Account Recipient { get; set; }
116
117 [JsonPropertyName("conversation")]
118 [JsonPropertyOrder(60)]
119 public Conversation Conversation { get; set; }
120
121 [JsonPropertyName("relatesTo")]
122 [JsonPropertyOrder(70)]
123 public ConversationReference? RelatesTo { get; set; }
124
125 [JsonPropertyName("serviceUrl")]
126 [JsonPropertyOrder(80)]
127 public string? ServiceUrl { get; set; }
128
129 [JsonPropertyName("locale")]
130 [JsonPropertyOrder(90)]
131 public string? Locale { get; set; }
132
133 [JsonPropertyName("timestamp")]
134 [JsonPropertyOrder(100)]
135 public DateTime? Timestamp { get; set; }
136
137 [JsonPropertyName("localTimestamp")]
138 [JsonPropertyOrder(110)]
139 public DateTime? LocalTimestamp { get; set; }
140
141 [JsonPropertyName("entities")]
142 [JsonPropertyOrder(120)]
143 public IList<IEntity>? Entities { get; set; }
144
145 [JsonPropertyName("channelData")]
146 [JsonPropertyOrder(130)]
147 public ChannelData? ChannelData { get; set; }
148
149 [JsonIgnore]
150 [Experimental("ExperimentalTeamsTargeted")]
151 public bool IsTargeted { get; set; }
152
153 [JsonExtensionData]
154 public IDictionary<string, object?> Properties { get; set; } = new Dictionary<string, object?>();
155
156 [JsonConstructor]
157 public Activity(string type)
158 {
159 Type = new(type);
160 }
161
162 public Activity(ActivityType type)
163 {
164 Type = type;
165 }
166
167 public Activity(IActivity activity)
168 {
169 Id = activity.Id;
170 Type = activity.Type;
171 ReplyToId = activity.ReplyToId;
172 ChannelId = activity.ChannelId;
173 From = activity.From;
174 Recipient = activity.Recipient;
175 Conversation = activity.Conversation;
176 RelatesTo = activity.RelatesTo;
177 ServiceUrl = activity.ServiceUrl;
178 Locale = activity.Locale;
179 Timestamp = activity.Timestamp;
180 LocalTimestamp = activity.LocalTimestamp;
181 Entities = activity.Entities;
182 ChannelData = activity.ChannelData;
183 Properties = activity.Properties;
184 }
185
186 [JsonIgnore]
187 public bool IsStreaming => Entities?.Any(entity => entity.Type == "streaminfo" && entity is StreamInfoEntity) ?? false;
188
189 public object Clone() => MemberwiseClone();
190 public virtual Activity Copy() => (Activity)Clone();
191 public virtual string GetPath() => string.Join(".", ["Activity", Type.ToPrettyString()]);
192
193 public virtual Activity WithId(string value)
194 {
195 Id = value;
196 return this;
197 }
198
199 public virtual Activity WithReplyToId(string value)
200 {
201 ReplyToId = value;
202 return this;
203 }
204
205 public virtual Activity WithChannelId(ChannelId value)
206 {
207 ChannelId = value;
208 return this;
209 }
210
211 public virtual Activity WithFrom(Account value)
212 {
213 From = value;
214 return this;
215 }
216
217 public virtual Activity WithConversation(Conversation value)
218 {
219 Conversation = value;
220 return this;
221 }
222
223 public virtual Activity WithRelatesTo(ConversationReference value)
224 {
225 RelatesTo = value;
226 return this;
227 }
228
229 public virtual Activity WithRecipient(Account value)
230 {
231 #pragma warning disable ExperimentalTeamsTargeted
232 return WithRecipient(value, false);
233 #pragma warning restore ExperimentalTeamsTargeted
234 }
235
236 [Experimental("ExperimentalTeamsTargeted")]
237 public virtual Activity WithRecipient(Account value, bool isTargeted)
238 {
239 Recipient = value;
240 #pragma warning disable ExperimentalTeamsTargeted
241 Recipient.IsTargeted = null;
242 #pragma warning restore ExperimentalTeamsTargeted
243 return this;
244 }
245
246 [Experimental("ExperimentalTeamsTargeted")]
247 public virtual Activity WithRecipient(Account value, bool isTargeted)
248 {
249 Recipient = value;
250 #pragma warning disable ExperimentalTeamsTargeted
251 Recipient.IsTargeted = isTargeted ? true : null;
252 #pragma warning restore ExperimentalTeamsTargeted
253 return this;
254 }
255
256 public virtual Activity WithServiceUrl(string value)
257 {
258 ServiceUrl = value;
259 return this;
260 }
261
262 public virtual Activity WithTimestamp(DateTime value)
263 {
264 Timestamp = value;
265 return this;
266 }
267
268 public virtual Activity WithLocale(string value)
269 {
270 Locale = value;
271 return this;
272 }
273
274 public virtual Activity WithLocalTimestamp(DateTime value)
275 {
276 LocalTimestamp = value;
277 return this;
278 }
279
280 public virtual Activity WithData(ChannelData value)
281 {
282 ChannelData ??= new();
283 ChannelData.Merge(value);
284 return this;
285 }
286
287 public virtual Activity WithData(string key, object? value)
288 {
289 ChannelData ??= new();
290 ChannelData.Properties[key] = value;
291 return this;
292 }
293
294 public virtual Activity WithAppId(string value)
295 {
296 ChannelData ??= new();
297 ChannelData.App ??= new App() { Id = value };
298 return this;
299 }
300
301 /// <summary>
302 /// add an entity
303 /// </summary>
304 public virtual Activity AddEntity(params IEntity[] entities)
305 {
306 Entities ??= [];
307
308 foreach (var entity in entities)
309 {
310 Entities.Add(entity);
311 }
312
313 return this;
314 }
315
316 public virtual Activity UpdateEntity(IEntity oldEntity, IEntity newEntity)
317 {
318 if (Entities != null)
319 {
320 Entities.Remove(oldEntity);
321 }
322 else
323 {
324 Entities = [];
325 }
326
327 Entities.Add(newEntity);
328 return this;
329 }
330
331 /// <summary>
332 /// ensures a single root level message entity exists
333 /// </summary>
334 private IMessageEntity GetRootLevelMessageEntity()
335 {
336 var messageEntity = Entities?.FirstOrDefault(
337 e => e.Type == "https://schema.org/Message" && e.OType == "Message"
338 ) as IMessageEntity;
339
340 if (messageEntity is null)
341 {
342 messageEntity = new MessageEntity()
343 {
344 Type = "https://schema.org/Message",
345 OType = "Message",
346 OContext = "https://schema.org"
347 };
348
349 AddEntity(messageEntity);
350 }
351
352 return messageEntity;
353 }
354
355 /// <summary>
356 /// add the `Generated By AI` label
357 /// </summary>
358 public virtual Activity AddAIGenerated()
359 {
360 var messageEntity = GetRootLevelMessageEntity();
361 messageEntity.AdditionalType ??= [];
362
363 if (!messageEntity.AdditionalType.Contains("AIGeneratedContent"))
364 {
365 messageEntity.AdditionalType.Add("AIGeneratedContent");
366 }
367
368 return this;
369 }
370
371 /// <summary>
372 /// add content sensitivity label
373 /// </summary>
374 /// <param name="name">the content title</param>
375 /// <param name="description">the content description</param>
376 /// <param name="pattern">the pattern</param>
377 public virtual Activity AddSensitivityLabel(string name, string? description = null, DefinedTerm? pattern = null)
378 {
379 return AddEntity(new SensitiveUsageEntity()
380 {
381 Name = name,
382 Description = description,
383 Pattern = pattern
384 });
385 }
386
387 /// <summary>
388 /// enable/disable message feedback
389 /// </summary>
390 public virtual Activity AddFeedback(bool value = true)
391 {
392 ChannelData ??= new();
393 ChannelData.FeedbackLoopEnabled = value;
394 return this;
395 }
396
397 /// <summary>
398 /// add a citation
399 /// </summary>
400 public virtual Activity AddCitation(int position, CitationAppearance appearance)
401 {
402 var messageEntity = GetRootLevelMessageEntity();
403 var citationEntity = new CitationEntity(messageEntity);
404 citationEntity.Citation ??= [];
405 citationEntity.Citation.Add(new CitationEntity.Claim()
406 {
407 Position = position,
408 Appearance = appearance.ToDocument()
409 });
410
411 UpdateEntity(messageEntity, citationEntity);
412 return this;
413 }
414
415 public CommandActivity ToCommand() => (CommandActivity)this;
416 public CommandResultActivity ToCommandResult() => (CommandResultActivity)this;
417 public TypingActivity ToTyping() => (TypingActivity)this;
418 public InstallUpdateActivity ToInstallUpdate() => (InstallUpdateActivity)this;
419 public MessageActivity ToMessage() => (MessageActivity)this;
420 public MessageUpdateActivity ToMessageUpdate() => (MessageUpdateActivity)this;
421 public MessageDeleteActivity ToMessageDelete() => (MessageDeleteActivity)this;
422 public MessageReactionActivity ToMessageReaction() => (MessageReactionActivity)this;
423 public ConversationUpdateActivity ToConversationUpdate() => (ConversationUpdateActivity)this;
424 public EndOfConversationActivity ToEndOfConversation() => (EndOfConversationActivity)this;
425 public EventActivity ToEvent() => (EventActivity)this;
426 public InvokeActivity ToInvoke() => (InvokeActivity)this;
427
428 public Activity Merge(Activity from)
429 {
430 Id ??= from.Id;
431 ReplyToId ??= from.ReplyToId;
432 ChannelId ??= from.ChannelId;
433 From ??= from.From;
434 Recipient ??= from.Recipient;
435 Conversation ??= from.Conversation;
436 RelatesTo ??= from.RelatesTo;
437 ServiceUrl ??= from.ServiceUrl;
438 Locale ??= from.Locale;
439 Timestamp ??= from.Timestamp;
440 LocalTimestamp ??= from.LocalTimestamp;
441 AddEntity(from.Entities?.ToArray() ?? []);
442
443 #pragma warning disable ExperimentalTeamsTargeted
444 if (from.IsTargeted)
445 {
446 IsTargeted = true;
447 }
448 #pragma warning restore ExperimentalTeamsTargeted
449
450 if (from.ChannelData is not null)
451 {
452 WithData(from.ChannelData);
453 }
454
455 if (from.Properties is not null)
456 {
457 Properties ??= new Dictionary<string, object?>();
458
459 foreach (var kv in from.Properties)
460 {
461 Properties[kv.Key] = kv.Value;
462 }
463 }
464
465 return this;
466 }
467
468 public string ToQuoteReply()
469 {
470 var text = string.Empty;
471
472 if (this is MessageActivity message)
473 {
474 text = $"<p itemprop=\"preview\">{message.Text}</p>";
475 }
476
477 return $"""
478 <blockquote itemscope="" itemtype="http://schema.skype.com/Reply" itemid="{Id}">
479 <strong itemprop="mri" itemid="{From.Id}">
480 {From.Name}
481 </strong>
482 <span itemprop="time" itemid="{Id}"></span>
483 {text}
484 </blockquote>
485 """;
486 }
487
488 public override string ToString()
489 {
490 return JsonSerializer.Serialize(this, new JsonSerializerOptions()
491 {
492 WriteIndented = true,
493 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
494 });
495 }
496}