bolt-python
bolt-python copied to clipboard
Issue in modal submission (We had some trouble connecting.Try again ?)
I am using socket mode python for a modal triggering with slash command
It has three stages
- modal triggered through slash command with an input field for GitHub issue id
- once the modal is submitted , some parsing on the data(GitHub issue id and issue body is retrieved) is done and if no errors, new modal which has multiple input fields(body of above issue) is updated
- once submitted , retrieve values and update the issue body
The error message is displayed when hitting the submit in first modal , sometimes the modal flow closes at that point or continue to next modal with the same error, sometimes the modal doesn't closes even after submit but the changes intended are reflected
Failed to run listener function (error: The request to the Slack API failed. (url: https://www.slack.com/api/views.update)
The server responded with: {'ok': False, 'error': 'not_found'})
Please help me in figuring out what is going wrong Attaching the code
@app.command("/editTicket")
def open_ticket_modal(ack, body, client):
ack()
client.views_open(
trigger_id=body["trigger_id"],
view={
"type": "modal",
"callback_id": "submit_ticket",
"title": {"type": "plain_text", "text": "Edit Ticket"},
"submit": {"type": "plain_text", "text": "Submit"},
"blocks": [
{
"type": "input",
"block_id": "gitissue",
"element": {
"type": "plain_text_input",
"multiline": True,
"action_id": "gitissue-value",
"placeholder": {
"type": "plain_text",
"text": "Enter ticket link",
}
},
"label": {
"type": "plain_text",
"text": " Ticket Link(s):",
"emoji": True
}
},
]
}
)
@app.view("submit_ticket")
def handle_ticket_submission(ack,body,client):
print(body)
slack_user_id = body["user"]["id"]
values = body["view"]["state"]["values"]
values[""][""]["value"] if "" in values else None
viewID = body["view"]["id"]
hashvalue = body["view"]["hash"]
gitissue = values["gitissue"]["gitissue-value"]["value"] if "gitissue" in values else None
errors = {}
gh = issue.J_Git(gh_token)
exist , issueno = gh.check_if_valid_ticket(gitissue)
if not exist :
print("Enter a valid ticket link")
errors["gitissue"] = "Enter a valid ticket link"
if slack_user_id not in editAccessGroup:
print("You do not have permission to edit this ticket")
errors["gitissue"] = "You do not have permission to edit this ticket"
if len(errors) > 0:
ack(response_action="errors", errors=errors)
else:
ack(response_action = "update")
#fetching the current value of issue
values = gh.stripBody(gitissue)
# global variable - editing ticket- to access it in next callback id function
global ticket
ticket = gitissue
client.views_update(
view_id = viewID,
hash = hashvalue,
view={
"type": "modal",
"callback_id": "submit_edit_ticket",
"submit": {
"type": "plain_text",
"text": "Submit",
"emoji": True
},
"close": {
"type": "plain_text",
"text": "Cancel",
"emoji": True
},
"title": {
"type": "plain_text",
"text": "Risk Intake Edit Form",
"emoji": True
},
"blocks": [
...
]
}
}
@app.view("submit_edit_ticket")
def handle_edit_submission_event(ack,body,client):
ack()
print(body)
print(ticket)
slack_user_id = body["user"]["id"]
values = body["view"]["state"]["values"]
values[""][""]["value"] if "" in values else None
#retrive values of the input fields
if len(errors) > 0:
ack(response_action="errors", errors=errors)
else:
ack(response_action = "clear" )
gh = issue.J_Git(gh_token)
url = gh.update_issue_body(ticket,Body)
message = f"Hey \nYou can check the updated issue below \n {ticket}"
I believe the problem is here:
else:
ack(response_action = "update")
...
client.views_update()
You are essentially making two calls to the views.update API in this code block: once via ack(response_action="update") and then again view client.views_update. Both of these calls are doing the same thing: updating the view in response to the user clicking the submit button.
It seems in the ack(response_action="update") case, you are not using this API correctly. Have a look at Updating View Modals documentation. This part of the documentation has two subsections:
As you can see in the Update a view via response_action, as part of this API, you should provide the updated view. In your code, you are not doing this - you are telling Slack "please update the view" but you are not providing what to update the view to.
My suggestion is to use either of these calls, but not both. Your call to the views.update API via the client looks correct to me: it provides the view ID, the hash (to prevent race conditions) and the updated view contents.
Hi @filmaj
I am still having the same issue even after only usingviews.update only
This might be confusing, but in response to view_submission requests (meaning within @app.view listeners in a Bolt app), you must use only ack() to manipulates modals. Calling views.update API etc. does not work stably for you.
It is also feasible to update a modal with ack() method first, and then call views.update API for async update again.
Here is a simple code example I've shared in the past: https://github.com/seratch/deepl-for-slack/blob/d8d3e30f2cc032b77d2da3ac9bdead61b875200f/src/index.ts#L40-L58
If this approach does not work for you, perhaps, your code may not use a valid view_id for views.update API call.
I see But in the docs I can only see examples of views.update API https://slack.dev/bolt-python/concepts Could you please provide me any examples of using ack() to manipulate modals ?
The "client.views_update()" code exmaple in the document is for @app.action listener use cases. For the pattern, views.update API is the right way to manipulate modals. I've shared an example in the previous reply. Please refer to the code for more details.
Hi @seratch
Same issue with error (We had some trouble connecting), views are being updated after 5 sec
The whole thing works fine but with delay and that error displayed at the top of the modal
slack_sdk.errors.SlackApiError: The request to the Slack API failed. (url: https://www.slack.com/api/views.open, status: 200)
The server responded with: {'ok': False, 'error': 'expired_trigger_id'}
It seems that you're trying to open a new modal using views.open API. When you already opened a modal, views.open never works for the same user interaction. You can use either views.update or views.push API in the case.
If you're still confused with the modal APIs, I highly recommend using only ack(response_action="update") within @app.view listeners. This approach is very simple, plus you won't be confused with anything. Also, most use cases can be covered by the approach.
So I want a second view on view submission of a modal before the end processing of data
For that I used ack(response_action="update", view= updated_view) only instead of web client API
But now the whole modal is getting closed suddenly once I hit submit
I can see the second view if I am using any listeners inside first view and use web client API
But then the submit button will be there doing nothing (I believe we can't remove it in a modal with input type fields)
Hi @seratch Could you please help ?
Sorry, with given the last two comments, I am not sure what you're struggling with now.
Could you share a simplified code snippet demonstrating what you want to do (or the one that does not work as you expect)? As we are unable to help you out for the entire app development, you don't need to share your complete code. With simple code snippets, we may be able to help you on more specifics.
Sure Please check the below code snippet
@app.command("/editticket")
def open_ticket_modal(ack, body, client):
ack()
res=client.views_open(
trigger_id=body["trigger_id"],
view={
"type": "modal",
"callback_id": "submit_ticket",
"title": {"type": "plain_text", "text": "Edit Ticket"},
"blocks": [
]
}
)
print(res)
@app.view("submit_ticket")
def handle_ticket_submission(ack,body,client):
ack()
values = body["view"]["state"]["values"]
errors = {}
# performs some checks on input entered in previous view and raise errors
if len(errors) > 0:
ack(response_action="errors", errors=errors)
else:
ack()
#a github api call to get values to fill in the next view
updated_view={
"type": "modal",
"callback_id": "submit_edit_ticket",
"blocks": [
]
}
ack(response_action="update", view= updated_view2)
@app.view("submit_edit_ticket")
def handle_edit_submission_event(ack,body,client):
ack()
slack_user_id = body["user"]["id"]
values = body["view"]["state"]["values"]
values[""][""]["value"] if "" in values else None
# fetch values from each block in previous view
errors = {}
#checks for errors
if len(errors) > 0:
ack(response_action="errors", errors=errors)
else:
ack(response_action = "clear" )
#api call for updations
message = f"....."
client.chat_postEphemeral (channel = Channel_name, text = message , user = slack_user_id)
Thanks for sharing it. Here is the complete example code. Your code had a few issues:
- ack() closes the current modal, so if you want to continue the interaction, ack with response_action is the way to go
- title property is always required, since your view data lacks it, the ack() call failed
import os
import logging
import time
from slack_sdk import WebClient
from slack_bolt import App, Ack
from slack_bolt.adapter.socket_mode import SocketModeHandler
logging.basicConfig(level=logging.DEBUG)
app = App(token=os.environ.get("SLACK_BOT_TOKEN"))
@app.command("/editticket")
def open_ticket_modal(ack: Ack, body: dict, client: WebClient):
ack() # ack comamnd execution within 3 seconds
# Open a modal dialog
client.views_open(
trigger_id=body["trigger_id"],
view={
"type": "modal",
"callback_id": "submit_ticket",
"title": {"type": "plain_text", "text": "Edit Ticket"},
"submit": {"type": "plain_text", "text": "Submit"},
"blocks": [
{"type": "section", "text": {"type": "mrkdwn", "text": "First page"}},
],
},
)
@app.view("submit_ticket")
def handle_ticket_submission(ack: Ack, payload: dict, client: WebClient):
# Don't call ack() here, ack with no arg closes this modal
errors = {}
if len(errors) > 0:
ack(response_action="errors", errors=errors)
else:
ack(
response_action="update",
view={
# type, callback_id, title, blocks are required
"type": "modal",
"callback_id": "submit_edit_ticket",
"title": {"type": "plain_text", "text": "Edit Ticket"},
# intentionally removed "submit" here to avoid the end-user submitting this modal before it's ready
"blocks": [
{
"type": "section",
"text": {"type": "mrkdwn", "text": "Loading..."},
},
],
},
)
# demonstrating how to asynchronously update the same modal
time.sleep(3)
client.views_update(
view_id=payload["id"],
view={
"type": "modal",
"callback_id": "submit_edit_ticket",
"title": {"type": "plain_text", "text": "Edit Ticket"},
"submit": {"type": "plain_text", "text": "Submit"},
"blocks": [
{
"type": "section",
"text": {"type": "mrkdwn", "text": "Second page loaded!"},
},
],
},
)
@app.view("submit_edit_ticket")
def handle_edit_submission_event(ack, body, client):
errors = {}
if len(errors) > 0:
ack(response_action="errors", errors=errors)
else:
# If you want to close this modal immediately,
# either ack() or ack(response_action="clear") works
ack(
response_action="update",
view={
"type": "modal",
"callback_id": "submit_edit_ticket",
"title": {"type": "plain_text", "text": "Edit Ticket"},
# intentionally removed "submit" as this is the completion page
"blocks": [
{
"type": "section",
"text": {"type": "mrkdwn", "text": ":white_check_mark: Done!"},
},
],
},
)
if __name__ == "__main__":
SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start()
I believe this example should answer all the questions you had. Would you mind closing this issue? Whenever you have a new question, please feel free to open a new issue for it!
👋 It looks like this issue has been open for 30 days with no activity. We'll mark this as stale for now, and wait 10 days for an update or for further comment before closing this issue out. If you think this issue needs to be prioritized, please comment to get the thread going again! Maintainers also review issues marked as stale on a regular basis and comment or adjust status if the issue needs to be reprioritized.
As this issue has been inactive for more than one month, we will be closing it. Thank you to all the participants! If you would like to raise a related issue, please create a new issue which includes your specific details and references this issue number.