diff --git a/msteams-platform/bots/how-to/conversations/send-proactive-messages.md b/msteams-platform/bots/how-to/conversations/send-proactive-messages.md index 7120a93cb70..a8ca65197c0 100644 --- a/msteams-platform/bots/how-to/conversations/send-proactive-messages.md +++ b/msteams-platform/bots/how-to/conversations/send-proactive-messages.md @@ -1,6 +1,6 @@ --- title: Send proactive messages -description: Learn how to send proactive messages with your Teams bot, install your app using Microsoft Graph, and check code samples based on Bot Framework SDK v4. +description: Learn how to send proactive messages with your Teams bot using the Teams SDK (Teams AI Library) ms.topic: article ms.owner: angovil ms.localizationpriority: high @@ -18,7 +18,7 @@ A proactive message is any message sent by a bot that isn't in response to a req > [!IMPORTANT] > -> * To send proactive message, it's recommended to start with [building notification bot with JavaScript](../../../sbs-gs-notificationbot.yml) or [incoming webhook notification sample](https://github.com/OfficeDev/TeamsFx-Samples/tree/dev/incoming-webhook-notification). To get started, download [Microsoft 365 Agents Toolkit](https://marketplace.visualstudio.com/items?itemName=TeamsDevApp.ms-teams-vscode-extension) (previously known as Teams Toolkit). For more information, see [Microsoft 365 Agents Toolkit documents](../../../toolkit/agents-toolkit-fundamentals.md). +> * To send proactive message, it's recommended to start with [Teams SDK - Proactive Messaging](/microsoftteams/platform/teams-sdk/essentials/sending-messages/proactive-messaging) or [incoming webhook notification sample](https://github.com/OfficeDev/TeamsFx-Samples/tree/dev/incoming-webhook-notification). To get started, download [Microsoft 365 Agents Toolkit](https://marketplace.visualstudio.com/items?itemName=TeamsDevApp.ms-teams-vscode-extension) (previously known as Teams Toolkit). For more information, see [Microsoft 365 Agents Toolkit documents](../../../toolkit/agents-toolkit-fundamentals.md). > > * Bots are available in [Government Community Cloud (GCC), GCC High, Department of Defense (DoD)](../../../concepts/cloud-overview.md#teams-app-capabilities), and [Teams operated by 21Vianet](../../../concepts/sovereign-cloud.md) environments. For proactive messages, the bots must use the following end points for government cloud environments:
- GCC: `https://smba.infra.gcc.teams.microsoft.com/teams`
- GCC High: `https://smba.infra.gov.teams.microsoft.us/teams`
- DoD: `https://smba.infra.dod.teams.microsoft.us/teams` @@ -26,7 +26,7 @@ To send a proactive message to a user, a group chat, or a team, your bot must ha You can [proactively install your app using Microsoft Graph](#proactively-install-your-app-using-graph) in a team, if necessary, or use a [custom app policy](/microsoftteams/teams-custom-app-policies-and-settings) to install an app in your teams and for organization's users. For certain scenarios, you must proactively install your app using Graph. For a user to receive proactive messages, install the app for the user or make the user a part of a team in which the app is installed. -Sending a proactive message is different from sending a regular message. There's no active `turnContext` to use for a reply. You must create the conversation before sending the message. For example, a new one-on-one chat or a new conversation thread in a channel. You can't create a new group chat or a new channel in a team with proactive messaging. +Sending a proactive message is different from sending a regular message. Proactive messages are sent via app.Send() outside an activity handler. The SDK creates the conversation automatically when you call app.Send(). You need a `conversationId`, the SDK resolves the service URL automatically. For example, a new one-on-one chat or a new conversation thread in a channel. You can't create a new group chat or a new channel in a team with proactive messaging. To send a proactive message, follow these steps: @@ -45,7 +45,7 @@ You can create a new conversation with a user or a conversation thread in a chan * When your app is installed in a particular context, you receive an [`onMembersAdded` activity](~/bots/how-to/conversations/subscribe-to-conversation-events.md). * When a new user is added to a context where your app is installed, you receive an `onMembersAdded` activity. -* Every event that the bot receives contains the required information, which you can get from the bot context (TurnContext object). +* Every event that the bot receives contains the required information, which you can get from the bot context (activity context). * You can retrieve the [list of channels](~/bots/how-to/get-teams-context.md) in a team where your app is installed. * You can retrieve the [list of members](~/bots/how-to/get-teams-context.md) of a team where your app is installed. @@ -64,7 +64,7 @@ Create the conversation, after you have the user or channel information. ## Create the conversation -You can create the conversation if it doesn't exist, or you don't know the `conversationId`. Create the conversation only once and store the `conversationId` value or `conversationReference` object. +You can create the conversation if it doesn't exist or if you don't know the `conversationId`. Create the conversation only once, and store the resulting `conversationId` for future proactive messages. To [create the conversation](/azure/bot-service/rest-api/bot-framework-rest-connector-api-reference#create-conversation), you need a `aadObjectId` or `userId`, `tenantId`, and `serviceUrl`. @@ -100,7 +100,7 @@ After you get the appropriate address information, you can send your message. ## Send the message -Now that you have the right address information, you can send your message. If you're using the SDK, you must use the `continueConversation` method, and the `conversationId` and `tenantId` to make a direct API call. To send your message, set the `conversationParameters`. See the [samples](#samples) section or use one of the samples listed in the [code sample](#code-sample) section. +Now that you have the right address information, you can send your message. If you're using the SDK, you must use the `app.Send()` method, and the `conversationId` to make a direct API call. To send your message, set the `conversationParameters`. See the [samples](#samples) section or use one of the samples listed in the [code sample](#code-sample) section. > [!NOTE] > Teams doesn't support sending proactive messages using email or User Principal Name (UPN). @@ -185,7 +185,7 @@ To update or delete a proactive message sent by a notification only bot: 1. Keep track of the sent messages by storing their message IDs or conversation references when sending the proactive message. -1. Use `UpdateActivityAsync` or `DeleteActivityAsync` methods to update or delete the original message. +1. Use `context.Api.Conversations.Activities.UpdateAsync(conversationId, activityId, updatedActivity)` or `context.Api.Conversations.Activities.DeleteAsync(conversationId, activityId)` methods to update or delete the original message. ### Scheduled messages @@ -234,67 +234,33 @@ Ensure that you authenticate and have a [bearer token](/azure/bot-service/rest-a ## Samples -The following code shows how to send proactive messages: +The following code shows how to send proactive messages using the Teams SDK (Teams AI Library): # [C#](#tab/dotnet) -* [SDK reference](/dotnet/api/microsoft.bot.builder.cloudadapterbase.continueconversationasync?view=botbuilder-dotnet-stable&preserve-view=true#microsoft-bot-builder-cloudadapterbase-continueconversationasync(system-string-microsoft-bot-schema-activity-microsoft-bot-builder-botcallbackhandler-system-threading-cancellationtoken)) +* [SDK reference](https://microsoft.github.io/teams-sdk/csharp/essentials/sending-messages/proactive-messaging) * [Sample code reference](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/graph-meeting-notification/csharp/MeetingNotification/Controllers/NotificationController.cs#L112) ```csharp -[Route("api/notify")] -[ApiController] -public class NotifyController : ControllerBase +// Save the conversation ID and schedule a proactive reminder on install +teams.OnInstall(async context => { - private readonly IBotFrameworkHttpAdapter _adapter; - private readonly string _appId; - private readonly ConcurrentDictionary _conversationReferences; - - public NotifyController(IBotFrameworkHttpAdapter adapter, IConfiguration configuration, ConcurrentDictionary conversationReferences) - { - _adapter = adapter; - _conversationReferences = conversationReferences; - _appId = configuration["MicrosoftAppId"] ?? string.Empty; - } - - public async Task Get() - { - foreach (var conversationReference in _conversationReferences.Values) - { - var newReference = new ConversationReference() - { - Bot = new ChannelAccount() - { - Id = conversationReference.Bot.Id - }, - Conversation = new ConversationAccount() - { - Id = conversationReference.Conversation.Id - }, - ServiceUrl = conversationReference.ServiceUrl, - }; - - // Sends a proactive message from the bot to a conversation. - await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, newReference, BotCallback, default(CancellationToken)); - } - - // Let the caller know proactive messages have been sent. - return new ContentResult() - { - Content = "

Proactive messages have been sent.

", - ContentType = "text/html", - StatusCode = (int)HttpStatusCode.OK, - }; - } - - private async Task BotCallback(ITurnContext turnContext, CancellationToken cancellationToken) + context.Storage.Set(context.Activity.From.AadObjectId!, context.Activity.Conversation.Id); + await context.Send("Hi! I am going to remind you to say something to me soon!"); + notificationQueue.AddReminder(context.Activity.From.AadObjectId!, Notifications.SendProactive, 10_000); +}); + +// Send proactive message using stored conversation ID +public static class Notifications +{ + public static async Task SendProactive(string userId) { - // If you encounter permission-related errors when sending this message, see - // https://learn.microsoft.com/en-us/azure/bot-service/bot-builder-howto-proactive-message?view=azure-bot-service-4.0&tabs=csharp#avoiding-401-unauthorized-errors - // Sends an activity to the sender of the incoming activity. - await turnContext.SendActivityAsync("proactive hello"); + var conversationId = (string?)storage.Get(userId); + if (conversationId is null) return; + await app.Send(conversationId, "Hey! It's been a while. How are you?"); } } + ``` Example of a code snippet to demonstrate creating conversation reference. @@ -314,75 +280,57 @@ Example of a code snippet to demonstrate creating conversation reference. }; ``` -# [JavaScript](#tab/javascript) +# [TypeScript](#tab/typescript) -* [SDK reference](/javascript/api/botbuilder-core/turncontext?view=botbuilder-ts-latest&preserve-view=true#botbuilder-core-turncontext-getconversationreference) +* [SDK reference](https://microsoft.github.io/teams-sdk/typescript/essentials/sending-messages/proactive-messaging) * [Sample code reference](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/graph-proactive-installation/nodejs/bots/proactiveBot.js#L59) -```javascript - -async SendNotificationToAllUsersAsync(context) { - const TeamMembers = await TeamsInfo.getPagedMembers(context); - let Sent_msg_Cout = TeamMembers.members.length; - TeamMembers.members.map(async member => { - const ref = TurnContext.getConversationReference(context.activity); - ref.user = member; - await context.adapter.createConversation(ref, async (context) => { - const ref = TurnContext.getConversationReference(context.activity); - await context.adapter.continueConversation(ref, async (context) => { - await context.sendActivity("Proactive hello."); - }); - }); - }); - await context.sendActivity(MessageFactory.text("Message sent:" + Sent_msg_Cout)); -} - +```typescript + +// This would be some persistent storage +const myConversationIdStorage = new Map(); + +// Save the conversation ID on app install +app.on('install.add', async ({ activity, send }) => { + myConversationIdStorage.set(activity.from.aadObjectId!, activity.conversation.id); + await send('Hi! I am going to remind you to say something to me soon!'); + notificationQueue.addReminder(activity.from.aadObjectId!, sendProactiveNotification, 10_000); +}); + +// Send proactive message using stored conversation ID +const sendProactiveNotification = async (userId: string) => { + const conversationId = myConversationIdStorage.get(userId); + if (!conversationId) { + return; + } + const activity = new MessageActivity('Hey! It\'s been a while. How are you?'); + await app.send(conversationId, activity); +}; ``` # [Python](#tab/python) -* [SDK reference](/python/api/botbuilder-core/botbuilder.core.botframeworkadapter?view=botbuilder-py-latest&preserve-view=true#botbuilder-core-botframeworkadapter-create-conversation) +* [SDK reference](https://microsoft.github.io/teams-sdk/python/essentials/sending-messages/proactive-messaging) * [Sample code reference](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/bot-conversation/python/bots/teams_conversation_bot.py#L200) ```python -# Send message to all members. -async def _message_all_members(self, turn_context: TurnContext): - team_members = await self._get_paged_members(turn_context) - - for member in team_members: - # A conversation reference for the conversation that contains this activity. - conversation_reference = TurnContext.get_conversation_reference( - turn_context.activity - ) - - conversation_parameters = ConversationParameters( - is_group=False, - bot=turn_context.activity.recipient, - members=[member], - tenant_id=turn_context.activity.conversation.tenant_id, - ) - - async def get_ref(tc1): - conversation_reference_inner = TurnContext.get_conversation_reference( - tc1.activity - ) - return await tc1.adapter.continue_conversation( - conversation_reference_inner, send_message, self._app_id - ) - - async def send_message(tc2: TurnContext): - return await tc2.send_activity( - f"Hello {member.name}. I'm a Teams conversation bot." - ) - - await turn_context.adapter.create_conversation( - conversation_reference, get_ref, conversation_parameters - ) - - # Sends an activity to the sender of the incoming activity. - await turn_context.send_activity( - MessageFactory.text("All messages have been sent") - ) +# This would be some persistent storage +storage = dict[str, str]() + +# Save the conversation_id when the app is installed +@app.on_install_add +async def handle_install_add(ctx: ActivityContext[InstalledActivity]): + storage[ctx.activity.from_.aad_object_id] = ctx.activity.conversation.id + await ctx.send("Hi! I am going to remind you to say something to me soon!") + notification_queue.add_reminder(ctx.activity.from_.aad_object_id, send_proactive_notification, 60000) + +# Send proactive message using stored conversation ID +async def send_proactive_notification(user_id: str): + conversation_id = storage.get(user_id, "") + if not conversation_id: + return + activity = MessageActivityInput(text="Hey! It's been a while. How are you?") + await app.send(conversation_id, activity) ``` @@ -420,12 +368,12 @@ You must supply the user ID and the tenant ID. If the call succeeds, the API ret ## Code sample -The following table provides a simple code sample that incorporates basic conversation flow into a Teams application and how to create a new conversation thread in a channel in Teams: +The following table provides code samples that incorporate basic conversation flow and proactive messaging into a Teams application using the Teams SDK: | **Sample Name** | **Description** | **.NET** | **Node.js** | **Python** | **Manifest** |---------------|--------------|--------|-------------|--------|--------| -| Teams Conversation Basics | This sample app shows how to use different bot conversation events available in bot framework v4 for personal and teams scope.| [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-conversation/csharp) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-conversation/nodejs) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-conversation/python) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/bot-conversation/csharp/demo-manifest/bot-conversation.zip) -| Start new thread in a channel | This sample shows how to start a thread in a specific Team's channel using Bot Framework v4. | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-initiate-thread-in-channel/csharp) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-initiate-thread-in-channel/nodejs) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-initiate-thread-in-channel/python) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/bot-initiate-thread-in-channel/csharp/demo-manifest/bot-initiate-thread-in-channel.zip) | +| Teams Conversation Basics | This sample app shows how to use different bot conversation events available in Teams SDK v2 for personal and teams scope.| [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-quickstart/dotnet/bot-quickstart) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-quickstart/nodejs/bot-quickstart) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-quickstart/python/bot-quickstart) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/bot-conversation/csharp/demo-manifest/bot-conversation.zip) +| Start new thread in a channel | This sample shows how to start a thread in a specific Team's channel using Teams SDK v2. | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-initiate-thread-in-channel/csharp) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-initiate-thread-in-channel/nodejs) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-initiate-thread-in-channel/python) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/bot-initiate-thread-in-channel/csharp/demo-manifest/bot-initiate-thread-in-channel.zip) | | Proactive installation of app and sending proactive notifications | This sample shows how you can use proactive installation of app for users and send proactive notifications by calling Microsoft Graph APIs. | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/graph-proactive-installation/csharp) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/graph-proactive-installation/nodejs) | NA | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/graph-proactive-installation/csharp/demo-manifest/graph-proactive-installation.zip) | Proactive Messaging | This is a sample that shows how to save user's conversation reference information to send proactive reminder message using Bots. | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-conversation/csharp) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-proactive-messaging-teamsfx) | NA | | Teams Conversation Bot | This sample shows how to incorporate basic conversational flow into a Teams application. You can also use this sample to learn how to get `serviceURL` from the incoming request. | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/bot-conversation/csharp/Bots/TeamsConversationBot.cs) | NA | NA | NA | @@ -445,7 +393,6 @@ The following table provides a simple code sample that incorporates basic conver * [Channel and group chat conversations with a bot](~/bots/how-to/conversations/channel-and-group-conversations.md) * [Respond to the dialog submit action](~/messaging-extensions/how-to/action-commands/respond-to-task-module-submit.md) * [Send proactive notifications to users](/azure/bot-service/bot-builder-howto-proactive-message) -* [Build your first bot app using JavaScript](../../../sbs-gs-bot.yml) -* [Build notification bot with JavaScript to send a proactive message](../../../sbs-gs-notificationbot.yml) +* [Build your first bot app](../../../get-started/tutorials.md#build-your-first-bot-app) * [TurnContext](/javascript/api/botbuilder-core/turncontext?view=botbuilder-ts-latest&preserve-view=true) * [Implement custom storage for bot](/azure/bot-service/bot-builder-custom-storage)