Based on Slack documentation, each new block element is a new message.
https://api.slack.com/messaging/composing/layouts
In such a case, the below example would send one message.
{
"blocks": [
{
"type": "section",
"text": {
"type": "plain_text",
"text": "This is a plain text section block.",
"emoji": true
}
}
]
}
In this case, two messages:
{
"blocks": [
{
"type": "section",
"text": {
"type": "plain_text",
"text": "This is a plain text section block.",
"emoji": true
}
},
{
"type": "section",
"text": {
"type": "plain_text",
"text": "This is a plain text section block.",
"emoji": true
}
}
]
}
My goal - is to generate such a JSON/Dictionary structure in Python, from two lists and then send it all as separate messages, as shown in the second case, but instead of two, as many elements as there are.
first = ['Tomato', 'Potato', 'Fish', 'Chicken']
second = ['tasty', 'good', 'nice', 'bad']
dictionary = {first[i]: second[i] for i in range(len(first))}
slackmessagexample = {"blocks": [
{
"type": "section",
"text": food + " " + taste
} for food, taste in dictionary.items()
]
}
jsonoutput = json.dumps(slackmessagexample, indent=2)
print(jsonoutput)
Output:
{
"blocks": [
{
"type": "section",
"text": "Tomato tasty"
},
{
"type": "section",
"text": "Potato good"
},
{
"type": "section",
"text": "Fish nice"
},
{
"type": "section",
"text": "Chicken bad"
}
]
}
This may seem silly, but I'm not able to figure out, how would one add for loop within the nested dictionary/JSON format in such case.
My final desired output should look like this:
{
"blocks": [
{
"type": "section",
"text": {
"type": "plain_text",
"text": "Tomato tasty",
"emoji": true
}
},
{
"type": "section",
"text": {
"type": "plain_text",
"text": "Potato good",
"emoji": true
}
},
{
"type": "section",
"text": {
"type": "plain_text",
"text": "Fish nice",
"emoji": true
}
},
{
"type": "section",
"text": {
"type": "plain_text",
"text": "Chicken bad",
"emoji": true
}
}
]
}
You were almost there! You can just add the nested dictionary in the comprehension. Also, I recommend using Python's zip function for your case when creating the dictionary. It creates an iterable of tuples which can be casted into dict directly.
As for your JSON structure, take a look at the following:
import json
first = ["Tomato", "Potato", "Fish", "Chicken"]
second = ["tasty", "good", "nice", "bad"]
dictionary = dict(zip(first, second))
slackmessagexample = {
"blocks": [
{
"type": "section",
"text": {"type": "plain_text", "text": food + " " + taste, "emoji": True},
}
for food, taste in dictionary.items()
]
}
jsonoutput = json.dumps(slackmessagexample, indent=2)
print(jsonoutput)
Output:
{
"blocks": [
{
"type": "section",
"text": {
"type": "plain_text",
"text": "Tomato tasty",
"emoji": true
}
},
{
"type": "section",
"text": {
"type": "plain_text",
"text": "Potato good",
"emoji": true
}
},
{
"type": "section",
"text": {
"type": "plain_text",
"text": "Fish nice",
"emoji": true
}
},
{
"type": "section",
"text": {
"type": "plain_text",
"text": "Chicken bad",
"emoji": true
}
}
]
}
Related
I've been working on a Slack bot to help handle emergency engagement for my team. It works really well, but all the configuration is in JSON files and it's time to make it "more interactive."
The other day we ended up with an email storm that triggered the bot over and over and over again, so I thought that a great and relatively simple place to start with the "more interactive" would be to add a "Snooze" button to our bot (as opposed to killing the running process).
I successfully display the modal, and the UI functions correctly, but I do not receive a payload on interaction. I expect my Flask App to receive a POST request containing payload data for an interaction, but I don't see any arrive not at the "/" endpoint that handles the majority of my interactions with Slack or the /911_snooze endpoint which is the one directly connected to the Slash Command.
I get a little warning triangle with the following error:
The JSON below is mostly created by Block Kit Builder, with some hand-cut, and the drop-down is created programmatically, based on an external list. Block Kit Builder reports no errors.
{
"title": {
"type": "plain_text",
"text": "911 Snooze Alerts",
"emoji": true
},
"submit": {
"type": "plain_text",
"text": "Submit",
"emoji": true
},
"type": "modal",
"callback_id": "snooze_911_alerts",
"close": {
"type": "plain_text",
"text": "Cancel",
"emoji": true
},
"blocks": [
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Application to Snooze"
},
"accessory": {
"type": "static_select",
"placeholder": {
"type": "plain_text",
"text": "Application",
"emoji": true
},
"options": [
{
"text": {
"type": "plain_text",
"text": "APP1",
"emoji": true
},
"value": "BOE"
},
{
"text": {
"type": "plain_text",
"text": "APP2",
"emoji": true
},
"value": "IBOE"
},
{
"text": {
"type": "plain_text",
"text": "APP3",
"emoji": true
},
"value": "GBOE"
},
{
"text": {
"type": "plain_text",
"text": "APP4",
"emoji": true
},
"value": "Swift"
}
],
"action_id": "application_select_action"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "How long do you want to snooze for:"
},
"accessory": {
"type": "radio_buttons",
"options": [
{
"text": {
"type": "plain_text",
"text": "5 minutes",
"emoji": true
},
"value": "minutes-5"
},
{
"text": {
"type": "plain_text",
"text": "30 minutes",
"emoji": true
},
"value": "minutes-30"
},
{
"text": {
"type": "plain_text",
"text": "60 minutes",
"emoji": true
},
"value": "minutes-60"
},
{
"text": {
"type": "plain_text",
"text": "90 minutes",
"emoji": true
},
"value": "minutes-90"
}
],
"action_id": "radio_buttons-action"
}
}
]
}
The element block needs to be inside an action block, or input block with dispatch_action: true.
See https://api.slack.com/reference/block-kit/blocks#actions and https://api.slack.com/reference/block-kit/blocks#input
So my goal is to include an image, with buttons, in a message. However, the file is not public and files_upload method takes away the functionality of the buttons, issue similar to this https://github.com/slackapi/bolt-python/issues/411.
While working on the solution to 1. upload the file, 2. unfurl the permalink, I managed to make it work but the output is not what I would like:
I would like the unfurled link image to appear under the blocks while keeping the blocks in the attachment section. Is this achievable? Also, the only time I can unfurl is to include the link within the test section, can this be changed?
Sourcecode:
upload_resp = slack_client.files_upload(
initial_comment=uploadfiles["initial_comment"],
file=uploadfiles["file"],
filename=uploadfiles["filename"],
filetype=uploadfiles["filetype"],
title=uploadfiles["title"]
)
attachments = [
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Hi !*" ,
}
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Title*"
}
}
,{
"type": "actions",
"block_id": "actionblock",
"elements": [
{
"type": "button",
"action_id" : "rerun_action",
"text": {
"type": "plain_text",
"text": "Rerun",
"emoji": True
},
"style": "primary",
"value": "rerun"
},
{
"type": "button",
"action_id" : "launch_wizard",
"text": {
"type": "plain_text",
"text": "Launch Wizard",
"emoji": True
},
"style": "primary",
"value": "launch"
}
]
}
]
}
]
resp = slack_client.chat_postMessage(
channel=message["channel_id"],
text=upload_resp['file']['permalink'],
attachments = attachments,
blocks=[],
unfurl_links=True,
unfurl_media=True
)
I am creating a slack bot in python using slack bolt. I have created a form like structure for the user to provide input values but I need to specify some inputs as mandatory. I tried using the "optional":true field but did not get any result
I need to give the datepicker input and multi select input as mandatory fields for the user where input is necessary.
Below is the block I have created:
blocks = [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Title"
}
},
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Date Duration"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "_Required Field_"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Start Date"
}
},
{
"type": "input",
"element": {
"type": "datepicker",
"placeholder": {
"type": "plain_text",
"text": "Select Start date"
},
"action_id": "start_date_action"
},
"label": {
"type": "plain_text",
"text": " "
},
"hint": {
"type": "plain_text",
"text": "Please ensure start date should be greater than current date "
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "End Date"
}
},
{
"type": "input",
"element": {
"type": "datepicker",
"placeholder": {
"type": "plain_text",
"text": "Select End date"
},
"action_id": "end_date_action"
},
"label": {
"type": "plain_text",
"text": " "
},
"hint": {
"type": "plain_text",
"text": "Please ensure end date should be greater than start date "
}
},
{
"type": "divider"
},
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Field1"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "_Required Field_"
}
},
{
"type": "input",
"hint": {
"type": "plain_text",
"text": "Multiple inputs are accepted"
},
"element": {
"type": "multi_static_select",
"placeholder": {
"type": "plain_text",
"text": "Select field"
},
"options": [
{
"text": {
"type": "plain_text",
"text": "11111"
},
"value": "value-0"
},
{
"text": {
"type": "plain_text",
"text": "22222"
},
"value": "value-1"
}
],
"action_id": "1_multi_select-action"
},
"label": {
"type": "plain_text",
"text": " "
}
},
{
"type": "divider"
},
{
"type": "divider"
},
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Field3"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "_Optional Field_"
}
},
{
"type": "input",
"hint": {
"type": "plain_text",
"text": "Multiple inputs are accepted"
},
"element": {
"type": "multi_static_select",
"placeholder": {
"type": "plain_text",
"text": "Select Categories"
},
"options": [
{
"text": {
"type": "plain_text",
"text": "adventure"
},
"value": "value-0"
},
{
"text": {
"type": "plain_text",
"text": "biking"
},
"value": "value-2"
}
],
"action_id": "categories_multi_select-action"
},
"label": {
"type": "plain_text",
"text": " "
}
},
{
"type": "divider"
},
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Number"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "_Optional Field_"
}
},
{
"type": "input",
"hint": {
"type": "plain_text",
"text": "Numeric values only"
},
"element": {
"type": "plain_text_input",
"action_id": "plain_text_input-action",
"placeholder": {
"type": "plain_text",
"text": "Enter Numeric Value"
}
},
"label": {
"type": "plain_text",
"text": " "
}
},
{
"type": "divider"
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Submit"
},
"value": "submit_form",
"action_id": "submit_action"
}
]
}
]
And passing the black to post a message like this
app.client.chat_postEphemeral(
channel=body["channel"]["id"],
user=body['user']['id'],
blocks=param_block,
text="text_msg",
)
Is there a way to consider text input field with just numerical values?
I'm also attaching the screenshot of the block
I currently have my application to open a modal when a global shortcut is used. The modal opens and I am presented with 3 options. I was able to get it to push a new modal when any of the options were selected, but now I am stuck on how to open different modals based on the user response. Right now the code does not interpret which was selected, and just that an actionId-0 took place. I guess I am not really sure at the next steps. Do I need to utilize #app.view in order to read the submission payload?
Open on shortcut:
def open_modal(ack, shortcut, client):
# Acknowledge the shortcut request
ack()
# Call the views_open method using the built-in WebClient
client.views_open(
trigger_id=shortcut["trigger_id"],
# A simple view payload for a modal
view={
"title": {
"type": "plain_text",
"text": "#Tech Request"
},
"submit": {
"type": "plain_text",
"text": "Submit"
},
"type": "modal",
"close": {
"type": "plain_text",
"text": "Cancel"
},
"blocks": [
{
"type": "actions",
"elements": [
{
"type": "static_select",
"placeholder": {
"type": "plain_text",
"text": "Select an issue type"
},
"options": [
{
"text": {
"type": "plain_text",
"text": "Account Issues"
},
"value": "value-0"
},
{
"text": {
"type": "plain_text",
"text": "M1"
},
"value": "value-1"
},
{
"text": {
"type": "plain_text",
"text": "M2"
},
"value": "value-2"
}
],
"action_id": "actionId-0"
}
]
}
]
}
)
Open when any option is selected:
def update_modal(ack, body, client):
# Acknowledge the button request
ack()
# Call views_update with the built-in client
client.views_update(
# Pass the view_id
view_id=body["view"]["id"],
# String that represents view state to protect against race conditions
hash=body["view"]["hash"],
# View payload with updated blocks
view={
"title": {
"type": "plain_text",
"text": "Account Issue Request"
},
"submit": {
"type": "plain_text",
"text": "Submit"
},
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "This is a link to a confluence page"
},
"accessory": {
"type": "button",
"text": {
"type": "plain_text",
"text": "Click Me",
},
"value": "click_me_123",
"url": "https://google.com",
"action_id": "button-action"
}
},
{
"type": "input",
"element": {
"type": "plain_text_input",
"action_id": "plain_text_input-action"
},
"label": {
"type": "plain_text",
"text": "SFDC Contact ID:",
}
},
{
"type": "input",
"element": {
"type": "plain_text_input",
"action_id": "plain_text_input-action"
},
"label": {
"type": "plain_text",
"text": "Describe the issue",
}
},
{
"type": "input",
"element": {
"type": "multi_users_select",
"placeholder": {
"type": "plain_text",
"text": "Tag related people",
},
"action_id": "multi_users_select-action"
},
"label": {
"type": "plain_text",
"text": "Tag related people",
}
}
],
"type": "modal"
}
)
This is something that you have to take care of on the backend. I found that the easiest way to do this is to parse the payload received and send back a modal based on that
I have a python dictionary as and I want to substitute the values in it.
dict = {
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":{circle}: <{log_url}| {task}>"
}
},
{
"type": "divider"
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Dag Name:* {dag}"
},
{
"type": "mrkdwn",
"text": "*Execution Time:* {exec_time} UTC"
}
]
}
]
}
I want to substitute the values enclosed in {} such as circle, log_url and so on.
I have tried the following approach by converting it in string representation but I am getting exceptions.
dict = """
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":{circle}: <{log_url}| {task}>"
}
},
{
"type": "divider"
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Dag Name:* {dag}"
},
{
"type": "mrkdwn",
"text": "*Execution Time:* {exec_time} UTC"
}
]
}
]
}""".format(task="a",
dag="b",
exec_time="10",
log_url="url",
circle=circle)
But it it giving me errors.
ERROR - '\n\t"blocks"'
...
KeyError: '\n\t"blocks"'
How can I resolve it?
Complete code:
import json
import requests
def send_slack_success_metadata_alert():
circle = 'green-tick'
template = """{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": ":{circle}: <{log_url}| {task}>"
}
},
{
"type": "divider"
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Dag Name:* {dag}"
},
{
"type": "mrkdwn",
"text": "*Execution Time:* {exec_time} UTC"
}
]
}
]
}""".format(
task="T1",
dag="ID",
exec_time="10",
log_url="dummyurl",
circle=circle
)
print(template)
send_alert("http://localhost:80",template)
def send_alert(url, message):
requests.post(url=url, data=json.dumps({'text': message}),
headers={'Content-type': 'application/json'})
if __name__ == "__main__":
print(send_slack_success_metadata_alert())
do you need to set the values at a later step? Or could f-strings be the solution for you?
task="a"
dag="b"
exec_time="10"
log_url="url"
circle=circle
dict = {
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f":{circle}: <{log_url}| {task}>"
}
},
{
"type": "divider"
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": f"*Dag Name:* {dag}"
},
{
"type": "mrkdwn",
"text": f"*Execution Time:* {exec_time} UTC"
}
]
}
]
}