Webex Webhook Actions
via Bot

Now that you can send messages from the portal via the Bot, you can focus on receiving messages via webhooks. In this chapter you will:

  1. Remove and Add Webhooks
  2. Handle incoming Webhook messages
  3. Configure a message response
  4. Sending an Adaptive Card
  5. Configure Adaptive Card Response Actions

This is an area that was not covered in previous examples, because the tasks require a web server, such as the Flask instance you are working on, to receive HTTPS POST messages from Webex.

Step 1 - Remove and Add Webhooks

The earlier webhook chapter involved sending requests to create two webhooks, one for messages, one for attachmentActions event types with an HTTP callback destination. Those webhooks will remain active until they are removed or they encounter enough errors to where Webex marks them disabled (for example, after 100 failed attempts in a 5-minute period).

To make sure that you don't have multiple webhooks registered, a good housekeeping practice is to remove any existing webhook and re-add them each time you start your Flask app.

  1. In your VS Code tab, open up flaskr/api/v1/wbxt.py using the Explorer
  2. If you completed earlier sections, this file will already have access to the WBX_BOT_ACCESS_TOKEN environment variable which will be used for all webhook tasks.

    Your first job is to finish out the functions that remove and re-add the webhooks. Starting with the delete_webhooks_by_name() function. The SDK has a WebehookAPI with some methods that mirror the capabilities from the Webex documentation.

    The wbx_bot_api.webhook.list() method is the SDK's version of the Webex List Webhooks API.

    As with other list() methods, you can iterate through until you find the "name" that matches the WBX_WEBHOOK_NAME, a constant set to "Cisco Live LTRCOL-2574 Webhook", and then call the wbx_bot_api.webhook.webhook_delete() method supplying the webhook ID of the one you found.

    Remove the pass at the beginning of the function and replace it with the code below.

  3. Next, create both webhooks (one for messages and one for attachmentActions). Using the SDK, the wbx_bot_api.webhook.create() method is equivalent to the Webex Create a Webhook API that you used previously. The Create a Webhook API WebhookResource and WebhookEventType models used here from the SDK ensure that the data sent is validated and normalized.

    Find the create_webhooks(), remove the pass at the beginning of the function and add the following to your file:

  4. Because the wbx_bot_api variable used all API actions that involve your Bot was instantiated in the previous section (at the very beginning), you can just call the functions you created directly.

    Add the following code to your file:

  5. In VS Code, click Save on the file then start (or restart) your Flask app by clicking Debug > Start Debugging (or Restart Debugging, if already started).
  6. You should see messages that the Webhooks are deleted (the ones you created manually) and two new ones created.

Step 2 - Handling incoming webhook messages

You may have noticed that are webhook callback URL is http://collab-api-webhook.ciscolive.com:9006/api/v1/wbxt/events. This callback destination routes from Webex directly to your lab Flask web server. The webhooks will be routed to /events in this flaskr/api/v1/wbxt.py file, where this Namespace is configured.

  1. Find the wbxt_events_api class. It has an @api.route('/events') decorator in front of it, to let flask know that this will be a destination for that route.
    The code has a few important steps:
    • The JSON data of the request (request.json) is used to create a WebhookEvent object using its parse_obj method.
    • Depending on the resource, you can call the respond_to_message() or respond_to_button_press() method. Otherwise, don't reply with any other data.
    Scroll down to the post() method of the wbxt_events_api class and add the following:

Step 3 - Configure a message response

You have webhooks configured and a Flask app to receive them. You just need to decide how to handle the message as it arrives.

  1. In this section, you can utilize the information obtained from the webhook event to:
    • Determine the Room that originated the event
    • The sent Message itself
    • This Message, in turn, is used to look up the sender
    • Determine if this is a message that you want to respond to

      Why do you need to look up the sender and the message for a generic response that only requires the room ID? Consider that this Bot could have been added by anyone using Webex. You cannot control that. But you can control who to respond to. In this case, you might want to only reply if the sender is in the same Webex organization as the Bot. You could also base the response on the email domain or even specific user who sent the message. Furthermore, you never want to respond if the sender is the Bot itself.
    • Based on who you decide to respond to, create your response message with wbx_bot_api.messages.create(), using the room id.

    Scroll up a bit to find the def respond_to_message() function. Remove the pass and replace it with the following. Note that the "self.send_card(room_id=room.id)" line is there already but that function does not do anything yet.

  2. In VS Code, click Save on the file then start (or restart) your Flask app by clicking Debug > Start Debugging (or Restart Debugging, if already started).
  3. The Webex app should be still running. If not, you can launch it and sign in using email: pod6wbxuser@collab-api.com and password: C1sco.123
  4. Click to start a conversation with your Pod6 Test Bot (clus2024-lab-pod6-bot@webex.bot)
  5. Send a message and verify that you receive "Your available options are:" in reply.

