Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 38 additions & 18 deletions core/samples/TeamsBot/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.Teams.Bot.Apps;
using Microsoft.Teams.Bot.Apps.Handlers;
using Microsoft.Teams.Bot.Apps.Schema;
using Microsoft.Teams.Bot.Core.Schema;
using TeamsBot;

WebApplicationBuilder webAppBuilder = WebApplication.CreateSlimBuilder(args);
Expand All @@ -27,6 +28,22 @@ await context.SendActivityAsync(
{
TextFormat = TextFormats.Markdown
}, cancellationToken);


var helpActivity = TeamsActivity.CreateBuilder()
.WithType(TeamsActivityType.Message)
.WithText(WelcomeMessageMiddleware.WelcomeMessage, TextFormats.Markdown)
.WithSuggestedActions(new SuggestedActions()
{
To = [context.Activity.From?.Id!],
Actions = [
new SuggestedAction(ActionType.IMBack, "hello") { Value = "hello" },
new SuggestedAction(ActionType.IMBack, "feedback") { Value = "feedback" },
]
})
.Build();

await context.SendActivityAsync(helpActivity, cancellationToken);
});

// Pattern-based handler: matches "hello" (case-insensitive)
Expand Down Expand Up @@ -233,6 +250,27 @@ await context.TeamsBotApplication.Api.Conversations.Reactions.DeleteAsync(
await context.SendActivityAsync(taskActivity, cancellationToken);
});

teamsApp.OnMessage("(?i)^suggested$", async (context, cancellationToken) =>
{
var suggestedActions = new SuggestedActions()
{
To = [context.Activity.From?.Id!],
Actions = [
new SuggestedAction(ActionType.IMBack, "Option 1") { Value = "You chose option 1" },
new SuggestedAction(ActionType.IMBack, "Option 2") { Value = "You chose option 2" },
new SuggestedAction(ActionType.IMBack, "Option 3") { Value = "You chose option 3" }
]
};

var reply = TeamsActivity.CreateBuilder()
.WithType(TeamsActivityType.Message)
.WithText("Here are some suggested actions for you:")
.WithSuggestedActions(suggestedActions)
.Build();

await context.SendActivityAsync(reply, cancellationToken);
});

// Regex-based handler: matches commands starting with "/"
Regex commandRegex = Regexes.CommandRegex();
teamsApp.OnMessage(commandRegex, async (context, cancellationToken) =>
Expand All @@ -254,24 +292,6 @@ await context.TeamsBotApplication.Api.Conversations.Reactions.DeleteAsync(
}
});

//// Catch-all message handler: echoes the message back with a mention
//teamsApp.OnMessage(async (context, cancellationToken) =>
//{
// await context.SendTypingActivityAsync(cancellationToken);

// ArgumentNullException.ThrowIfNull(context.Activity.From);

// string replyText = $"You sent: `{context.Activity.Text}`. Type `help` to see available commands.";

// TeamsActivity ta = TeamsActivity.CreateBuilder()
// .WithType(TeamsActivityType.Message)
// .WithText(replyText)
// .AddMention(context.Activity.From)
// .Build();

// await context.SendActivityAsync(ta, cancellationToken);
//});

// ==================== MESSAGE LIFECYCLE ====================

