bolt-python icon indicating copy to clipboard operation
bolt-python copied to clipboard

Issue in modal submission (We had some trouble connecting.Try again ?)

Open devikaTV opened this issue 1 year ago • 1 comments

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}"

devikaTV avatar Feb 08 '24 07:02 devikaTV

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:

  1. Update a view via response_action
  2. Update a view view API

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.

filmaj avatar Feb 08 '24 12:02 filmaj

Hi @filmaj I am still having the same issue even after only usingviews.update only

devikaTV avatar Mar 04 '24 07:03 devikaTV

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.

seratch avatar Mar 04 '24 07:03 seratch

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.

seratch avatar Mar 04 '24 07:03 seratch

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 ?

devikaTV avatar Mar 04 '24 07:03 devikaTV

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.

seratch avatar Mar 04 '24 07:03 seratch

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'}

devikaTV avatar Mar 04 '24 13:03 devikaTV

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.

seratch avatar Mar 05 '24 01:03 seratch

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

devikaTV avatar Mar 06 '24 11:03 devikaTV

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)

devikaTV avatar Mar 06 '24 11:03 devikaTV

Hi @seratch Could you please help ?

devikaTV avatar Mar 07 '24 03:03 devikaTV

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.

seratch avatar Mar 07 '24 03:03 seratch

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)

devikaTV avatar Mar 07 '24 04:03 devikaTV

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!

seratch avatar Mar 07 '24 05:03 seratch

👋 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.

github-actions[bot] avatar Apr 08 '24 00:04 github-actions[bot]

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.

github-actions[bot] avatar Apr 22 '24 00:04 github-actions[bot]