Based on the message reponse, you are clearly not done. Next add an adaptive card to the response.

Step 4 - Respond with an Adaptive Card

The intricacies of adaptive card design is definitely beyond the scope of this lab. It is just a JSON-formatted text file used implement a graphical element similar to an HTML web form, including certain graphics, buttons and drop-downs. For this lab a simple adaptive card has been provided in the file: flaskr/api/v1/adaptive_card.json.

It has a few actions, that will generate data that you can see when when a user clicks a button on the card. If you like, you can copy the contents of that file and paste it in a message to the Webhook Echo Bot (webhook-echo@webex.bot) and see what it looks like. Shortly, your web server will be responding with the same content, so you can see it then.

In your code, you need to do two things: send this adaptive card as part of a message, so the sender can see it; and then handle any submissions. Start by sending the card:

  1. The adaptive card file is already loaded for you and stored in the adaptive_card_content variable. Expand the send_card function to send the adaptive card content as an attachment to a message. As indicated, the text will only be visible if the client cannot render the card.

    Remove the pass statement and replace it with the following:

  2. Now we have the ability to respond with a card. Save your file in VS Code then start (or restart) your Flask app by clicking Debug > Start Debugging (or Restart Debugging, if already started).
  3. Click to resume the conversation with your Pod6 Test Bot (clus2024-lab-pod6-bot@webex.bot)
  4. Send another message and see if you get the adaptive card in return

Step 5 - Configure Response Actions

So far, you are using the webhook to respond to text-based messages. The reply now also includes an adaptive card. The final step is to respond to submissions by that adaptive card, which triggers a second webhook.

All the work has to be done in the respond_to_button_press() function. Similar to replying to a message, we need to determine the room the message originated from, instead of a message, an attachment action must be read, which will allow us to read the submitted data as well as determine the sender. We will again only reply to a sender within our own Webex organization.

  1. Located the respond_to_button_press() method, where the room, attachement_action, and sender are determined similar to a received message.

  2. You now just need to determine the action to take based on the particular submission. Let's take the get version request. The attachment action will have an 'action' key that, per the adaptive card, will have have a value of get_ucm_version based on that specific button press. You can then call the API via cucm_get_version_api.get(Resource), convert that reply to JSON, then reply back with the version if the query was successful. Note that you can use markdown instead of plain text, so some things can be bolded.

  3. Another button action triggers the get_ucm_reg_phones action. For this, you can leverage the cucm_perfmon_api.post() function created earlier, passing the perfmon_counters directly to the method. Depending on the logic you want to implement, you can just sum the values of the counters together and return the result in the reply message.

  4. Lastly, you can handle the user_search action. You have to read the user key, which stores the user ID to query. In flaskr/api/v1/core.py, you already have a core_users_api class, which calls methods in the classes you created, namely cucm_user_api to query CUCM, and wbxc_user_api, which queries Webex. for user information. A determination is made as to where the user "lives" (i.e. if the CUCM user has a Home Cluster property set, or the Webex Calling user has a Location ID ). The result is simply which phone system the user belongs to. If the user_detail checkbox is selected during the card submission, then the full user details are also sent.

  5. In VS Code, start (or restart) your Flask app by clicking Debug > Start Debugging (or Restart Debugging, if already started).
  6. In your Webex app, access your conversation Pod6 Test Bot (clus2024-lab-pod6-bot@webex.bot)
  7. Now try clicking the buttons, such as the Version and Get Registered Phones under Unified CM, or try looking up pod6wbxuser under User Lookup.

Congratulations! You have completed have implemented all the Python functions to access our backend Collaboration server APIs. Now you can see how this can all be leveraged using in a sample Provisioning Portal web application, that displays a server health dashboard and allows for basic user administration tasks.


Note: You will need to have a valid bot access token for this to work.