teamsApp.OnMessageUpdate(async (context, cancellationToken) =>
Expand Down
1 change: 1 addition & 0 deletions core/samples/TeamsBot/WelcomeMessageMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ internal class WelcomeMessageMiddleware : ITurnMiddleware
- `card` - Send an Adaptive Card with a feedback form
- `feedback` - Feedback form with Adaptive Card action round-trip
- `task` - Open a task module dialog
- `suggested` - Suggested actions

** Commands**
- `/help` - Available slash commands
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Teams.Bot.Core.Schema;

Expand Down Expand Up @@ -71,6 +72,18 @@ protected MessageActivity(CoreActivity activity) : base(activity)
AttachmentLayout = attachmentLayout?.ToString();
activity.Properties.Remove("attachmentLayout");
}
if (activity.Properties.TryGetValue("suggestedActions", out object? suggestedActions) && suggestedActions != null)
{
if (suggestedActions is JsonElement je)
{
SuggestedActions = JsonSerializer.Deserialize<SuggestedActions>(je.GetRawText());
}
else
{
SuggestedActions = suggestedActions as SuggestedActions;
}
activity.Properties.Remove("suggestedActions");
}
/*
if (activity.Properties.TryGetValue("speak", out var speak))
{
Expand Down Expand Up @@ -122,6 +135,8 @@ protected MessageActivity(CoreActivity activity) : base(activity)
[JsonPropertyName("attachmentLayout")]
public string? AttachmentLayout { get; set; }



//TODO : Review properties
/*
/// <summary>
Expand Down Expand Up @@ -159,9 +174,6 @@ protected MessageActivity(CoreActivity activity) : base(activity)
/// </summary>
[JsonPropertyName("expiration")]
public string? Expiration { get; set; }

[JsonPropertyName("suggestedActions")]
public SuggestedActions? SuggestedActions { get; set; }
*/

}
Expand Down Expand Up @@ -263,19 +275,4 @@ public static class DeliveryModes
}


public class SuggestedActions
{
/// <summary>
/// Ids of the recipients that the actions should be shown to. These Ids are relative to the
/// channelId and a subset of all recipients of the activity
/// </summary>
[JsonPropertyName("to")]
public IList<string> To { get; set; } = [];

/// <summary>
/// Actions that can be shown to the user
/// </summary>
[JsonPropertyName("actions")]
public IList<Cards.Action> Actions { get; set; } = [];
}
*/
131 changes: 131 additions & 0 deletions core/src/Microsoft.Teams.Bot.Apps/Schema/SuggestedAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text.Json.Serialization;

namespace Microsoft.Teams.Bot.Apps.Schema;

/// <summary>
/// Represents a clickable action
/// </summary>
public class SuggestedAction
{
/// <summary>
/// Default constructor for JSON deserialization.
/// </summary>
public SuggestedAction()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="SuggestedAction"/> class with the specified type and title.
/// </summary>
/// <param name="type">The type of action. See <see cref="ActionType"/> for common values.</param>
/// <param name="title">The text description displayed on the button.</param>
public SuggestedAction(string type, string title)
{
Type = type;
Title = title;
}

/// <summary>
/// Gets or sets the type of action implemented by this button.
/// See <see cref="ActionType"/> for common values.
/// </summary>
[JsonPropertyName("type")]
public string? Type { get; set; }

/// <summary>
/// Gets or sets the text description which appears on the button.
/// </summary>
[JsonPropertyName("title")]
public string? Title { get; set; }

/// <summary>
/// Gets or sets the image URL which will appear on the button, next to the text label.
/// </summary>
[JsonPropertyName("image")]
public string? Image { get; set; }

/// <summary>
/// Gets or sets the text for this action.
/// </summary>
[JsonPropertyName("text")]
public string? Text { get; set; }

/// <summary>
/// Gets or sets the text to display in the chat feed if the button is clicked.
/// </summary>
[JsonPropertyName("displayText")]
public string? DisplayText { get; set; }

/// <summary>
/// Gets or sets the supplementary parameter for the action.
/// The content of this property depends on the action type.
/// </summary>
[JsonPropertyName("value")]
public object? Value { get; set; }

/// <summary>
/// Gets or sets the channel-specific data associated with this action.
/// </summary>
[JsonPropertyName("channelData")]
public object? ChannelData { get; set; }

/// <summary>
/// Gets or sets the alternate image text to be used in place of the image.
/// </summary>
[JsonPropertyName("imageAltText")]
public string? ImageAltText { get; set; }
}

