How to proactively send a message to a teams channel - python-3.x

I can't seem to figure out how to proactively message a MS teams channel using a Python Bot (botframework).
An user installs my third-party MS teams bot, adding it to one of their Teams channels.
My Bot needs to send ad-hoc messages as part of an event from an unrelated back-end system.
The botframework does not let you message channels at will, it needs a conversation reference. You can get a conversation reference in various ways, such as someone messaging the bot, or fetching the list of channels and constructing a conversationId from that.
Reading the documentation
The documentation will have you believe that it is in fact possible to send message at will, using the following steps:
Get the user ID or team/channel ID (if needed).
Create the conversation or conversation thread (if needed).
Get the conversation ID.
Send the message.
For step 1, how/when do I get the channel ID if there are no events that my Bot has been added to a channel?
For step 2, how do I create a conversation if I don't know what team channels there are?
Conclusion
Does someone know how to send a message to a MS Teams channel using a Python app/bot? It should not require user interaction. The app/bot gets added to a Teams channel, and it should immediately post a message inside this channel.

Turns out the issue was that my on_teams_members_added() was not getting called because I kept deleting the app within Teams instead of uninstalling.
Make sure to:
Click on the ... overflow menu next to the team name
Pick Manage Team
Select the "Apps" tab
Click the trash icon to remove the app from that team
Then try to install the app again
With this code you can send a channel message when the Bot enters the channel:
async def on_teams_members_added( # pylint: disable=unused-argument
self,
teams_members_added: [TeamsChannelAccount],
team_info: TeamInfo,
turn_context: TurnContext,
):
for member in teams_members_added:
if member.id == turn_context.activity.recipient.id and team_info is not None:
# bot entered a Teams channel
await turn_context.send_activity("Hello channel! I have just been added.")
Your handler needs to inherit from TeamsActivityHandler.

I'm working on a sample for the pnp Teams samples repo on GitHub that I'm hoping to submit in the next few days. I haven't started on the documentation yet, but the code is fully functional, with both a C# and a Node.js version of the backend, which sends a -very- simple proactive message example (showing the most basic things you need) - hopefully it can be of use even though it's not in Python - see https://github.com/HiltonGiesenow/teams-dev-samples/tree/add-proactive-messaging-sample/samples/bot-proactive-messaging/src

Related

Teams Bot how to listen on incoming calls?

I am getting lost in all the documentation available on my first time attempting to set up a Microsoft Teams bot. I have it set up and able to write command activated messages in personal, groups and teams. I need to transition this to be able to receive a payload of data (the data that I will use to populate my activity card), but I can't figure out how am I supposed to let the bot know which Group or Team the payload is ment for.
The payload will be sent from my Azure hosted API, to the endpoint of my Bot. It should then, with some data in the payload I suppose, know which group or team to sent the processed activity card to?
If you could provide me with the documentation I need to read up on, or further explanation on how to proceed, I would greatly appreciate it.
You can listen to teams bot for incoming calls,
Bots can interact with Teams calls and meetings using real-time voice, video, and screen sharing. With Microsoft Graph APIs for calls and online meetings, Teams apps can now interact with users using voice and video to enhance the experience. These APIs allow you to add the following new features:
Interactive voice response (IVR).
Call control.
Access to real-time audio and video streams, including desktop and app sharing.
please check this doc for more info and sample
I apologize for the delay in responding to you.
I read the documentation you sent, but I am not sure how it relates as I do not need real-time voice, audio or screen sharing capabilities.
I can understand that the title writing "incoming calls" could be cause of misunderstanding, however, the issue is as described that I need to forward a payload from my API endpoint that needs to be received in the Teams Bot that listens to that specific payload. I don't know how to approach a solution for that part, after that part the Teams Bot should then sendActivity with an activity card attachment (this I have prepared already using dummy data and the card designer for layout), containing that payload data, to the group/team that the Teams Bot is active on.
I resolved this using the answers from this thread:
Programmatically sending a message to a bot in Microsoft Teams
Specifically thanks to the message from #Hilton Giesenow:
https://stackoverflow.com/a/59439602/10429879

ms teams custom app starting a chat session with a bot

I'm building a teams app that contains some tabs and a bot.
Is it possible to, when an action is performed on a tab (i.e. clicking a button), initialize automatically a chat with the bot, or send a specific message?
Tks
Based on the comments above, what you're looking to do is implement "pro-active" messaging, where the bot itself initiates the conversation. It's definitely possible, and you can read more to get started here and here. The most important thing to know is that your user has to have installed the bot already, to get a "conversation context", but if they've installed the app, which includes the bot, to get the tab, then you're fine. You need to get some variables when they do install the app, which you hook into the conversationUpdate event to get access to. Give it a go and let me know if you have specific questions, here on SOverflow.

