diff --git a/src/libraries/Core/Microsoft.Agents.Core/Models/Activity.cs b/src/libraries/Core/Microsoft.Agents.Core/Models/Activity.cs
index 6fcac7ef0..c7f17fc6e 100644
--- a/src/libraries/Core/Microsoft.Agents.Core/Models/Activity.cs
+++ b/src/libraries/Core/Microsoft.Agents.Core/Models/Activity.cs
@@ -14,7 +14,7 @@
namespace Microsoft.Agents.Core.Models
{
///
- public class Activity :
+ public partial class Activity :
IActivity,
IConversationUpdateActivity,
IContactRelationUpdateActivity,
@@ -142,6 +142,268 @@ public bool IsType(string type)
return string.Equals(type, Type, StringComparison.OrdinalIgnoreCase);
}
+ ///
+ public IActivity WithText(string text)
+ {
+ Text = text;
+ return this;
+ }
+
+ ///
+ public IActivity WithSpeak(string speak)
+ {
+ Speak = speak;
+ return this;
+ }
+
+ ///
+ public IActivity WithInputHint(string inputHint)
+ {
+ InputHint = inputHint;
+ return this;
+ }
+
+ ///
+ public IActivity WithSummary(string summary)
+ {
+ Summary = summary;
+ return this;
+ }
+
+ ///
+ public IActivity WithLocale(string locale)
+ {
+ Locale = locale;
+ return this;
+ }
+
+ ///
+ public IActivity WithTextFormat(string textFormat)
+ {
+ TextFormat = textFormat;
+ return this;
+ }
+
+ ///
+ public IActivity WithAttachmentLayout(string attachmentLayout)
+ {
+ AttachmentLayout = attachmentLayout;
+ return this;
+ }
+
+ ///
+ public IActivity WithDeliveryMode(string deliveryMode)
+ {
+ DeliveryMode = deliveryMode;
+ return this;
+ }
+
+ ///
+ public IActivity WithName(string name)
+ {
+ Name = name;
+ return this;
+ }
+
+ ///
+ public IActivity WithValue(object value)
+ {
+ Value = value;
+ return this;
+ }
+
+ ///
+ public IActivity WithValue(object value, string valueType)
+ {
+ Value = value;
+ ValueType = valueType;
+ return this;
+ }
+
+ ///
+ public IActivity WithSuggestedActions(SuggestedActions suggestedActions)
+ {
+ SuggestedActions = suggestedActions;
+ return this;
+ }
+
+ ///
+ public IActivity AddText(string text)
+ {
+ Text += text;
+ return this;
+ }
+
+ ///
+ public IActivity AddAttachment(params Attachment[] attachments)
+ {
+ if (attachments == null)
+ {
+ return this;
+ }
+
+ Attachments ??= [];
+ foreach (var attachment in attachments)
+ {
+ Attachments.Add(attachment);
+ }
+
+ return this;
+ }
+
+ ///
+ public IActivity AddCard(Card card) => card == null ? this : AddAttachment(card.ToAttachment());
+
+ ///
+ public IActivity AddEntity(params Entity[] entities)
+ {
+ if (entities == null)
+ {
+ return this;
+ }
+
+ Entities ??= [];
+ foreach (var entity in entities)
+ {
+ Entities.Add(entity);
+ }
+
+ return this;
+ }
+
+ ///
+ public bool IsMessage() => IsType(ActivityTypes.Message);
+
+ ///
+ public bool IsEvent() => IsType(ActivityTypes.Event);
+
+ ///
+ public bool IsInvoke() => IsType(ActivityTypes.Invoke);
+
+ ///
+ public bool IsTyping() => IsType(ActivityTypes.Typing);
+
+ ///
+ public bool IsConversationUpdate() => IsType(ActivityTypes.ConversationUpdate);
+
+ ///
+ public bool IsEndOfConversation() => IsType(ActivityTypes.EndOfConversation);
+
+ ///
+ public bool IsHandoff() => IsType(ActivityTypes.Handoff);
+
+ ///
+ public bool IsTrace() => IsType(ActivityTypes.Trace);
+
+ ///
+ public bool IsCommand() => IsType(ActivityTypes.Command);
+
+ ///
+ public bool IsCommandResult() => IsType(ActivityTypes.CommandResult);
+
+ ///
+ public IActivity AddMention(ChannelAccount account, string text = null, bool addText = true)
+ {
+ var mentionText = text ?? account?.Name;
+ var markup = $"{mentionText}";
+
+ if (addText)
+ {
+ Text = string.IsNullOrEmpty(Text) ? markup : $"{markup} {Text}";
+ }
+
+ return AddEntity(new Mention(mentioned: account, text: markup));
+ }
+
+ ///
+ public Mention[] GetMentions()
+ {
+ var result = new List();
+ if (Entities != null)
+ {
+ foreach (var entity in Entities)
+ {
+ if (entity is Mention mention)
+ {
+ result.Add(mention);
+ }
+ }
+ }
+
+ return [.. result];
+ }
+
+ ///
+ public Mention GetAccountMention(string accountId)
+ {
+ if (Entities == null || accountId == null)
+ {
+ return null;
+ }
+
+ foreach (var entity in Entities)
+ {
+ if (entity is Mention mention && mention.Mentioned?.Id == accountId)
+ {
+ return mention;
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ public bool IsRecipientMentioned()
+ {
+ return Recipient?.Id != null && GetAccountMention(Recipient.Id) != null;
+ }
+
+ ///
+ public string RemoveRecipientMention()
+ {
+ return this.RemoveMentionText(Recipient?.Id);
+ }
+
+ ///
+ public bool IsTargetedActivity()
+ {
+ if (Entities == null || Entities.Count == 0)
+ {
+ return false;
+ }
+
+ foreach (var entity in Entities)
+ {
+ if (entity.Type == EntityTypes.ActivityTreatment && entity is ActivityTreatment treatment && treatment.Treatment == ActivityTreatmentTypes.Targeted)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ public IActivity MakeTargetedActivity(ChannelAccount user = null)
+ {
+ if (IsTargetedActivity())
+ {
+ return this;
+ }
+
+ if (Recipient == null && user == null)
+ {
+ throw new InvalidOperationException("Cannot mark activity as targeted because both the Activity.Recipient and `user` argument are null. At least one must be provided.");
+ }
+
+ Entities ??= [];
+ Entities.Add(new ActivityTreatment() { Treatment = ActivityTreatmentTypes.Targeted });
+
+ Recipient = user ?? Recipient;
+
+ return this;
+ }
+
///
public string Type { get; set; }
diff --git a/src/libraries/Core/Microsoft.Agents.Core/Models/AdaptiveCardCard.cs b/src/libraries/Core/Microsoft.Agents.Core/Models/AdaptiveCardCard.cs
new file mode 100644
index 000000000..5ff08fbed
--- /dev/null
+++ b/src/libraries/Core/Microsoft.Agents.Core/Models/AdaptiveCardCard.cs
@@ -0,0 +1,51 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+#nullable disable
+
+using System.IO;
+using System.Text;
+
+namespace Microsoft.Agents.Core.Models
+{
+ /// A card that carries a raw Adaptive Card JSON payload.
+ ///
+ /// The JSON is stored verbatim in and unpacked into the attachment as nested
+ /// JSON when the activity is serialized.
+ ///
+ public class AdaptiveCardCard : Card
+ {
+ ///
+ /// The content type value of an .
+ ///
+ public const string ContentType = Models.ContentTypes.AdaptiveCard;
+
+ /// The Adaptive Card content as a JSON string.
+ public string Content;
+
+ /// Initializes a new instance of from an Adaptive Card JSON string.
+ /// The Adaptive Card content as a JSON string.
+ public AdaptiveCardCard(string json)
+ {
+ Content = json;
+ }
+
+ /// Initializes a new instance of from a stream containing Adaptive Card JSON.
+ /// A stream containing the Adaptive Card JSON. The stream is left open.
+ public AdaptiveCardCard(Stream stream)
+ {
+ using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true);
+ Content = reader.ReadToEnd();
+ }
+
+ ///
+ public override Attachment ToAttachment()
+ {
+ return new Attachment
+ {
+ ContentType = ContentType,
+ Content = Content
+ };
+ }
+ }
+}
diff --git a/src/libraries/Core/Microsoft.Agents.Core/Models/AnimationCard.cs b/src/libraries/Core/Microsoft.Agents.Core/Models/AnimationCard.cs
index 9ebf7f8e4..76fbe8b43 100644
--- a/src/libraries/Core/Microsoft.Agents.Core/Models/AnimationCard.cs
+++ b/src/libraries/Core/Microsoft.Agents.Core/Models/AnimationCard.cs
@@ -9,7 +9,7 @@
namespace Microsoft.Agents.Core.Models
{
/// An animation card (Ex: gif or short video clip).
- public class AnimationCard
+ public class AnimationCard : Card
{
/// Initializes a new instance of AnimationCard.
public AnimationCard()
@@ -57,7 +57,7 @@ public AnimationCard(string title = default, string subtitle = default, string t
///
/// The instance of .
/// The generated attachment.
- public Attachment ToAttachment()
+ public override Attachment ToAttachment()
{
return new Attachment
{
@@ -66,22 +66,6 @@ public Attachment ToAttachment()
};
}
- ///
- /// Creates a new message activity that includes this animation card as an attachment.
- ///
- /// Use this method to generate a message activity suitable for sending to a user, with
- /// the current animation card included as an attachment. The returned activity has its type set to .
- /// An representing a message activity with the animation card attached.
- public IActivity ToMessage()
- {
- return new Activity
- {
- Type = ActivityTypes.Message,
- Attachments = [ToAttachment()]
- };
- }
-
/// Title of this card.
public string Title { get; set; }
/// Subtitle of this card.
@@ -107,5 +91,12 @@ public IActivity ToMessage()
/// Supplementary parameter for this card.
[JsonConverter(typeof(Serialization.Converters.ObjectTypeConverter))]
public object Value { get; set; }
+
+ /// Adds a media URL and returns this card.
+ public AnimationCard AddMedia(MediaUrl media) { Media ??= []; Media.Add(media); return this; }
+ /// Adds a media URL by string and returns this card.
+ public AnimationCard AddMedia(string url, string profile = null) { Media ??= []; Media.Add(new MediaUrl(url, profile)); return this; }
+ /// Adds a button and returns this card.
+ public AnimationCard AddButton(CardAction button) { Buttons ??= []; Buttons.Add(button); return this; }
}
}
diff --git a/src/libraries/Core/Microsoft.Agents.Core/Models/AudioCard.cs b/src/libraries/Core/Microsoft.Agents.Core/Models/AudioCard.cs
index b758fff7b..fba653a46 100644
--- a/src/libraries/Core/Microsoft.Agents.Core/Models/AudioCard.cs
+++ b/src/libraries/Core/Microsoft.Agents.Core/Models/AudioCard.cs
@@ -9,7 +9,7 @@
namespace Microsoft.Agents.Core.Models
{
/// Audio card.
- public class AudioCard
+ public class AudioCard : Card
{
/// Initializes a new instance of AudioCard.
public AudioCard()
@@ -57,7 +57,7 @@ public AudioCard(string title = default, string subtitle = default, string text
///
/// The instance of .
/// The generated attachment.
- public Attachment ToAttachment()
+ public override Attachment ToAttachment()
{
return new Attachment
{
@@ -66,22 +66,6 @@ public Attachment ToAttachment()
};
}
- ///
- /// Creates a new message activity that includes this audio card as an attachment.
- ///
- /// Use this method to generate a message activity suitable for sending to a user, with
- /// the current audio card included as an attachment. The returned activity has its type set to .
- /// An representing a message activity with the audio card attached.
- public IActivity ToMessage()
- {
- return new Activity
- {
- Type = ActivityTypes.Message,
- Attachments = [ToAttachment()]
- };
- }
-
/// Title of this card.
public string Title { get; set; }
/// Subtitle of this card.
@@ -107,5 +91,12 @@ public IActivity ToMessage()
/// Supplementary parameter for this card.
[JsonConverter(typeof(Serialization.Converters.ObjectTypeConverter))]
public object Value { get; set; }
+
+ /// Adds a media URL and returns this card.
+ public AudioCard AddMedia(MediaUrl media) { Media ??= []; Media.Add(media); return this; }
+ /// Adds a media URL by string and returns this card.
+ public AudioCard AddMedia(string url, string profile = null) { Media ??= []; Media.Add(new MediaUrl(url, profile)); return this; }
+ /// Adds a button and returns this card.
+ public AudioCard AddButton(CardAction button) { Buttons ??= []; Buttons.Add(button); return this; }
}
}
diff --git a/src/libraries/Core/Microsoft.Agents.Core/Models/BasicCard.cs b/src/libraries/Core/Microsoft.Agents.Core/Models/BasicCard.cs
index 4aa217229..7c6f4ebcd 100644
--- a/src/libraries/Core/Microsoft.Agents.Core/Models/BasicCard.cs
+++ b/src/libraries/Core/Microsoft.Agents.Core/Models/BasicCard.cs
@@ -3,11 +3,13 @@
#nullable disable
+using System;
using System.Collections.Generic;
namespace Microsoft.Agents.Core.Models
{
/// A basic card.
+ [Obsolete("BasicCard is not an Activity Protocol card type (it has no content type) and will be removed in a future release.")]
public class BasicCard
{
/// Initializes a new instance of BasicCard.
@@ -46,5 +48,16 @@ public BasicCard(string title = default, string subtitle = default, string text
public IList Buttons { get; set; }
/// A clickable action.
public CardAction Tap { get; set; }
+
+ /// Adds an image and returns this card.
+ public BasicCard AddImage(CardImage image) { Images ??= []; Images.Add(image); return this; }
+ /// Adds an image by URL and returns this card.
+ public BasicCard AddImage(string url, string alt = null) { Images ??= []; Images.Add(new CardImage(url, alt)); return this; }
+ /// Adds a button and returns this card.
+ public BasicCard AddButton(CardAction button) { Buttons ??= []; Buttons.Add(button); return this; }
+ /// Adds a button by title/type/value and returns this card.
+ public BasicCard AddButton(string title, string type = ActionTypes.ImBack, object value = null) { Buttons ??= []; Buttons.Add(new CardAction(type: type, title: title, value: value ?? title)); return this; }
+ /// Adds one or more buttons and returns this card.
+ public BasicCard AddButtons(params CardAction[] buttons) { if (buttons == null) { return this; } Buttons ??= []; foreach (var button in buttons) { Buttons.Add(button); } return this; }
}
}
diff --git a/src/libraries/Core/Microsoft.Agents.Core/Models/Card.cs b/src/libraries/Core/Microsoft.Agents.Core/Models/Card.cs
new file mode 100644
index 000000000..9484e0d4c
--- /dev/null
+++ b/src/libraries/Core/Microsoft.Agents.Core/Models/Card.cs
@@ -0,0 +1,39 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+#nullable disable
+
+namespace Microsoft.Agents.Core.Models
+{
+ ///
+ /// Base class for rich cards that can be sent to a user as an .
+ ///
+ ///
+ /// Each concrete card implements to wrap itself in an attachment using its own
+ /// content type. builds on that to produce a ready-to-send message activity.
+ ///
+ public abstract class Card
+ {
+ ///
+ /// Creates a new that wraps this card.
+ ///
+ /// The generated attachment.
+ public abstract Attachment ToAttachment();
+
+ ///
+ /// Creates a new message activity that includes this card as an attachment.
+ ///
+ /// Use this method to generate a message activity suitable for sending to a user, with
+ /// the current card included as an attachment. The returned activity has its type set to .
+ /// An representing a message activity with the card attached.
+ public virtual IActivity ToMessage()
+ {
+ return new Activity
+ {
+ Type = ActivityTypes.Message,
+ Attachments = [ToAttachment()]
+ };
+ }
+ }
+}
diff --git a/src/libraries/Core/Microsoft.Agents.Core/Models/HeroCard.cs b/src/libraries/Core/Microsoft.Agents.Core/Models/HeroCard.cs
index 81a692038..3752daf5e 100644
--- a/src/libraries/Core/Microsoft.Agents.Core/Models/HeroCard.cs
+++ b/src/libraries/Core/Microsoft.Agents.Core/Models/HeroCard.cs
@@ -8,7 +8,7 @@
namespace Microsoft.Agents.Core.Models
{
/// A Hero card (card with a single, large image).
- public class HeroCard
+ public class HeroCard : Card
{
/// Initializes a new instance of HeroCard.
public HeroCard()
@@ -44,7 +44,7 @@ public HeroCard(string title = default, string subtitle = default, string text =
///
/// The instance of .
/// The generated attachment.
- public Attachment ToAttachment()
+ public override Attachment ToAttachment()
{
return new Attachment
{
@@ -53,22 +53,6 @@ public Attachment ToAttachment()
};
}
- ///
- /// Creates a new message activity that includes this hero card as an attachment.
- ///
- /// Use this method to generate a message activity suitable for sending to a user, with
- /// the current hero card included as an attachment. The returned activity has its type set to .
- /// An representing a message activity with the hero card attached.
- public IActivity ToMessage()
- {
- return new Activity
- {
- Type = ActivityTypes.Message,
- Attachments = [ToAttachment()]
- };
- }
-
/// Title of the card.
public string Title { get; set; }
/// Subtitle of the card.
@@ -81,5 +65,16 @@ public IActivity ToMessage()
public IList Buttons { get; set; }
/// A clickable action.
public CardAction Tap { get; set; }
+
+ /// Adds an image and returns this card.
+ public HeroCard AddImage(CardImage image) { Images ??= []; Images.Add(image); return this; }
+ /// Adds an image by URL and returns this card.
+ public HeroCard AddImage(string url, string alt = null) { Images ??= []; Images.Add(new CardImage(url, alt)); return this; }
+ /// Adds a button and returns this card.
+ public HeroCard AddButton(CardAction button) { Buttons ??= []; Buttons.Add(button); return this; }
+ /// Adds a button by title/type/value and returns this card.
+ public HeroCard AddButton(string title, string type = ActionTypes.ImBack, object value = null) { Buttons ??= []; Buttons.Add(new CardAction(type: type, title: title, value: value ?? title)); return this; }
+ /// Adds one or more buttons and returns this card.
+ public HeroCard AddButtons(params CardAction[] buttons) { if (buttons == null) { return this; } Buttons ??= []; foreach (var button in buttons) { Buttons.Add(button); } return this; }
}
}
diff --git a/src/libraries/Core/Microsoft.Agents.Core/Models/IActivity.cs b/src/libraries/Core/Microsoft.Agents.Core/Models/IActivity.cs
index 7bd5d9e06..65a42ac92 100644
--- a/src/libraries/Core/Microsoft.Agents.Core/Models/IActivity.cs
+++ b/src/libraries/Core/Microsoft.Agents.Core/Models/IActivity.cs
@@ -270,6 +270,8 @@ public interface IActivity
///
/// The type field controls the meaning of each Activity, and are by convention short strings (e.g. "message").
+ /// In most cases you should assign one of the well-known values from
+ /// (e.g. ) rather than a raw string literal.
/// Senders may define their own application-layer types, although they are encouraged to choose values that are
/// unlikely to collide with future well-defined values. If senders use URIs as type values, they SHOULD NOT
/// implement URI ladder comparisons to establish equivalence.
@@ -336,6 +338,137 @@ public interface IActivity
/// The new trace Activity.
IActivity CreateTrace(string name, object value = null, string valueType = null, [CallerMemberName] string label = null);
+ /// Sets and returns this activity.
+ IActivity WithText(string text);
+
+ /// Sets and returns this activity.
+ IActivity WithSpeak(string speak);
+
+ /// Sets and returns this activity.
+ IActivity WithInputHint(string inputHint);
+
+ /// Sets and returns this activity.
+ IActivity WithSummary(string summary);
+
+ /// Sets and returns this activity.
+ IActivity WithLocale(string locale);
+
+ /// Sets and returns this activity.
+ IActivity WithTextFormat(string textFormat);
+
+ /// Sets and returns this activity.
+ IActivity WithAttachmentLayout(string attachmentLayout);
+
+ /// Sets and returns this activity.
+ IActivity WithDeliveryMode(string deliveryMode);
+
+ /// Sets and returns this activity.
+ IActivity WithName(string name);
+
+ /// Sets and returns this activity.
+ IActivity WithValue(object value);
+
+ /// Sets both and and returns this activity.
+ IActivity WithValue(object value, string valueType);
+
+ /// Sets and returns this activity.
+ IActivity WithSuggestedActions(SuggestedActions suggestedActions);
+
+ /// Appends the given text to and returns this activity.
+ IActivity AddText(string text);
+
+ /// Adds one or more attachments to and returns this activity.
+ IActivity AddAttachment(params Attachment[] attachments);
+
+ /// Adds a as an attachment and returns this activity.
+ IActivity AddCard(Card card);
+
+ /// Adds one or more entities to and returns this activity.
+ IActivity AddEntity(params Entity[] entities);
+
+ ///
+ /// Adds a entity for the given account and, optionally,
+ /// prepends the mention markup to .
+ ///
+ /// The account being mentioned.
+ /// Optional display text for the mention. Defaults to .
+ /// When true (default), the <at> mention markup is prepended to the activity text.
+ /// This activity.
+ IActivity AddMention(ChannelAccount account, string text = null, bool addText = true);
+
+ ///
+ /// Gets the for a specific account, if this activity mentions it.
+ ///
+ /// The to look for.
+ /// The matching ; or null if the account is not mentioned.
+ Mention GetAccountMention(string accountId);
+
+ ///
+ /// Indicates whether the activity's is mentioned in the activity.
+ ///
+ /// true if the recipient is mentioned; otherwise, false.
+ bool IsRecipientMentioned();
+
+ /// Returns true when the activity type is .
+ bool IsMessage();
+
+ /// Returns true when the activity type is .
+ bool IsEvent();
+
+ /// Returns true when the activity type is .
+ bool IsInvoke();
+
+ /// Returns true when the activity type is .
+ bool IsTyping();
+
+ /// Returns true when the activity type is .
+ bool IsConversationUpdate();
+
+ /// Returns true when the activity type is .
+ bool IsEndOfConversation();
+
+ /// Returns true when the activity type is .
+ bool IsHandoff();
+
+ /// Returns true when the activity type is .
+ bool IsTrace();
+
+ /// Returns true when the activity type is .
+ bool IsCommand();
+
+ /// Returns true when the activity type is .
+ bool IsCommandResult();
+
+ ///
+ /// Resolves the mentions from the entities of this activity.
+ ///
+ /// The array of mentions; or an empty array, if none are found.
+ /// Intended for use with a message activity, where the activity is
+ /// .
+ ///
+ Mention[] GetMentions();
+
+ ///
+ /// Determines whether this activity represents a targeted activity treatment.
+ ///
+ /// true if the activity contains an entity with a
+ /// treatment of Targeted; otherwise, false.
+ bool IsTargetedActivity();
+
+ ///
+ /// Marks this activity as targeted by adding a targeted treatment entity to its collection.
+ ///
+ /// Optional user to set as the recipient. Replaces when provided.
+ /// This activity.
+ IActivity MakeTargetedActivity(ChannelAccount user = null);
+
+ ///
+ /// Removes the recipient mention text from the property.
+ ///
+ /// Use with caution because this function alters the on the activity.
+ /// The new value.
+ string RemoveRecipientMention();
+
///
/// Gets properties that are not otherwise defined by the type but that
/// might appear in the serialized REST JSON object.
diff --git a/src/libraries/Core/Microsoft.Agents.Core/Models/IActivityExtensions.cs b/src/libraries/Core/Microsoft.Agents.Core/Models/IActivityExtensions.cs
index 9892fa031..1bd62c883 100644
--- a/src/libraries/Core/Microsoft.Agents.Core/Models/IActivityExtensions.cs
+++ b/src/libraries/Core/Microsoft.Agents.Core/Models/IActivityExtensions.cs
@@ -1,8 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
-using System;
-using System.Collections.Generic;
using System.Text.Json;
using Microsoft.Agents.Core.Serialization;
@@ -23,40 +21,6 @@ public static string ToJson(this IActivity activity)
return JsonSerializer.Serialize(activity, ProtocolJsonSerializer.SerializationOptions);
}
- ///
- /// Resolves the mentions from the entities of this activity.
- ///
- /// The array of mentions; or an empty array, if none are found.
- /// This method is defined on the class, but is only intended
- /// for use with a message activity, where the activity is set to
- /// .
- ///
- public static Mention[] GetMentions(this IActivity activity)
- {
- var result = new List();
- if (activity.Entities != null)
- {
- foreach (var entity in activity.Entities)
- {
- if (entity is Mention mention)
- {
- result.Add(mention);
- }
- }
- }
- return [.. result];
- }
-
- ///
- /// Remove recipient mention text from Text property.
- /// Use with caution because this function is altering the text on the Activity.
- ///
- /// new .Text property value.
- public static string RemoveRecipientMention(this T activity) where T : IActivity
- {
- return activity.RemoveMentionText(activity.Recipient?.Id);
- }
-
///
/// Clone the activity to a new instance of activity.
///
@@ -118,62 +82,5 @@ public static bool TryGetChannelData(this IActivity activity, out T instance)
return false;
}
}
-
- ///
- /// Determines whether the specified activity represents a targeted activity treatment.
- ///
- /// Use this method to identify activities that are specifically marked as targeted
- /// treatments. This can be useful for filtering or processing activities based on their treatment
- /// type.
- /// The activity to evaluate for targeted treatment. Cannot be null.
- /// true if the activity contains an entity of type ActivityTreatment with a treatment of Targeted; otherwise,
- /// false.
- public static bool IsTargetedActivity(this IActivity activity)
- {
- if (activity.Entities == null || activity.Entities.Count == 0)
- {
- return false;
- }
-
- foreach (var entity in activity.Entities)
- {
- if (entity.Type == EntityTypes.ActivityTreatment && entity is ActivityTreatment treatment && treatment.Treatment == ActivityTreatmentTypes.Targeted)
- {
- return true;
- }
- }
-
- return false;
- }
-
- ///
- /// Marks the specified activity as targeted by adding a targeted treatment entity to its collection.
- ///
- /// This method ensures that the activity's Entities collection is initialized before
- /// adding the targeted treatment. Use this extension to indicate that an activity should be processed or
- /// interpreted as targeted in downstream workflows.
- /// The activity to be marked as targeted. Cannot be null.
- /// The user to be set as the recipient. This replaces Activity.Recipient if provided.
- public static IActivity MakeTargetedActivity(this IActivity activity, ChannelAccount user = null)
- {
- AssertionHelpers.ThrowIfNull(activity, nameof(activity));
-
- if (activity.IsTargetedActivity())
- {
- return activity;
- }
-
- if (activity.Recipient == null && user == null)
- {
- throw new InvalidOperationException("Cannot mark activity as targeted because both the Activity.Recipient and `user` argument are null. At least one must be provided.");
- }
-
- activity.Entities ??= [];
- activity.Entities.Add(new ActivityTreatment() { Treatment = ActivityTreatmentTypes.Targeted });
-
- activity.Recipient = user ?? activity.Recipient;
-
- return activity;
- }
}
}
\ No newline at end of file
diff --git a/src/libraries/Core/Microsoft.Agents.Core/Models/MediaCard.cs b/src/libraries/Core/Microsoft.Agents.Core/Models/MediaCard.cs
index be892a4a9..99def419a 100644
--- a/src/libraries/Core/Microsoft.Agents.Core/Models/MediaCard.cs
+++ b/src/libraries/Core/Microsoft.Agents.Core/Models/MediaCard.cs
@@ -3,12 +3,14 @@
#nullable disable
+using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.Core.Models
{
/// Media card.
+ [Obsolete("MediaCard is a structural base without an Activity Protocol content type. Use AnimationCard, AudioCard, or VideoCard instead. Will be removed in a future release.")]
public class MediaCard
{
/// Initializes a new instance of MediaCard.
@@ -72,5 +74,12 @@ public MediaCard(string title = default, string subtitle = default, string text
/// Supplementary parameter for this card.
[JsonConverter(typeof(Serialization.Converters.ObjectTypeConverter))]
public object Value { get; set; }
+
+ /// Adds a media URL and returns this card.
+ public MediaCard AddMedia(MediaUrl media) { Media ??= []; Media.Add(media); return this; }
+ /// Adds a media URL by string and returns this card.
+ public MediaCard AddMedia(string url, string profile = null) { Media ??= []; Media.Add(new MediaUrl(url, profile)); return this; }
+ /// Adds a button and returns this card.
+ public MediaCard AddButton(CardAction button) { Buttons ??= []; Buttons.Add(button); return this; }
}
}
diff --git a/src/libraries/Core/Microsoft.Agents.Core/Models/OAuthCard.cs b/src/libraries/Core/Microsoft.Agents.Core/Models/OAuthCard.cs
index b7292c5d0..8296af10d 100644
--- a/src/libraries/Core/Microsoft.Agents.Core/Models/OAuthCard.cs
+++ b/src/libraries/Core/Microsoft.Agents.Core/Models/OAuthCard.cs
@@ -8,7 +8,7 @@
namespace Microsoft.Agents.Core.Models
{
/// A card representing a request to perform a sign in via OAuth.
- public class OAuthCard
+ public class OAuthCard : Card
{
/// Initializes a new instance of OAuthCard.
public OAuthCard()
@@ -37,7 +37,7 @@ public OAuthCard(string text = default, string connectionName = default, IList
/// The instance of .
/// The generated attachment.
- public Attachment ToAttachment()
+ public override Attachment ToAttachment()
{
return new Attachment
{
diff --git a/src/libraries/Core/Microsoft.Agents.Core/Models/ReceiptCard.cs b/src/libraries/Core/Microsoft.Agents.Core/Models/ReceiptCard.cs
index 43e48e83b..7e41b6c80 100644
--- a/src/libraries/Core/Microsoft.Agents.Core/Models/ReceiptCard.cs
+++ b/src/libraries/Core/Microsoft.Agents.Core/Models/ReceiptCard.cs
@@ -8,7 +8,7 @@
namespace Microsoft.Agents.Core.Models
{
/// A receipt card.
- public class ReceiptCard
+ public class ReceiptCard : Card
{
/// Initializes a new instance of ReceiptCard.
public ReceiptCard()
@@ -49,7 +49,7 @@ public ReceiptCard(string title = default, IList facts = default, IList
/// The instance of .
/// The generated attachment.
- public Attachment ToAttachment()
+ public override Attachment ToAttachment()
{
return new Attachment
{
@@ -58,22 +58,6 @@ public Attachment ToAttachment()
};
}
- ///
- /// Creates a new message activity that includes this receipt card as an attachment.
- ///
- /// Use this method to generate a message activity suitable for sending to a user, with
- /// the current receipt card included as an attachment. The returned activity has its type set to .
- /// An representing a message activity with the receipt card attached.
- public IActivity ToMessage()
- {
- return new Activity
- {
- Type = ActivityTypes.Message,
- Attachments = [ToAttachment()]
- };
- }
-
/// Title of the card.
public string Title { get; set; }
@@ -91,5 +75,12 @@ public IActivity ToMessage()
public string Vat { get; set; }
/// Set of actions applicable to the current card.
public IList Buttons { get; set; }
+
+ /// Adds a fact and returns this card.
+ public ReceiptCard AddFact(Fact fact) { Facts ??= []; Facts.Add(fact); return this; }
+ /// Adds a receipt item and returns this card.
+ public ReceiptCard AddItem(ReceiptItem item) { Items ??= []; Items.Add(item); return this; }
+ /// Adds a button and returns this card.
+ public ReceiptCard AddButton(CardAction button) { Buttons ??= []; Buttons.Add(button); return this; }
}
}
diff --git a/src/libraries/Core/Microsoft.Agents.Core/Models/SigninCard.cs b/src/libraries/Core/Microsoft.Agents.Core/Models/SigninCard.cs
index 568713102..5a4814b2c 100644
--- a/src/libraries/Core/Microsoft.Agents.Core/Models/SigninCard.cs
+++ b/src/libraries/Core/Microsoft.Agents.Core/Models/SigninCard.cs
@@ -8,7 +8,7 @@
namespace Microsoft.Agents.Core.Models
{
/// A card representing a request to sign in.
- public class SigninCard
+ public class SigninCard : Card
{
public SigninCard()
{
@@ -34,7 +34,7 @@ public SigninCard(string text = default, IList buttons = default)
///
/// The instance of .
/// The generated attachment.
- public Attachment ToAttachment()
+ public override Attachment ToAttachment()
{
return new Attachment
{
diff --git a/src/libraries/Core/Microsoft.Agents.Core/Models/SuggestedActions.cs b/src/libraries/Core/Microsoft.Agents.Core/Models/SuggestedActions.cs
index 943e6f2fb..901b77778 100644
--- a/src/libraries/Core/Microsoft.Agents.Core/Models/SuggestedActions.cs
+++ b/src/libraries/Core/Microsoft.Agents.Core/Models/SuggestedActions.cs
@@ -44,5 +44,53 @@ public SuggestedActions(IEnumerable to, IEnumerable actions)
public IList To { get; set; }
/// Actions that can be shown to the user.
public IList Actions { get; set; }
+
+ ///
+ /// Adds a single action to and returns this instance.
+ ///
+ public SuggestedActions AddAction(CardAction action)
+ {
+ Actions ??= [];
+ Actions.Add(action);
+ return this;
+ }
+
+ ///
+ /// Adds one or more actions to and returns this instance.
+ ///
+ public SuggestedActions AddActions(params CardAction[] actions)
+ {
+ if (actions == null)
+ {
+ return this;
+ }
+
+ Actions ??= [];
+ foreach (var action in actions)
+ {
+ Actions.Add(action);
+ }
+
+ return this;
+ }
+
+ ///
+ /// Adds one or more recipient ids to and returns this instance.
+ ///
+ public SuggestedActions AddRecipients(params string[] recipients)
+ {
+ if (recipients == null)
+ {
+ return this;
+ }
+
+ To ??= [];
+ foreach (var recipient in recipients)
+ {
+ To.Add(recipient);
+ }
+
+ return this;
+ }
}
}
diff --git a/src/libraries/Core/Microsoft.Agents.Core/Models/ThumbnailCard.cs b/src/libraries/Core/Microsoft.Agents.Core/Models/ThumbnailCard.cs
index ed2540520..1fbae9178 100644
--- a/src/libraries/Core/Microsoft.Agents.Core/Models/ThumbnailCard.cs
+++ b/src/libraries/Core/Microsoft.Agents.Core/Models/ThumbnailCard.cs
@@ -8,7 +8,7 @@
namespace Microsoft.Agents.Core.Models
{
/// A thumbnail card (card with a single, small thumbnail image).
- public class ThumbnailCard
+ public class ThumbnailCard : Card
{
public ThumbnailCard()
{
@@ -43,7 +43,7 @@ public ThumbnailCard(string title = default, string subtitle = default, string t
///
/// The instance of .
/// The generated attachment.
- public Attachment ToAttachment()
+ public override Attachment ToAttachment()
{
return new Attachment
{
@@ -52,22 +52,6 @@ public Attachment ToAttachment()
};
}
- ///
- /// Creates a new message activity that includes this thumbnail card as an attachment.
- ///
- /// Use this method to generate a message activity suitable for sending to a user, with
- /// the current thumbnail card included as an attachment. The returned activity has its type set to .
- /// An representing a message activity with the thumbnail card attached.
- public IActivity ToMessage()
- {
- return new Activity
- {
- Type = ActivityTypes.Message,
- Attachments = [ToAttachment()]
- };
- }
-
/// Title of the card.
public string Title { get; set; }
/// Subtitle of the card.
@@ -80,5 +64,16 @@ public IActivity ToMessage()
public IList Buttons { get; set; }
/// A clickable action.
public CardAction Tap { get; set; }
+
+ /// Adds an image and returns this card.
+ public ThumbnailCard AddImage(CardImage image) { Images ??= []; Images.Add(image); return this; }
+ /// Adds an image by URL and returns this card.
+ public ThumbnailCard AddImage(string url, string alt = null) { Images ??= []; Images.Add(new CardImage(url, alt)); return this; }
+ /// Adds a button and returns this card.
+ public ThumbnailCard AddButton(CardAction button) { Buttons ??= []; Buttons.Add(button); return this; }
+ /// Adds a button by title/type/value and returns this card.
+ public ThumbnailCard AddButton(string title, string type = ActionTypes.ImBack, object value = null) { Buttons ??= []; Buttons.Add(new CardAction(type: type, title: title, value: value ?? title)); return this; }
+ /// Adds one or more buttons and returns this card.
+ public ThumbnailCard AddButtons(params CardAction[] buttons) { if (buttons == null) { return this; } Buttons ??= []; foreach (var button in buttons) { Buttons.Add(button); } return this; }
}
}
diff --git a/src/libraries/Core/Microsoft.Agents.Core/Models/VideoCard.cs b/src/libraries/Core/Microsoft.Agents.Core/Models/VideoCard.cs
index 281852b72..125820922 100644
--- a/src/libraries/Core/Microsoft.Agents.Core/Models/VideoCard.cs
+++ b/src/libraries/Core/Microsoft.Agents.Core/Models/VideoCard.cs
@@ -9,7 +9,7 @@
namespace Microsoft.Agents.Core.Models
{
/// Video card.
- public class VideoCard
+ public class VideoCard : Card
{
public VideoCard()
{
@@ -56,7 +56,7 @@ public VideoCard(string title = default, string subtitle = default, string text
///
/// The instance of .
/// The generated attachment.
- public Attachment ToAttachment()
+ public override Attachment ToAttachment()
{
return new Attachment
{
@@ -65,22 +65,6 @@ public Attachment ToAttachment()
};
}
- ///
- /// Creates a new message activity that includes this video card as an attachment.
- ///
- /// Use this method to generate a message activity suitable for sending to a user, with
- /// the current video card included as an attachment. The returned activity has its type set to .
- /// An representing a message activity with the video card attached.
- public IActivity ToMessage()
- {
- return new Activity
- {
- Type = ActivityTypes.Message,
- Attachments = [ToAttachment()]
- };
- }
-
/// Title of this card.
public string Title { get; set; }
/// Subtitle of this card.
@@ -107,5 +91,12 @@ public IActivity ToMessage()
/// Supplementary parameter for this card.
[JsonConverter(typeof(Serialization.Converters.ObjectTypeConverter))]
public object Value { get; set; }
+
+ /// Adds a media URL and returns this card.
+ public VideoCard AddMedia(MediaUrl media) { Media ??= []; Media.Add(media); return this; }
+ /// Adds a media URL by string and returns this card.
+ public VideoCard AddMedia(string url, string profile = null) { Media ??= []; Media.Add(new MediaUrl(url, profile)); return this; }
+ /// Adds a button and returns this card.
+ public VideoCard AddButton(CardAction button) { Buttons ??= []; Buttons.Add(button); return this; }
}
}
diff --git a/src/libraries/Core/Microsoft.Agents.Core/Serialization/CoreJsonContext.cs b/src/libraries/Core/Microsoft.Agents.Core/Serialization/CoreJsonContext.cs
index 8df26ba7a..6f005be30 100644
--- a/src/libraries/Core/Microsoft.Agents.Core/Serialization/CoreJsonContext.cs
+++ b/src/libraries/Core/Microsoft.Agents.Core/Serialization/CoreJsonContext.cs
@@ -32,9 +32,12 @@ namespace Microsoft.Agents.Core.Serialization
[JsonSerializable(typeof(AdaptiveCardInvokeValue))]
[JsonSerializable(typeof(AadResourceUrls))]
[JsonSerializable(typeof(AIEntity))]
+ [JsonSerializable(typeof(AnimationCard))]
[JsonSerializable(typeof(Attachment))]
[JsonSerializable(typeof(AudioCard))]
+#pragma warning disable CS0618 // BasicCard is obsolete but retained for serialization compatibility
[JsonSerializable(typeof(BasicCard))]
+#pragma warning restore CS0618
[JsonSerializable(typeof(CardImage))]
[JsonSerializable(typeof(ChannelAccount))]
[JsonSerializable(typeof(Citation))]
@@ -50,7 +53,9 @@ namespace Microsoft.Agents.Core.Serialization
[JsonSerializable(typeof(HeroCard))]
[JsonSerializable(typeof(InnerHttpError))]
[JsonSerializable(typeof(InvokeResponse))]
+#pragma warning disable CS0618 // MediaCard is obsolete but retained for serialization compatibility
[JsonSerializable(typeof(MediaCard))]
+#pragma warning restore CS0618
[JsonSerializable(typeof(MediaEventValue))]
[JsonSerializable(typeof(MediaUrl))]
[JsonSerializable(typeof(Mention))]
@@ -71,6 +76,7 @@ namespace Microsoft.Agents.Core.Serialization
[JsonSerializable(typeof(SuggestedActions))]
[JsonSerializable(typeof(TextHighlight))]
[JsonSerializable(typeof(Thing))]
+ [JsonSerializable(typeof(ThumbnailCard))]
[JsonSerializable(typeof(TokenExchangeInvokeRequest))]
[JsonSerializable(typeof(TokenExchangeInvokeResponse))]
[JsonSerializable(typeof(TokenExchangeRequest))]
diff --git a/src/tests/Microsoft.Agents.Model.Tests/ActivityBuilderTests.cs b/src/tests/Microsoft.Agents.Model.Tests/ActivityBuilderTests.cs
new file mode 100644
index 000000000..8df6b2aae
--- /dev/null
+++ b/src/tests/Microsoft.Agents.Model.Tests/ActivityBuilderTests.cs
@@ -0,0 +1,118 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.Agents.Core.Models;
+using Xunit;
+
+namespace Microsoft.Agents.Model.Tests
+{
+ public class ActivityBuilderTests
+ {
+ [Fact]
+ public void FluentSettersChainAndSetProperties()
+ {
+ var activity = Activity.CreateMessageActivity()
+ .WithText("hello")
+ .WithSpeak("hello there")
+ .WithInputHint(InputHints.ExpectingInput)
+ .WithSummary("a summary")
+ .WithLocale("en-US")
+ .WithTextFormat(TextFormatTypes.Markdown)
+ .WithAttachmentLayout(AttachmentLayoutTypes.Carousel)
+ .WithDeliveryMode(DeliveryModes.Normal)
+ .WithName("theName")
+ .WithValue("theValue", "theValueType");
+
+ Assert.Equal("hello", activity.Text);
+ Assert.Equal("hello there", activity.Speak);
+ Assert.Equal(InputHints.ExpectingInput, activity.InputHint);
+ Assert.Equal("a summary", activity.Summary);
+ Assert.Equal("en-US", activity.Locale);
+ Assert.Equal(TextFormatTypes.Markdown, activity.TextFormat);
+ Assert.Equal(AttachmentLayoutTypes.Carousel, activity.AttachmentLayout);
+ Assert.Equal(DeliveryModes.Normal, activity.DeliveryMode);
+ Assert.Equal("theName", activity.Name);
+ Assert.Equal("theValue", activity.Value);
+ Assert.Equal("theValueType", activity.ValueType);
+ }
+
+ [Fact]
+ public void AddTextAppends()
+ {
+ var activity = Activity.CreateMessageActivity().WithText("foo").AddText("bar");
+ Assert.Equal("foobar", activity.Text);
+ }
+
+ [Fact]
+ public void AddAttachmentAddsAttachments()
+ {
+ var activity = Activity.CreateMessageActivity()
+ .AddAttachment(new Attachment(contentType: "a"), new Attachment(contentType: "b"));
+
+ Assert.Equal(2, activity.Attachments.Count);
+ Assert.Equal("a", activity.Attachments[0].ContentType);
+ Assert.Equal("b", activity.Attachments[1].ContentType);
+ }
+
+ [Fact]
+ public void AddAttachmentWrapsCards()
+ {
+ var activity = Activity.CreateMessageActivity()
+ .AddCard(new HeroCard(title: "hero"))
+ .AddCard(new ThumbnailCard(title: "thumb"))
+ .AddCard(new OAuthCard())
+ .AddCard(new SigninCard());
+
+ Assert.Equal(4, activity.Attachments.Count);
+ Assert.Equal(HeroCard.ContentType, activity.Attachments[0].ContentType);
+ Assert.IsType(activity.Attachments[0].Content);
+ Assert.Equal(ThumbnailCard.ContentType, activity.Attachments[1].ContentType);
+ Assert.Equal(OAuthCard.ContentType, activity.Attachments[2].ContentType);
+ Assert.Equal(SigninCard.ContentType, activity.Attachments[3].ContentType);
+ }
+
+ [Fact]
+ public void AddEntityAddsEntities()
+ {
+ var activity = Activity.CreateMessageActivity().AddEntity(new Entity("myType"));
+ Assert.Single(activity.Entities);
+ Assert.Equal("myType", activity.Entities[0].Type);
+ }
+
+ [Fact]
+ public void AddMentionAddsEntityAndText()
+ {
+ var account = new ChannelAccount(id: "u1", name: "User One");
+ var activity = Activity.CreateMessageActivity().WithText("hi").AddMention(account);
+
+ Assert.Equal("User One hi", activity.Text);
+ var mention = Assert.IsType(activity.Entities[0]);
+ Assert.Equal("u1", mention.Mentioned.Id);
+ Assert.Equal("User One", mention.Text);
+ }
+
+ [Fact]
+ public void AddMentionCanSkipText()
+ {
+ var account = new ChannelAccount(id: "u1", name: "User One");
+ var activity = Activity.CreateMessageActivity().WithText("hi").AddMention(account, text: "Custom", addText: false);
+
+ Assert.Equal("hi", activity.Text);
+ var mention = Assert.IsType(activity.Entities[0]);
+ Assert.Equal("Custom", mention.Text);
+ }
+
+ [Fact]
+ public void IsTypeHelpers()
+ {
+ Assert.True(Activity.CreateMessageActivity().IsMessage());
+ Assert.True(Activity.CreateTypingActivity().IsTyping());
+ Assert.True(Activity.CreateEventActivity().IsEvent());
+ Assert.True(Activity.CreateInvokeActivity().IsInvoke());
+ Assert.True(Activity.CreateConversationUpdateActivity().IsConversationUpdate());
+ Assert.True(Activity.CreateEndOfConversationActivity().IsEndOfConversation());
+ Assert.True(Activity.CreateHandoffActivity().IsHandoff());
+ Assert.False(Activity.CreateMessageActivity().IsInvoke());
+ }
+ }
+}
diff --git a/src/tests/Microsoft.Agents.Model.Tests/BasicCardTests.cs b/src/tests/Microsoft.Agents.Model.Tests/BasicCardTests.cs
index f8e7f21ed..c4ce8fca2 100644
--- a/src/tests/Microsoft.Agents.Model.Tests/BasicCardTests.cs
+++ b/src/tests/Microsoft.Agents.Model.Tests/BasicCardTests.cs
@@ -5,6 +5,8 @@
using System.Collections.Generic;
using Xunit;
+#pragma warning disable CS0618 // Type or member is obsolete - exercising obsolete BasicCard
+
namespace Microsoft.Agents.Model.Tests
{
public class BasicCardTests
diff --git a/src/tests/Microsoft.Agents.Model.Tests/BuilderHelpersTests.cs b/src/tests/Microsoft.Agents.Model.Tests/BuilderHelpersTests.cs
new file mode 100644
index 000000000..ba479e531
--- /dev/null
+++ b/src/tests/Microsoft.Agents.Model.Tests/BuilderHelpersTests.cs
@@ -0,0 +1,52 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.Agents.Core.Models;
+using Xunit;
+
+namespace Microsoft.Agents.Model.Tests
+{
+ public class BuilderHelpersTests
+ {
+ [Fact]
+ public void SuggestedActionsFluentAdders()
+ {
+ var suggested = new SuggestedActions()
+ .AddRecipients("r1", "r2")
+ .AddAction(new CardAction(title: "a"))
+ .AddActions(new CardAction(title: "b"), new CardAction(title: "c"));
+
+ Assert.Equal(new[] { "r1", "r2" }, suggested.To);
+ Assert.Equal(3, suggested.Actions.Count);
+ Assert.Equal("a", suggested.Actions[0].Title);
+ Assert.Equal("c", suggested.Actions[2].Title);
+ }
+
+ [Fact]
+ public void GetAccountMentionReturnsMatch()
+ {
+ var account = new ChannelAccount(id: "u1", name: "User One");
+ var activity = Activity.CreateMessageActivity().AddMention(account);
+
+ var mention = activity.GetAccountMention("u1");
+ Assert.NotNull(mention);
+ Assert.Equal("u1", mention.Mentioned.Id);
+
+ Assert.Null(activity.GetAccountMention("other"));
+ Assert.Null(activity.GetAccountMention(null));
+ }
+
+ [Fact]
+ public void IsRecipientMentioned()
+ {
+ var recipient = new ChannelAccount(id: "bot", name: "Bot");
+ var activity = Activity.CreateMessageActivity();
+ activity.Recipient = recipient;
+
+ Assert.False(activity.IsRecipientMentioned());
+
+ activity.AddMention(recipient);
+ Assert.True(activity.IsRecipientMentioned());
+ }
+ }
+}
diff --git a/src/tests/Microsoft.Agents.Model.Tests/CardBuilderTests.cs b/src/tests/Microsoft.Agents.Model.Tests/CardBuilderTests.cs
new file mode 100644
index 000000000..6c9655215
--- /dev/null
+++ b/src/tests/Microsoft.Agents.Model.Tests/CardBuilderTests.cs
@@ -0,0 +1,127 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System.IO;
+using System.Text;
+using Microsoft.Agents.Core.Models;
+using Microsoft.Agents.Core.Serialization;
+using Xunit;
+
+#pragma warning disable CS0618 // Type or member is obsolete - exercising obsolete BasicCard/MediaCard builders
+
+namespace Microsoft.Agents.Model.Tests
+{
+ public class CardBuilderTests
+ {
+ [Fact]
+ public void HeroCardFluentBuilders()
+ {
+ var card = new HeroCard { Title = "t", Subtitle = "s", Text = "txt", Tap = new CardAction(type: ActionTypes.OpenUrl) }
+ .AddImage("https://img", "alt")
+ .AddImage(new CardImage("https://img2"))
+ .AddButton("Yes", value: "yes")
+ .AddButton(new CardAction(type: ActionTypes.PostBack, title: "No"))
+ .AddButtons(new CardAction(title: "A"), new CardAction(title: "B"));
+
+ Assert.Equal("t", card.Title);
+ Assert.Equal("s", card.Subtitle);
+ Assert.Equal("txt", card.Text);
+ Assert.Equal(ActionTypes.OpenUrl, card.Tap.Type);
+ Assert.Equal(2, card.Images.Count);
+ Assert.Equal("https://img", card.Images[0].Url);
+ Assert.Equal("alt", card.Images[0].Alt);
+ Assert.Equal(4, card.Buttons.Count);
+ Assert.Equal("Yes", card.Buttons[0].Title);
+ Assert.Equal("yes", card.Buttons[0].Value);
+ }
+
+ [Fact]
+ public void ThumbnailAndBasicCardBuilders()
+ {
+ var thumb = new ThumbnailCard { Title = "t" }.AddImage("u").AddButton("b");
+ Assert.Equal("t", thumb.Title);
+ Assert.Single(thumb.Images);
+ Assert.Single(thumb.Buttons);
+
+ var basic = new BasicCard { Text = "x" }.AddImage(new CardImage("u")).AddButton(new CardAction(title: "b"));
+ Assert.Equal("x", basic.Text);
+ Assert.Single(basic.Images);
+ Assert.Single(basic.Buttons);
+ }
+
+ [Fact]
+ public void MediaCardBuildersAddMediaAndButtons()
+ {
+ var animation = new AnimationCard { Title = "a" }.AddMedia("https://m").AddButton(new CardAction(title: "b"));
+ Assert.Equal("a", animation.Title);
+ Assert.Single(animation.Media);
+ Assert.Equal("https://m", animation.Media[0].Url);
+ Assert.Single(animation.Buttons);
+
+ var audio = new AudioCard().AddMedia(new MediaUrl("https://a"));
+ Assert.Single(audio.Media);
+
+ var video = new VideoCard().AddMedia("https://v", "profile");
+ Assert.Equal("profile", video.Media[0].Profile);
+
+ var media = new MediaCard { Text = "m" }.AddMedia("https://x");
+ Assert.Equal("m", media.Text);
+ Assert.Single(media.Media);
+ }
+
+ [Fact]
+ public void ReceiptCardBuilders()
+ {
+ var receipt = new ReceiptCard { Title = "r", Total = "$10", Tax = "$1", Tap = new CardAction(type: ActionTypes.OpenUrl) }
+ .AddFact(new Fact("key", "value"))
+ .AddItem(new ReceiptItem(title: "item"))
+ .AddButton(new CardAction(title: "b"));
+
+ Assert.Equal("r", receipt.Title);
+ Assert.Equal("$10", receipt.Total);
+ Assert.Equal("$1", receipt.Tax);
+ Assert.Equal(ActionTypes.OpenUrl, receipt.Tap.Type);
+ Assert.Single(receipt.Facts);
+ Assert.Single(receipt.Items);
+ Assert.Single(receipt.Buttons);
+ }
+
+ [Fact]
+ public void AdaptiveCardCardFromJsonToAttachment()
+ {
+ var json = "{\"type\":\"AdaptiveCard\",\"version\":\"1.4\"}";
+ var card = new AdaptiveCardCard(json);
+
+ Assert.Equal(json, card.Content);
+
+ var attachment = card.ToAttachment();
+ Assert.Equal(ContentTypes.AdaptiveCard, attachment.ContentType);
+ Assert.Equal(json, attachment.Content);
+ }
+
+ [Fact]
+ public void AdaptiveCardCardFromStreamToAttachment()
+ {
+ var json = "{\"type\":\"AdaptiveCard\",\"version\":\"1.4\"}";
+ using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
+ var card = new AdaptiveCardCard(stream);
+
+ Assert.Equal(json, card.Content);
+ // Stream is left open.
+ Assert.True(stream.CanRead);
+ }
+
+ [Fact]
+ public void AdaptiveCardCardSerializesContentAsNestedJson()
+ {
+ var json = "{\"type\":\"AdaptiveCard\",\"version\":\"1.4\"}";
+ var activity = ((IActivity)Activity.CreateMessageActivity()).AddCard(new AdaptiveCardCard(json));
+
+ var serialized = ProtocolJsonSerializer.ToJson(activity);
+
+ // Content is unpacked into nested JSON (an object), not an escaped string.
+ Assert.Contains("\"content\":{\"type\":\"AdaptiveCard\"", serialized);
+ Assert.Equal(ContentTypes.AdaptiveCard, activity.Attachments[0].ContentType);
+ }
+ }
+}
diff --git a/src/tests/Microsoft.Agents.Model.Tests/IActivityExtensionsTests.cs b/src/tests/Microsoft.Agents.Model.Tests/IActivityExtensionsTests.cs
index 2092d0d1f..b945e5958 100644
--- a/src/tests/Microsoft.Agents.Model.Tests/IActivityExtensionsTests.cs
+++ b/src/tests/Microsoft.Agents.Model.Tests/IActivityExtensionsTests.cs
@@ -118,10 +118,10 @@ public void MakeTargetedActivity_AlreadyTargeted_IsIdempotent()
}
[Fact]
- public void MakeTargetedActivity_NullActivity_ThrowsArgumentNullException()
+ public void MakeTargetedActivity_NullActivity_Throws()
{
IActivity activity = null;
- Assert.Throws(() => activity.MakeTargetedActivity());
+ Assert.Throws(() => activity.MakeTargetedActivity());
}
}
}
diff --git a/src/tests/Microsoft.Agents.Model.Tests/MediaTests.cs b/src/tests/Microsoft.Agents.Model.Tests/MediaTests.cs
index 082b6f976..b7a9e8e64 100644
--- a/src/tests/Microsoft.Agents.Model.Tests/MediaTests.cs
+++ b/src/tests/Microsoft.Agents.Model.Tests/MediaTests.cs
@@ -5,6 +5,8 @@
using System.Collections.Generic;
using Xunit;
+#pragma warning disable CS0618 // Type or member is obsolete - exercising obsolete MediaCard
+
namespace Microsoft.Agents.Model.Tests
{
public class MediaTests