I'm trying to create a serverless slack bot in AWS Lambda. I managed to get the app to send a message to a user in Slack with an interactive button, which sends a post back to the app when clicked. The problem is that the body of this message (the one from the button click) has the body of the JSON all garbled. Here is a reduced version of the garble:
'body': 'cGF5bG9hZD0lN0IlMjJ0eXBlJTIyJTNBJTIyYmxvY2tfYWN0aW9...
The rest of the JSON object looks normal.
On the other hand, the button object I'm sending to slack is this one:
{
"type": "button",
"text": {
"type": "plain_text",
"emoji": True,
"text": "Yes"
},
"style": "primary",
"value": "click_me_123"
},
What am I missing? Appreciate all the help I can get!
Solution: It was that the body of the response, it was encoded in Base64, so I had to decode that with:
base64.b64decode('encoded_string').decode('utf-8')
Related
I am running a Telegram bot using Webhooks that queries an AWS Lambda function. In a private conversation with just the bot, it functions as expected. In group chats however, the bot fails to respond. In particular, when receiving updates from a group, the Message object is missing the text field (i.e. there is no text associated with messages from group chats).
Here is what I have tried:
Setting privacy mode to disabled (such that the bot can access all messages in groups)
Giving the bot admin privileges
Removing and adding the bot after doing the above
Deleting and recreating a whole new bot after doing the above
Deleting and setting a webhook
Here is the lambda code (it simply echos back whatever it receives):
import requests
import json
def lambda_handler(event, context):
# get the request body from API gateway
body = json.loads(event['body'])
token = 'my secret token'
URL = "https://api.telegram.org/bot{}/".format(token)
chat_id = body['message']['chat']['id']
# This if statement is triggered for EVERY group chat message the bot receives
# which is the error I'm trying to debug
if 'text' not in body['message']:
return { 'statusCode': 500 }
# this only works for direct messages
message = body['message']['text']
send_url = URL + "sendMessage?text={}&chat_id={}".format(message, chat_id)
requests.get(send_url)
response = {
"statusCode": 200
};
return response
Here is what the Message object looks like when received from a group chat (notice that it doesn't have a text field:
{
"message_id":27,
"from":{
"id":id_number,
"is_bot":False,
"first_name":"Jafer",
"last_name":"",
"username":"username",
"language_code":"en"
},
"chat":{
"id":-id_number,
"title":"test",
"type":"group",
"all_members_are_administrators":True
},
"date":1603138229,
"group_chat_created":True
}
Here are some of the resources on stackoverflow that I've already looked at:
Allow bot to access Telegram Group messages
python telegram bot(Telepot) group chat
Since I'm trying to run the bot in a serverless environment, I cannot use a polling mechanism as suggested here: Telegram Bot - how to get a group chat id?
I would very much appreciate some help understanding why my bot struggles with group chats! Thank you!
Your dumped response has "group_chat_created":True and it is a service message which normally doesn't have any text. This messages is created when a group is created (the first message of the group).
Docs says,
Field
Type
Description
group_chat_created
True
Optional. Service message: the group has been created
Your bot only receives this type of message if you create the group by adding the bot as a member first (i.e. when the bot is a member of the group when it is created.) Now, by default bot will not read the text messages, so, you can promote the bot as an admin to read the text messages.
As an example, if I create a group with a bot,
{
"update_id": 989846...,
"message": {
"message_id": 15,
"from": {
"id": 1521565...,
"is_bot": false,
"first_name": "me",
"username": "abc...",
"language_code": "en"
},
"chat": {
"id": -560877...,
"title": "MeAndMyBot...",
"type": "group",
"all_members_are_administrators": true
},
"date": 1614624584,
"group_chat_created": true
}
}
And you can see that,
Now give the appropriate permissions (e.g.: Admin privileges) to access text messages,
Then we can get updates like,
{
"update_id": 989846...,
"message": {
"message_id": 16,
"from": {
"id": 1521565...,
"is_bot": false,
"first_name": "me",
"username": "abc...",
"language_code": "en"
},
"chat": {
"id": -560877...,
"title": "MeAndMyBot...",
"type": "group",
"all_members_are_administrators": true
},
"date": 1614625333,
"text": "Hello there"
}
}
I have a slack /Slash command that generates the app file link. I want to send that link to a user as a button or link but it has to be prefixed with:
itms-services://?action=download-manifest&url={download_link}
Currently slack is not recognizing this as a valid link but it's mandatory by iOS because of permission issues.
Basically I want to mimic the download button on web on slack so user is not required to visit the website.
On Web this link itms-services://?action=download-manifest&url={download_link} works fine and user is asked to authorize the download.
test_attachment = [
{
"color": "#CC0000",
"actions": [
{
"type": "button",
"text": ":red_circle: Download Link:",
"url": "itms-services://?action=download-manifest&url={download_link}"
}
]
}
]
slack_client.api_call("chat.postMessage", channel=channel_id, text=text, attachments=json.dumps(test_attachment))
Another example:
<a href="itms-services://?action=download-manifest&url=http://loqi.me/app/Geoloqi.plist">
Download Geoloqi
</a>
This will work on Web and how to make it work on slack messages.
An itms-services link is not recognize as a valid URL by slack parser because its address (the thing we usually found just after ://) is empty.
A quick fix is to fill it with a dummy address for example 0.0.0.0.
Here is a block kit that works :
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Try to install me:"
},
"accessory": {
"type": "button",
"text": {
"type": "plain_text",
"text": "Install",
"emoji": true
},
"url": "itms-services://0.0.0.0?action=download-manifest&url={download_link}"
}
}
]
}
and it looks like :
I had a similar problem in one of my apps where I wanted to provide a download button for a dynamically generated image file.
As far as I know there is no switch or option in Slack that allows you to do this, so your only option is to use a helper web app.
The approach would be:
User clicks a link button on Slack that opens your helper web app in the browser
Helper web app performs the actual download (no further user interaction required)
My guess is the user will need to authenticate in the browser anyways, so this should not create any inconvenience for the user. But of course you would need to provide the additional web helper app.
How to receive attachment actions using python SlackClient. How to get user's reply?
Code :
intro_msg = json.dumps([{"text": "Choose an action", "fallback": "You are unable to choose an option",
"callback_id": "lunch_intro", "color": "#3AA3E3", "attachment_type": "default",
"actions": [
{"name": "enroll", "text": "Enroll", "type": "button", "value": "enroll"},
{"name": "leave", "text": "Leave", "type": "button", "value": "leave"}]}])
result = self.sc.api_call("chat.postMessage", channel=channel, text="What would you like to do?",attachments=intro_msg,as_user=True)
https://api.slack.com/interactive-messages
Define a Slack app
Your app (Python script) needs to run on a public webserver that can be reached by Slack (can also by your local machine if you use tools like ngrok)
Put the URL for your app as "request URL" in your Slack app configuration under "Interactive messages"
Once the user clicks one of the button, Slack will call your app with a POST request which will contain the action that the use chose
Check out the documentation you linked for details like how the data structure of the Slack request looks like.
I am trying to build a Slack bot in Python. I want my bot to send a message with interactive buttons on it, and then based on which the user clicks on, run a function in Python. I do not seem to find how to do this.
My code now look like this:
message = "message"
attachments = [{"text": "message",
"attachment_type": "default",
"actions": [
{
"name": "list",
"text": "message",
"type": "select",
"options": [
{
"name": "1",
"text": "1",
"type": "button",
"value": "1"
},
{
"name": "1",
"text": "1",
"type": "button",
"value": "2"
}
]}]}]
sc.api_call("chat.postMessage",
channel=channel,
text=message,
attachments=attachments)
So that gives me a message with two buttons. I, however, want to run a function based on the answer the user gives.
So say that if they click 1, function1() runs and when they click 2, function2() runs.
The Slack api documentation is quite confusing about how to do this, and the "listener" they provide rtm_read() does not pick on the user clicking on one of the buttons.
So if anyone could help me with this, it would be much appreciated.
When you click a button in a slack conversation it's basically applying a callback. The callback is sent somewhere that you define in the App's setting, then THAT service decides what to do next with the information that's given.
First you need to create a new Slack App.
After it's created click on the App to go to its Basic Information page.
From there, on the left side under Features find "Interactive Components".
Register the two URLs that will receive the POST data from clicking on a button.
Interpret the data and proceed :)
From the Slack documentation you can find their walkthrough here.
You're going to need a running web server, something simple in Flask will work just fine.
from flask import Flask, request
app = Flask('SlackReceiver')
#app.route('/slack/message', methods=['POST'])
def incoming_slack_message():
req = request.get_json()
# .. do something with the req ..
return 'action successful'
#app.route('/slack/options', methods=['POST', 'OPTIONS'])
def incoming_slack_options():
# .. idk ..
return 'ok'
if __name__ == '__main__':
app.run('0.0.0.0', 8088, debug=False)
...
Lastly, according to the docs you need to host this application on a web server with an HTTPS valid certificate configured. Setting up a server is beyond the scope of this question, the easiest way to get free (valid) HTTPS certs is with Let's Encrypt and certbot.
I'm trying to add an attachment to a slack message via their API. I'm using the python wrapper they recommend. I can send and receive basic messages but when I try to add an attachment in the form of 2 buttons it fails. I have made a slack app and linked the bot as they state in their API. I've carefully reviewed the API and cannot figure out what is going on.
def process_message(message, channel):
intro_msg = json.loads('{
"text": "What would you like to do?",
"attachments": [
{
"text": "Choose an action",
"fallback": "You are unable to choose an option",
"callback_id": "lunch_intro",
"color": "#3AA3E3",
"attachment_type": "default",
"actions": [
{
"name": "enroll",
"text": "Enroll",
"type": "button",
"value": "enroll"
},
{
"name": "leave",
"text": "Leave",
"type": "button",
"value": "leave"
}
]
}
]
}')
r = sc.api_call("chat.postMessage", channel=channel, attachments=intro_msg)
The response is only {u'ok': False, u'error': u'no_text'}
I figured it out.
The python wrapper separates out the payload.
intro_msg = json.dumps([{"text":"Choose an action","fallback":"You are unable to choose an option","callback_id":"lunch_intro","color":"#3AA3E3","attachment_type":"default","actions":[{"name":"enroll","text":"Enroll","type":"button","value":"enroll"},{"name":"leave","text":"Leave","type":"button","value":"leave"}]}])
sc.api_call("chat.postMessage", channel=channel, text="What would you like to do?", attachments=intro_msg, as_user=True)
My payload was all in attachments since that is how they format it in their API docs. The attachments needs to just be the array after the attachments key.
I guess the basic simple example works.
Example:
from slackclient import SlackClient
slack_token = os.environ["SLACK_API_TOKEN"]
sc = SlackClient(slack_token)
sc.api_call(
"chat.postMessage",
channel="#python",
text="Hello from Python! :tada:"
)
According to https://api.slack.com/methods/chat.postMessage and https://api.slack.com/docs/message-buttons#readying_your_application_for_message_buttons the attachments has to be an array. How about sending it as array:
json.loads('[{"text":"What would you like to do?","attachments":[{"text":"Choose an action","fallback":"You are unable to choose an option","callback_id":"lunch_intro","color":"#3AA3E3","attachment_type":"default","actions":[{"name":"enroll","text":"Enroll","type":"button","value":"enroll"},{"name":"leave","text":"Leave","type":"button","value":"leave"}]}]}]')
As there is no further magic involved but the requests module https://github.com/slackapi/python-slackclient/blob/ddf9d8f5803040f0397d68439d3217d1e1340d0a/slackclient/_slackrequest.py I'd give it a try with the sending as array.