Why doesn't the Azure Bot Service Slack connector forward Events and Interactive Messages?

Update: June 30, 2020
After more testing, I have details that might help someone recognize my problem.
The issue seems to be that Slack is sending data to Azure Bot Services, but that data isn't being forwarded to my code. Ive been able to use the Bot Emulator without any problems and the Azure Web Chat works fine.
I know that the Slack configuration for the OAuth Redirect URL is correct (I was able to add my bot to Slack) and the Request URL for Events is correct (they sent the 'challenge' and it's verified). I've subscribed to the exact Scopes and Events that are in the Microsoft documentation and I've verified that the Interactivity and Events options are enabled.
When a user types text in my bot's Slack channel, my app receives "message" activity and my code can send a response, so it looks like Microsoft can communicate end-to-end for normal messages. I do not receive any data when users first join my bot (like a ConversationUpdate) or if they click a button in a dialog. I can see Slack sending data when a button is pressed, it just never arrives.
As a test, I copied the Messaging Endpoint from my Azure bot settings and pasted it into Slack's Interactivity "Request URL" and when I click a button in Slack I can see the data that Slack is sending (sadly in a format that my code can't handle).
Original Post
I have a Bot Framework app (v4) that I've written in nodejs. It works well and I have an ActivityHandler that responds to people being added to a conversation and when they send messages. I was able to get pro-active messaging functioning and everything was great until I tried to get interactivity working.
I started off using some sample button code from Microsoft's documentation:
let reply = MessageFactory.suggestedActions(['Red', 'Yellow', 'Blue'], 'What is the best color?');
await turnContext.sendActivity(reply);
This works fine in the emulator, but in Slack it renders as a bulleted list. It looks like that's the way that "suggested actions" are handled in Slack.
I changed my code to use a "hero card":
let card = CardFactory.heroCard(
'What is the best color?',
undefined,
CardFactory.actions([
{
type: 'imBack',
title: 'Color Red',
value: 'Red Value'
}
])
);
let reply = MessageFactory.attachment(card);
await turnContext.sendActivity(reply);
This works okay in the emulator, except my app thinks the user typed "Red Value" and the button stays on-screen and is still clickable. I might be able to work around that, but the button doesn't work at all in Slack. It is rendered fine, but I don't get a notification in my app.
Clicking the button shows an HTTP request to:
https://{MY_SLACK}.slack.com/api/chat.attachmentAction?_x_id=f8d003c3-1592436018.632&_x_csid=NcWi3y50lFU&slack_route={OTHER_SLACK_STUFF}
And I can see that the request POSTs all sorts of data including:
payload: {"actions":[{"id":"1","name":"imBack","text":"Color Red","type":"button","value":"Red Value","style":"default"}],"attachment_id":"2","callback_id":"{MAGIC_NUMBER}:{TEAM_ID}","channel_id":"{CHANNEL_ID}","message_ts":"1592435983.056000","prompt_app_install":false,"team_id":"{TEAM_ID}"}
I'm not sure how to see anything useful in the Azure Portal - the analytics option for my bot doesn't seem to work and the activities option only says "Write a Bot Service". I don't see any sign of the message going from Slack to Azure.
I'm developing locally and configured ngrok so that my messaging endpoint in Azure could be set to https://69fe1382ce17.ngrok.io/api/messages On the Slack side of things, I've configured the Interactivity Request URL to be https://slack.botframework.com/api/Actions The Event Subscription Request URL is https://slack.botframework.com/api/Events/{MY_BOT_NAME}
What I would like is a set of buttons with different options and when the user clicks one, my bot gets some sort of "value" instead of message text. I'd also like for the button to go away so the user can't send repeated commands. It would be nice if the hero card collapsed with just the prompt being displayed.
Are there any interactive options that work for Slack and other channels?
Thanks!
Lee
I know linking to another site with no additional detail is frowned upon, but I don't have enough expertise to answer your question. I suspect the link here might move you in the right direction:
Choice Prompts are not translated over to Slack format #3974
Good luck!
Your question is multifaceted so I'll try to break it down into smaller pieces.
What's the deal with suggested actions in Slack?
Suggested actions are not supported in Slack, but the Bot Builder SDK thinks they are. This is a longstanding bug. I've just reported it again on the docs page you linked: https://github.com/MicrosoftDocs/bot-docs/issues/1742
This means you would encounter problems if you were trying to have the choice factory automatically generate the right kind of choices for your channel. You're not doing that, so you should be fine. Hero cards are supposed to work in Slack.
Why aren't hero cards working in Slack?
First I need to mention that hero cards only work with the Slack connector and not the Slack adapter. You seem to be using the connector so you should be fine.
I suspect your problem is related to how you've configured your bot's settings on the Slack side. There is a step in the Bot Framework doc that seems to be important if you want to get buttons to work. If you've followed the doc exactly and you still can't get buttons to work, it may be worthwhile to dig into the Slack API documentation.
How do I only allow a button to be clicked once?
You can update or delete the activity. There's no easy way to do this, but if you voice your support for my cards library then it can be done for you automatically.
The Slack connector actually puts a lot of relevant information in the incoming activity's channel data, and you can use that to figure out what activity the incoming activity came from. That would take some experimentation on your part.
There's another approach that works on more channels than just Slack. It's real complicated, but if you wanna tackle this then here are the basic steps:
You need to put an ID in the action data to help your bot identify the action.
You need to save the activity ID that gets returned when you send the action to Slack.
You need to associate the returned activity ID with the ID you put in the action data.
You need to retrieve the activity ID using the action data ID when the user clicks the button.
You need to use that activity ID to update or delete the activity.
Unfortunately there's no centralized guide to help you do this, but there are many examples explaining it scattered across Stack Overflow. Here is a good one: https://stackoverflow.com/a/55174866/2122672

