I have a Slack App that is connected to an AWS Lex bot. Thus, the Request URL for the Slack app is the Postback URL provided by Lex. However, I want to add a Home tab for the app, but am unable to publish a surface for all users. The Slack API only seems to allow you to publish a surface for a specific user (user_id is a required parameter for the POST call). How can I publish this view to all users who use the app?
publish_url = "https://slack.com/api/views.publish"
header = {'content-type':'application/json'}
parameters = {
"token": slack_token,
"user_id": member_id, # <--- This is my problem
"view": json.dumps({
"type": "home",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Welcome to *Survey Analyzer*! ..."
}
},
...
]
})
}
r = requests.post(publish_url, params=parameters, headers=header)
When I use my own member ID, it pushes the view properly to me and me only. I've also tried using the app's ID, but that doesn't seem to push the view to any users.
You can only publish a home tab for each user separately. You can use users.list to retrieve all users of a workspace and then call views.publish for each user in that list.
Alternatively you can subscribe to the app_home_opened event through the Events API. Once a user opens the home tab it will fire an event including the user's id. That way you'll only publish a view for a user who is actually visiting the home tab of your app.
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.
I am going to use Firebase Auth and Database modules to create my web app. However, not all things that I want my app to do is possible to achieve on only front end. So I want to also use backend with Python's Bottle framework to handle requests and Pyrebase to get access to Firebase Database.
Let's say that after logging in I need to go to mainpage and see personalized content, for example my notes. They are structured this way in DB:
{
"notes": [{
"id": "1",
"title": "X",
"author": "user1"
},
{
"id": "2",
"title": "Y",
"author": "user2"
} and so on... ]
}
So how it's possible to implement showing only my articles on main page?
I understand that I need to filter my notes based on author value, but how to let Bottle understand who is currently logged in?
I've read there, that I should somehow send unique token to backend server to authenticate current user, but how to do that? Inserting Token in every link as GET parameter seems to be silly, but I see no other way to implement that.
Start by organizing your database so that each note becomes a child object:
{
"notes": {
"id1": {
"id": "id1",
"title": "X",
"author": "user1",
},
"id2": {
}
}
}
Then this particular interaction can be implemented entirely in the client-side. Just execute a query to filter the notes you want. For example in a JS client:
var uid = firebase.auth().currentUser.uid;
var query = ref.orderByChild('author').equalTo(uid);
// Listen for query value events
If you want to run this on a backend server, and you want to ensure that only logged in users are allowed to execute it, then you must pass the ID token from the client app to the server on each request. Here's how to implement the server-side logic using the Python Admin SDK:
import firebase_admin
from firebase_admin import auth
from firebase_admin import db
token = '....' # Extract from the client request
try:
decoded = auth.verify_id_token(token)
uid = decoded.uid
ref = db.reference('path/to/notes')
notes = ref.order_by_child('author').equal_to(uid).get()
# Process notes response
except ValueError as ex:
print(ex)
# Send error to client
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 currently trying to implement a user notification system using Websockets via Crossbar/Autobahn. I have done multiple tests and gone through the documentation, however, I'm not sure if there's a solution to having the following workflow work:
User signs in with web app -- this is done through JWT
Frontend establishes a websocket connection to a running crossbar instance.
Frontend attempts to subscribe to a URI specifically for the user's notifications: i.e. com.example.notifications.user.23 or com.example.user.23.notifications'. Where23` is the user id.
User's JWT is checked to see if user is allowed to access subscription.
When activity is generated and causes a notification, the backend publishes the user-specific URIs.
For step 3, I can't tell if the current support auth methods have what I need. Ideally, I would like an auth method which I can customize (in order to implement a JWT authenticator within Crossbar) that I can apply to a URI pattern, but NOT give access to the entire pattern to the subscribing user. This is partially solved by the dynamic auth methods, but is missing the latter half:
For example (my ideal workflow):
User attempts to subscribe to a URI com.example.user.23.notifications.
URI matches com.example.user..notifications (wildcard pattern in http://crossbar.io/docs/Pattern-Based-Subscriptions/)
Auth token is validated and user is given access to only com.example.user.23.notifications.
Is the above achievable in a simple way? From what I can tell, it may only be possible if I somehow generate a .crossbar/config.json which contains URI permutations of all user ids...and automatically generate a new config for each new user -- which is completely not a reasonable solution.
Any help is appreciated!
Use authorizer.
See http://crossbar.io/docs/Authorization/#dynamic-authorization
Register a dynamic authorizer for the user role that session was assigned when joining/authenticating:
{
"name": "authorizer",
"permissions": [
{
"uri": "com.example.authorize",
"register": true
}
]
},
{
"name": "authenticator",
"permissions": [
{
"uri": "com.example.authenticate",
"register": true
}
]
},
{
"name": "user",
"authorizer": "com.example.authorize"
},
...
"components": [
{
"type": "class",
"classname": "example.AuthenticatorSession",
"realm": "realm1",
"role": "authenticator",
"extra": {
"backend_base_url": "http://localhost:8080/ws"
}
},
{
"type": "class",
"classname": "example.AuthorizerSession",
"realm": "realm1",
"role": "authorizer"
}
]
Write a class
class AuthorizerSession(ApplicationSession):
#inlineCallbacks
def onJoin(self, details):
print("In AuthorizerSession.onJoin({})".format(details))
try:
yield self.register(self.authorize, 'com.example.authorize')
print("AuthorizerSession: authorizer registered")
except Exception as e:
print("AuthorizerSession: failed to register authorizer procedure ({})".format(e))
def authorize(self, session, uri, action):
print("AuthorizerSession.authorize({}, {}, {})".format(session, uri, action))
if session['authrole'] == u'backend': # backnend can do whatever
return True
[Authorization logic here]
return authorized