microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
kavin/agents-sdk-interop

Branches

Tags

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

Clone

HTTPS

Download ZIP

core/src/Microsoft.Teams.Core/Schema/CoreActivity.cs

265lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using System.Text.Json;
5using System.Text.Json.Serialization.Metadata;
6
7namespace Microsoft.Teams.Core.Schema;
8
9/// <summary>
10/// Represents a dictionary for storing extended properties as key-value pairs.
11/// </summary>
12public class ExtendedPropertiesDictionary : Dictionary<string, object?>
13{
14 /// <summary>
15 /// Initializes a new empty instance of the <see cref="ExtendedPropertiesDictionary"/> class.
16 /// </summary>
17 public ExtendedPropertiesDictionary() { }
18
19 /// <summary>
20 /// Initializes a new instance of the <see cref="ExtendedPropertiesDictionary"/> class by shallow-copying entries from another dictionary.
21 /// </summary>
22 public ExtendedPropertiesDictionary(IDictionary<string, object?> source) : base(source) { }
23
24 /// <summary>
25 /// Extracts and deserializes a value from the dictionary, removing the entry if found.
26 /// Returns the deserialized value, or default if the key is not present.
27 /// </summary>
28 public T? Extract<T>(string key)
29 {
30 if (!TryGetValue(key, out object? raw))
31 return default;
32
33 Remove(key);
34
35 if (raw is T typed)
36 return typed;
37
38 if (raw is System.Text.Json.JsonElement element)
39 return System.Text.Json.JsonSerializer.Deserialize<T>(element.GetRawText());
40
41 return default;
42 }
43
44 /// <summary>
45 /// Gets and deserializes a value from the dictionary without removing it.
46 /// Handles <see cref="System.Text.Json.JsonElement"/> values that result from deserialization.
47 /// </summary>
48 public T? Get<T>(string key)
49 {
50 if (!TryGetValue(key, out object? raw))
51 return default;
52
53 if (raw is T typed)
54 return typed;
55
56 if (raw is System.Text.Json.JsonElement element)
57 {
58 T? deserialized = System.Text.Json.JsonSerializer.Deserialize<T>(element.GetRawText());
59 this[key] = deserialized;
60 return deserialized;
61 }
62
63 return default;
64 }
65}
66
67/// <summary>
68/// Represents a core activity object that encapsulates the data and metadata for a bot interaction.
69/// </summary>
70/// <remarks>
71/// This class provides the foundational structure for bot activities including message exchanges,
72/// conversation updates, and other bot-related events. It supports serialization to and from JSON
73/// and includes extension properties for channel-specific data.
74/// Follows the Activity Protocol Specification: https://github.com/microsoft/Agents/blob/main/specs/activity/protocol-activity.md
75/// </remarks>
76public class CoreActivity
77{
78 /// <summary>
79 /// Gets or sets the type of the activity. See <see cref="ActivityType"/> for common values.
80 /// </summary>
81 /// <remarks>
82 /// Common activity types include "message", "conversationUpdate", "contactRelationUpdate", etc.
83 /// </remarks>
84 [JsonPropertyName("type")] public string Type { get; set; }
85 /// <summary>
86 /// Gets or sets the unique identifier for the channel on which this activity is occurring.
87 /// </summary>
88 [JsonPropertyName("channelId")] public string? ChannelId { get; set; }
89 /// <summary>
90 /// Gets or sets the unique identifier for the activity.
91 /// </summary>
92 [JsonPropertyName("id")] public string? Id { get; set; }
93 /// <summary>
94 /// Gets or sets the URL of the service endpoint for this activity.
95 /// </summary>
96 /// <remarks>
97 /// This URL is used to send responses back to the channel.
98 /// </remarks>
99 [JsonPropertyName("serviceUrl")] public Uri? ServiceUrl { get; set; }
100
101 /// <summary>
102 /// Gets or sets the identifier of the activity this activity is a reply to.
103 /// </summary>
104 [JsonPropertyName("replyToId")] public string? ReplyToId { get; set; }
105
106 /// <summary>
107 /// Gets or sets the conversation information for this activity.
108 /// </summary>
109 [JsonPropertyName("conversation")] public Conversation? Conversation { get; set; }
110
111 /// <summary>
112 /// Gets or sets the sender account for this activity.
113 /// </summary>
114 [JsonPropertyName("from")] public ConversationAccount? From { get; set; }
115
116 /// <summary>
117 /// Gets or sets the recipient account for this activity.
118 /// </summary>
119 [JsonPropertyName("recipient")] public ConversationAccount? Recipient { get; set; }
120
121 /// <summary>
122 /// Gets the extension data dictionary for storing additional properties not defined in the schema.
123 /// </summary>
124 [JsonExtensionData] public ExtendedPropertiesDictionary Properties { get; set; } = [];
125
126 /// <summary>
127 /// Gets the default JSON serializer options used for serializing and deserializing activities.
128 /// </summary>
129 /// <remarks>
130 /// Uses the source-generated JSON context for AOT-compatible serialization.
131 /// </remarks>
132 public static readonly JsonSerializerOptions DefaultJsonOptions = CoreActivityJsonContext.Default.Options;
133
134 /// <summary>
135 /// Gets the JSON serializer options used for reflection-based serialization of extended activity types.
136 /// </summary>
137 /// <remarks>
138 /// Uses reflection-based serialization to support custom activity types that extend CoreActivity.
139 /// This is used when serializing/deserializing types not registered in the source-generated context.
140 /// </remarks>
141 private static readonly JsonSerializerOptions ReflectionJsonOptions = new()
142 {
143 WriteIndented = true,
144 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
145 PropertyNamingPolicy = JsonNamingPolicy.CamelCase
146 };
147
148 /// <summary>
149 /// Creates a new instance of the <see cref="CoreActivity"/> class with the specified activity type.
150 /// Defaults to <see cref="ActivityType.Message"/>.
151 /// </summary>
152 /// <param name="type">The activity type. Defaults to "message".</param>
153 [JsonConstructor]
154 public CoreActivity(string type = ActivityType.Message)
155 {
156 Type = type;
157 }
158
159 /// <summary>
160 /// Creates a new instance of the <see cref="CoreActivity"/> class by copying properties from another activity.
161 /// </summary>
162 /// <param name="activity">The source activity to copy from.</param>
163 protected CoreActivity(CoreActivity activity)
164 {
165 ArgumentNullException.ThrowIfNull(activity);
166
167 Id = activity.Id;
168 ServiceUrl = activity.ServiceUrl;
169 ChannelId = activity.ChannelId;
170 Type = activity.Type;
171 Conversation = activity.Conversation is not null ? new Conversation(activity.Conversation.Id) { Properties = new ExtendedPropertiesDictionary(activity.Conversation.Properties) } : null;
172 From = activity.From is not null ? CloneConversationAccount(activity.From) : null;
173 Recipient = activity.Recipient is not null ? CloneConversationAccount(activity.Recipient) : null;
174 Properties = new ExtendedPropertiesDictionary(activity.Properties);
175 }
176
177#pragma warning disable ExperimentalTeamsTargeted
178 private static ConversationAccount CloneConversationAccount(ConversationAccount source) => new()
179 {
180 Id = source.Id,
181 Name = source.Name,
182 IsTargeted = source.IsTargeted,
183 AgenticAppId = source.AgenticAppId,
184 AgenticUserId = source.AgenticUserId,
185 AgenticAppBlueprintId = source.AgenticAppBlueprintId,
186 Properties = new ExtendedPropertiesDictionary(source.Properties)
187 };
188#pragma warning restore ExperimentalTeamsTargeted
189
190 /// <summary>
191 /// Serializes the current activity to a JSON string.
192 /// </summary>
193 /// <returns>A JSON string representation of the activity.</returns>
194 public virtual string ToJson()
195 => JsonSerializer.Serialize(this, CoreActivityJsonContext.Default.CoreActivity);
196
197 /// <summary>
198 /// Serializes the current activity to a JSON string using the specified <see cref="JsonTypeInfo{T}"/> for source-generated serialization.
199 /// </summary>
200 /// <typeparam name="T">The type of the activity to serialize. Must inherit from <see cref="CoreActivity"/>.</typeparam>
201 /// <param name="ops">The JSON type info that provides serialization metadata for type <typeparamref name="T"/>.</param>
202 /// <returns>A JSON string representation of the activity.</returns>
203 public string ToJson<T>(JsonTypeInfo<T> ops) where T : CoreActivity
204 => JsonSerializer.Serialize(this, ops);
205
206 /// <summary>
207 /// Serializes the specified activity instance to a JSON string using the default serialization options.
208 /// </summary>
209 /// <remarks>The serialization uses the default JSON options defined by DefaultJsonOptions. The resulting
210 /// JSON reflects the public properties of the activity instance.</remarks>
211 /// <typeparam name="T">The type of the activity to serialize. Must inherit from CoreActivity.</typeparam>
212 /// <param name="instance">The activity instance to serialize. Cannot be null.</param>
213 /// <returns>A JSON string representation of the specified activity instance.</returns>
214 public static string ToJson<T>(T instance) where T : CoreActivity
215 => JsonSerializer.Serialize<T>(instance, ReflectionJsonOptions);
216
217 /// <summary>
218 /// Deserializes a JSON string into a <see cref="CoreActivity"/> object.
219 /// </summary>
220 /// <param name="json">The JSON string to deserialize.</param>
221 /// <returns>A <see cref="CoreActivity"/> instance.</returns>
222 public static CoreActivity FromJsonString(string json)
223 => JsonSerializer.Deserialize(json, CoreActivityJsonContext.Default.CoreActivity)!;
224
225 /// <summary>
226 /// Asynchronously deserializes a JSON stream into a <see cref="CoreActivity"/> object.
227 /// </summary>
228 /// <param name="stream">The stream containing JSON data to deserialize.</param>
229 /// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
230 /// <returns>A task that represents the asynchronous operation. The task result contains the deserialized <see cref="CoreActivity"/> instance, or null if deserialization fails.</returns>
231 public static ValueTask<CoreActivity?> FromJsonStreamAsync(Stream stream, CancellationToken cancellationToken = default)
232 => JsonSerializer.DeserializeAsync(stream, CoreActivityJsonContext.Default.CoreActivity, cancellationToken);
233
234 /// <summary>
235 /// Asynchronously deserializes a JSON stream into an instance of type <typeparamref name="T"/> using the specified <see cref="JsonTypeInfo{T}"/> for source-generated serialization.
236 /// </summary>
237 /// <typeparam name="T">The type of the activity to deserialize. Must inherit from <see cref="CoreActivity"/>.</typeparam>
238 /// <param name="stream">The stream containing JSON data to deserialize.</param>
239 /// <param name="ops">The JSON type info that provides deserialization metadata for type <typeparamref name="T"/>.</param>
240 /// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
241 /// <returns>A <see cref="ValueTask{T}"/> representing the asynchronous operation. The result contains the deserialized activity, or null if deserialization fails.</returns>
242 public static ValueTask<T?> FromJsonStreamAsync<T>(Stream stream, JsonTypeInfo<T> ops, CancellationToken cancellationToken = default) where T : CoreActivity
243 => JsonSerializer.DeserializeAsync(stream, ops, cancellationToken);
244
245 /// <summary>
246 /// Asynchronously deserializes a JSON value from the specified stream into an instance of type T.
247 /// </summary>
248 /// <remarks>The caller is responsible for managing the lifetime of the provided stream. The method uses
249 /// default JSON serialization options.</remarks>
250 /// <typeparam name="T">The type of the object to deserialize. Must derive from CoreActivity.</typeparam>
251 /// <param name="stream">The stream containing the JSON data to deserialize. The stream must be readable and positioned at the start of
252 /// the JSON content.</param>
253 /// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
254 /// <returns>A ValueTask that represents the asynchronous operation. The result contains an instance of type T if
255 /// deserialization is successful; otherwise, null.</returns>
256 public static ValueTask<T?> FromJsonStreamAsync<T>(Stream stream, CancellationToken cancellationToken = default) where T : CoreActivity
257 => JsonSerializer.DeserializeAsync<T>(stream, ReflectionJsonOptions, cancellationToken);
258
259 /// <summary>
260 /// Creates a new instance of the <see cref="CoreActivityBuilder"/> to construct activity instances.
261 /// </summary>
262 /// <returns>A new <see cref="CoreActivityBuilder"/> instance.</returns>
263 public static CoreActivityBuilder CreateBuilder() => new();
264
265}
266