Slack slash command is not working in public channels unless manually added to them

I'm not sure how much detail to provide, but I'll try to put everything that I feel is relevant.
I have a slash command that I do some logic with through AWS Lambda and API Gateway. I'm using the python slackclient module and through that I'm sending messages back into slack with the WebClient. To clarify, I'm using the new bot token system Slack has come up with, not the classic bot one.
From my understanding, using a webhook to send messages in all channels in a workspace is very painful, as a webhook is provided per channel. Thus I'm using the WebClient to interface with Slack and send messages in public channels. The problem I'm facing is that unless I add my app to a channel, the command doesn't work. The Cloudwatch logs show me that the events are coming in just fine, as I see the event fine. I also see the following log by using sys.exc_info():
(<class 'slack.errors.SlackApiError'>, SlackApiError("The request to the Slack API failed.\nThe server responded with: {'ok': False, 'error': 'not_in_channel'}"), <traceback object at 0x7ff4a0dc32c0>)
Based on that, it looks to me like sending a message back into channels I've not added this app into doesn't seem to work, but I'm not sure what OAuth permissions/scopes would be needed for this. I've enabled the following scopes for the bot token:
channels:read
chat:write
commands
As per my understanding, I don't need to add any user scopes, since I want my app/bot itself to respond, and not respond on behalf of a user.
In short, my desired behaviour is to add this app to my workspace and immediately be have it reply to a slash command regardless of which public channel the slash command has been invoked from.
The current behaviour is that the app is able to get event data from all public channels when the slash command is invoked from any of them, yet it's unable to send messages in channels the command is invoked in unless it is in the channel.
Any help given would be much appreciated!
For people migrating apps from the deprecated :bot scope, the new scope is now ready.
Add chat:write.public to your Bot Token Scopes and the error not_in_channel will not be returned anymore for public channels.
Good news: with two special scopes, you can gain those abilities by asking for them explicitly. Request the chat:write.public scope and chat:write.customize scope, respectively, to gain the ability post in all public channels and adjust your app's message authorship.
https://api.slack.com/authentication/quickstart#public
I messaged Slack help, and it turns out this functionality isn't available with the new granular bot permissions yet. As per this Slack documentation:
Currently, your app must be a member of any channel it wishes to post messages to. To join a channel, request the channels:join scope and call the conversations.join method. However, apps will soon be able to post in any public channel, without gaining additional access to the channel, by requesting a new scope.
It looks like this won't be an issue when the functionality is added by Slack.

Send private message to a slackbot without other users of the workspace seeing it

How can i send a private message to a bot other without users seeing it on the channel?
Im using node js and the module slackbots. And each time i communicate with the bot it goes something like this: "#robot hello" but everybody on the chat sees it.
Your example is not a direct message, but a so called "mention" in a channel, which is always visible by everyone else in the same channel.
But you can also send a real direct message to a bot user. It works the same like sending a direct message to any user. e.g. click on the plus next to "Direct Messages", select the bot user from the list and click on go. Now you got a direct messaging channel with that user which is 100% private.
Your bot obviously needs to be ready to receive messages via DM in order to respond correctly. e.g if your are using Slack's Events API you need to subscribe to message.im to receive posts in the direct message channel to your bot.
Check out this help desk page on how to send direct messages.
Another approach to communicate "secretly" with your bot user in a channel is slash commands, e.g. by posting /mybot hi in a channel. Both the slash command and respond from your app will not be visible to anyone else in the channel (that is default - but can be changed).

Resources