/// <summary>
/// String constants for card action types.
/// </summary>
public static class ActionType
{
/// <summary>
/// Opens the specified URL in the browser.
/// </summary>
public const string OpenUrl = "openUrl";

/// <summary>
/// Sends a message back to the bot as if the user typed it (visible to all conversation members).
/// </summary>
public const string IMBack = "imBack";

/// <summary>
/// Sends a message back to the bot privately (not visible to other conversation members).
/// </summary>
public const string PostBack = "postBack";

/// <summary>
/// Plays the specified audio content.
/// </summary>
public const string PlayAudio = "playAudio";

/// <summary>
/// Plays the specified video content.
/// </summary>
public const string PlayVideo = "playVideo";

/// <summary>
/// Displays the specified image.
/// </summary>
public const string ShowImage = "showImage";

/// <summary>
/// Downloads the specified file.
/// </summary>
public const string DownloadFile = "downloadFile";

/// <summary>
/// Initiates a sign-in flow.
/// </summary>
public const string SignIn = "signin";

/// <summary>
/// Initiates a phone call.
/// </summary>
public const string Call = "call";
}
68 changes: 68 additions & 0 deletions core/src/Microsoft.Teams.Bot.Apps/Schema/SuggestedActions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text.Json.Serialization;

namespace Microsoft.Teams.Bot.Apps.Schema;

/// <summary>
/// Represents suggested actions that can be shown to the user as quick reply buttons.
/// </summary>
public class SuggestedActions
{
/// <summary>
/// Gets or sets the IDs of the recipients that the actions should be shown to.
/// These IDs are relative to the channelId and a subset of all recipients of the activity.
/// </summary>
[JsonPropertyName("to")]
public IList<string> To { get; set; } = [];

/// <summary>
/// Gets or sets the actions that can be shown to the user.
/// </summary>
[JsonPropertyName("actions")]
public IList<SuggestedAction> Actions { get; set; } = [];

/// <summary>
/// Adds recipients to the suggested actions.
/// </summary>
/// <param name="recipients">The recipient IDs to add.</param>
/// <returns>This instance for chaining.</returns>
public SuggestedActions AddRecipients(params string[] recipients)
{
ArgumentNullException.ThrowIfNull(recipients);
foreach (var to in recipients)
{
To.Add(to);
}

return this;
}

/// <summary>
/// Adds a single action to the suggested actions.
/// </summary>
/// <param name="action">The action to add.</param>
/// <returns>This instance for chaining.</returns>
public SuggestedActions AddAction(SuggestedAction action)
{
Actions.Add(action);
return this;
}

/// <summary>
/// Adds multiple actions to the suggested actions.
/// </summary>
/// <param name="actions">The actions to add.</param>
/// <returns>This instance for chaining.</returns>
public SuggestedActions AddActions(params SuggestedAction[] actions)
{
ArgumentNullException.ThrowIfNull(actions);
foreach (var action in actions)
{
Actions.Add(action);
}

return this;
}
}
7 changes: 7 additions & 0 deletions core/src/Microsoft.Teams.Bot.Apps/Schema/TeamsActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ internal TeamsActivity Rebase()
[JsonPropertyName("localTimezone")]
public string? LocalTimezone { get; set; }

/// <summary>
/// Gets or sets the suggested actions for the message.
/// </summary>
[JsonPropertyName("suggestedActions")]
public SuggestedActions? SuggestedActions { get; set; }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious to know the motivation behind moving this property to the TeamsActivity

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

b/c the builder works with TeamsActivity, then inheriting in MessageActivity. I have a bigger workitem to reconcile these two.



/// <summary>
/// Adds an entity to the activity's Entities collection.
/// </summary>
Expand Down
12 changes: 12 additions & 0 deletions core/src/Microsoft.Teams.Bot.Apps/Schema/TeamsActivityBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,18 @@ public TeamsActivityBuilder WithText(string text, string textFormat = "plain")
return this;
}

/// <summary>
/// With Suggested Actions
/// </summary>
/// <param name="suggestedActions"></param>
/// <returns></returns>
public TeamsActivityBuilder WithSuggestedActions(SuggestedActions suggestedActions)
{
ArgumentNullException.ThrowIfNull(_activity);
_activity.SuggestedActions = suggestedActions;
return this;
}

/// <summary>
/// Adds a mention to the activity.
/// </summary>
Expand Down
Loading
Loading