microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/sub-pr-338

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

237lines · modecode

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