microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
devtools-port-no-auth

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

486lines · 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 IsTargeted = isTargeted;
242 #pragma warning restore ExperimentalTeamsTargeted
243 return this;
244 }
245
246 public virtual Activity WithServiceUrl(string value)
247 {
248 ServiceUrl = value;
249 return this;
250 }
251
252 public virtual Activity WithTimestamp(DateTime value)
253 {
254 Timestamp = value;
255 return this;
256 }
257
258 public virtual Activity WithLocale(string value)
259 {
260 Locale = value;
261 return this;
262 }
263
264 public virtual Activity WithLocalTimestamp(DateTime value)
265 {
266 LocalTimestamp = value;
267 return this;
268 }
269
270 public virtual Activity WithData(ChannelData value)
271 {
272 ChannelData ??= new();
273 ChannelData.Merge(value);
274 return this;
275 }
276
277 public virtual Activity WithData(string key, object? value)
278 {
279 ChannelData ??= new();
280 ChannelData.Properties[key] = value;
281 return this;
282 }
283
284 public virtual Activity WithAppId(string value)
285 {
286 ChannelData ??= new();
287 ChannelData.App ??= new App() { Id = value };
288 return this;
289 }
290
291 /// <summary>
292 /// add an entity
293 /// </summary>
294 public virtual Activity AddEntity(params IEntity[] entities)
295 {
296 Entities ??= [];
297
298 foreach (var entity in entities)
299 {
300 Entities.Add(entity);
301 }
302
303 return this;
304 }
305
306 public virtual Activity UpdateEntity(IEntity oldEntity, IEntity newEntity)
307 {
308 if (Entities != null)
309 {
310 Entities.Remove(oldEntity);
311 }
312 else
313 {
314 Entities = [];
315 }
316
317 Entities.Add(newEntity);
318 return this;
319 }
320
321 /// <summary>
322 /// ensures a single root level message entity exists
323 /// </summary>
324 private IMessageEntity GetRootLevelMessageEntity()
325 {
326 var messageEntity = Entities?.FirstOrDefault(
327 e => e.Type == "https://schema.org/Message" && e.OType == "Message"
328 ) as IMessageEntity;
329
330 if (messageEntity is null)
331 {
332 messageEntity = new MessageEntity()
333 {
334 Type = "https://schema.org/Message",
335 OType = "Message",
336 OContext = "https://schema.org"
337 };
338
339 AddEntity(messageEntity);
340 }
341
342 return messageEntity;
343 }
344
345 /// <summary>
346 /// add the `Generated By AI` label
347 /// </summary>
348 public virtual Activity AddAIGenerated()
349 {
350 var messageEntity = GetRootLevelMessageEntity();
351 messageEntity.AdditionalType ??= [];
352
353 if (!messageEntity.AdditionalType.Contains("AIGeneratedContent"))
354 {
355 messageEntity.AdditionalType.Add("AIGeneratedContent");
356 }
357
358 return this;
359 }
360
361 /// <summary>
362 /// add content sensitivity label
363 /// </summary>
364 /// <param name="name">the content title</param>
365 /// <param name="description">the content description</param>
366 /// <param name="pattern">the pattern</param>
367 public virtual Activity AddSensitivityLabel(string name, string? description = null, DefinedTerm? pattern = null)
368 {
369 return AddEntity(new SensitiveUsageEntity()
370 {
371 Name = name,
372 Description = description,
373 Pattern = pattern
374 });
375 }
376
377 /// <summary>
378 /// enable/disable message feedback
379 /// </summary>
380 public virtual Activity AddFeedback(bool value = true)
381 {
382 ChannelData ??= new();
383 ChannelData.FeedbackLoopEnabled = value;
384 return this;
385 }
386
387 /// <summary>
388 /// add a citation
389 /// </summary>
390 public virtual Activity AddCitation(int position, CitationAppearance appearance)
391 {
392 var messageEntity = GetRootLevelMessageEntity();
393 var citationEntity = new CitationEntity(messageEntity);
394 citationEntity.Citation ??= [];
395 citationEntity.Citation.Add(new CitationEntity.Claim()
396 {
397 Position = position,
398 Appearance = appearance.ToDocument()
399 });
400
401 UpdateEntity(messageEntity, citationEntity);
402 return this;
403 }
404
405 public CommandActivity ToCommand() => (CommandActivity)this;
406 public CommandResultActivity ToCommandResult() => (CommandResultActivity)this;
407 public TypingActivity ToTyping() => (TypingActivity)this;
408 public InstallUpdateActivity ToInstallUpdate() => (InstallUpdateActivity)this;
409 public MessageActivity ToMessage() => (MessageActivity)this;
410 public MessageUpdateActivity ToMessageUpdate() => (MessageUpdateActivity)this;
411 public MessageDeleteActivity ToMessageDelete() => (MessageDeleteActivity)this;
412 public MessageReactionActivity ToMessageReaction() => (MessageReactionActivity)this;
413 public ConversationUpdateActivity ToConversationUpdate() => (ConversationUpdateActivity)this;
414 public EndOfConversationActivity ToEndOfConversation() => (EndOfConversationActivity)this;
415 public EventActivity ToEvent() => (EventActivity)this;
416 public InvokeActivity ToInvoke() => (InvokeActivity)this;
417
418 public Activity Merge(Activity from)
419 {
420 Id ??= from.Id;
421 ReplyToId ??= from.ReplyToId;
422 ChannelId ??= from.ChannelId;
423 From ??= from.From;
424 Recipient ??= from.Recipient;
425 Conversation ??= from.Conversation;
426 RelatesTo ??= from.RelatesTo;
427 ServiceUrl ??= from.ServiceUrl;
428 Locale ??= from.Locale;
429 Timestamp ??= from.Timestamp;
430 LocalTimestamp ??= from.LocalTimestamp;
431 AddEntity(from.Entities?.ToArray() ?? []);
432
433 #pragma warning disable ExperimentalTeamsTargeted
434 if (from.IsTargeted)
435 {
436 IsTargeted = true;
437 }
438 #pragma warning restore ExperimentalTeamsTargeted
439
440 if (from.ChannelData is not null)
441 {
442 WithData(from.ChannelData);
443 }
444
445 if (from.Properties is not null)
446 {
447 Properties ??= new Dictionary<string, object?>();
448
449 foreach (var kv in from.Properties)
450 {
451 Properties[kv.Key] = kv.Value;
452 }
453 }
454
455 return this;
456 }
457
458 public string ToQuoteReply()
459 {
460 var text = string.Empty;
461
462 if (this is MessageActivity message)
463 {
464 text = $"<p itemprop=\"preview\">{message.Text}</p>";
465 }
466
467 return $"""
468 <blockquote itemscope="" itemtype="http://schema.skype.com/Reply" itemid="{Id}">
469 <strong itemprop="mri" itemid="{From.Id}">
470 {From.Name}
471 </strong>
472 <span itemprop="time" itemid="{Id}"></span>
473 {text}
474 </blockquote>
475 """;
476 }
477
478 public override string ToString()
479 {
480 return JsonSerializer.Serialize(this, new JsonSerializerOptions()
481 {
482 WriteIndented = true,
483 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
484 });
485 }
486}