Skip to content

Commit b090b40

Browse files
committed
Update bot-with-card-example-flask.py
* Refactor for PEP8. * Consistently use Python 3 syntax. * Miscellaneous small imrovements.
1 parent aa0617a commit b090b40

File tree

1 file changed

+100
-93
lines changed

1 file changed

+100
-93
lines changed

examples/bot-with-card-example-flask.py

100644100755
Lines changed: 100 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
66
A bot must be created and pointed to this server in the My Apps section of
77
https://developer.webex.com. The bot's Access Token should be added as a
8-
'WEBEX_TEAMS_ACCESS_TOKEN' environment variable on the web server hosting this
8+
"WEBEX_TEAMS_ACCESS_TOKEN" environment variable on the web server hosting this
99
script.
1010
1111
This script must expose a public IP address in order to receive notifications
@@ -29,8 +29,7 @@
2929
to a user submitting a form, the details of that response will be posted in
3030
the space.
3131
32-
This script should supports Python versions 2 and 3, but it has only been
33-
tested with version 3.
32+
This script should support Python versions 3 only.
3433
3534
Copyright (c) 2016-2020 Cisco and/or its affiliates.
3635
@@ -54,34 +53,22 @@
5453
"""
5554

5655

57-
# Use future for Python v2 and v3 compatibility
58-
from __future__ import (
59-
absolute_import,
60-
division,
61-
print_function,
62-
unicode_literals,
63-
)
64-
from builtins import *
56+
import os
57+
import sys
58+
from urllib.parse import urljoin
6559

60+
from flask import Flask, request
6661

62+
from webexteamssdk import WebexTeamsAPI, Webhook
63+
64+
65+
# Script metadata
6766
__author__ = "JP Shipherd"
6867
__author_email__ = "jshipher@cisco.com"
6968
__contributors__ = ["Chris Lunsford <chrlunsf@cisco.com>"]
7069
__copyright__ = "Copyright (c) 2016-2020 Cisco and/or its affiliates."
7170
__license__ = "MIT"
7271

73-
from flask import Flask, request
74-
from signal import signal, SIGINT
75-
import requests
76-
import sys
77-
78-
from webexteamssdk import WebexTeamsAPI, Webhook
79-
80-
# Find and import urljoin
81-
if sys.version_info[0] < 3:
82-
from urlparse import urljoin
83-
else:
84-
from urllib.parse import urljoin
8572

8673
# Constants
8774
WEBHOOK_NAME = "botWithCardExampleWebhook"
@@ -94,10 +81,10 @@
9481
# Adaptive Card Design Schema for a sample form.
9582
# To learn more about designing and working with buttons and cards,
9683
# checkout https://developer.webex.com/docs/api/guides/cards
97-
card_content = {
84+
CARD_CONTENT = {
9885
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
9986
"type": "AdaptiveCard",
100-
"version": "1.0",
87+
"version": "1.1",
10188
"body": [
10289
{
10390
"type": "TextBlock",
@@ -107,9 +94,10 @@
10794
},
10895
{
10996
"type": "TextBlock",
110-
"text": "This **Input.Text** element collects some free from text. \
111-
Designers can use attributes like `isMutiline`, `maxLength` and `placeholder` \
112-
to shape the way that users enter text in a form.",
97+
"text": "This **Input.Text** element collects some free from "
98+
"text. Designers can use attributes like `isMutiline`, "
99+
"`maxLength` and `placeholder to shape the way that users "
100+
"enter text in a form.",
113101
"wrap": True
114102
},
115103
{
@@ -121,9 +109,9 @@
121109
},
122110
{
123111
"type": "TextBlock",
124-
"text": "This **Input.Number** element collects a number. \
125-
Designers can use the `max`, `min` and `placeholder` attributes \
126-
to control the input options.",
112+
"text": "This **Input.Number** element collects a number. "
113+
"Designers can use the `max`, `min` and `placeholder` "
114+
"attributes to control the input options.",
127115
"wrap": True
128116
},
129117
{
@@ -135,10 +123,12 @@
135123
},
136124
{
137125
"type": "TextBlock",
138-
"text": "The **Input.ChoiceSet** element provides a variety of ways that users \
139-
can choose from a set of options. This is the default view, but designers can \
140-
use the `style` and `isMutiSelect` attributes to change the way it works. \
141-
The choices are defined in an array attribute called `choices`.",
126+
"text": "The **Input.ChoiceSet** element provides a variety of "
127+
"ways that users can choose from a set of options. This "
128+
"is the default view, but designers can use the `style` "
129+
"and `isMutiSelect` attributes to change the way it "
130+
"works. The choices are defined in an array attribute "
131+
"called `choices`.",
142132
"wrap": True
143133
},
144134
{
@@ -179,40 +169,40 @@
179169
]
180170
}
181171

182-
# Read required environment variables
183-
import os
184-
port = 0
185-
webhook_url = ""
186-
try:
187-
webhook_url = os.environ['WEBHOOK_URL']
188-
port = int(os.environ['PORT'])
189-
os.environ['WEBEX_TEAMS_ACCESS_TOKEN']
190-
except KeyError:
191-
print('''
192-
Missing required environment variable. You must set:
193-
* WEBEX_TEAMS_ACCESS_TOKEN -- Access token for a Webex bot\n
194-
* WEBHOOK_URL -- URL for Webex Webhooks (ie: https://2fXX9c.ngrok.io)
195-
* PORT - Port for Webhook URL (ie: the port param passed to ngrok)
196-
'''
197-
)
198-
sys.exit
172+
173+
# Module variables
174+
webhook_url = os.environ.get("WEBHOOK_URL", "")
175+
port = int(os.environ.get("PORT", 0))
176+
access_token = os.environ.get("WEBEX_TEAMS_ACCESS_TOKEN", "")
177+
if not all((webhook_url, port, access_token)):
178+
print(
179+
"""Missing required environment variable. You must set:
180+
* WEBHOOK_URL -- URL for Webex Webhooks (ie: https://2fXX9c.ngrok.io)
181+
* PORT - Port for Webhook URL (ie: the port param passed to ngrok)
182+
* WEBEX_TEAMS_ACCESS_TOKEN -- Access token for a Webex bot
183+
"""
184+
)
185+
sys.exit()
199186

200187
# Initialize the environment
201188
# Create the web application instance
202189
flask_app = Flask(__name__)
203190
# Create the Webex Teams API connection object
204191
api = WebexTeamsAPI()
192+
# Get the details for the account who's access token we are using
193+
me = api.people.me()
205194

206195

207196
# Helper functions
208-
def delete_webhooks_with_name(api):
209-
"""List all webhooks and delete ours."""
197+
def delete_webhooks_with_name():
198+
"""List all webhooks and delete webhooks created by this script."""
210199
for webhook in api.webhooks.list():
211200
if webhook.name == WEBHOOK_NAME:
212201
print("Deleting Webhook:", webhook.name, webhook.targetUrl)
213202
api.webhooks.delete(webhook.id)
214203

215-
def create_webhooks(api, webhook_url):
204+
205+
def create_webhooks(webhook_url):
216206
"""Create the Webex Teams webhooks we need for our bot."""
217207
print("Creating Message Created Webhook...")
218208
webhook = api.webhooks.create(
@@ -234,97 +224,114 @@ def create_webhooks(api, webhook_url):
234224
print(webhook)
235225
print("Webhook successfully created.")
236226

237-
def respond_to_button_press(api, webhook):
227+
228+
def respond_to_button_press(webhook):
238229
"""Respond to a button press on the card we posted"""
239230

240231
# Some server side debugging
241232
room = api.rooms.get(webhook.data.roomId)
242233
attachment_action = api.attachment_actions.get(webhook.data.id)
243234
person = api.people.get(attachment_action.personId)
244235
message_id = attachment_action.messageId
245-
print("NEW BUTTON PRESS IN ROOM '{}'".format(room.title))
246-
print("FROM '{}'".format(person.displayName))
236+
print(
237+
f"""
238+
NEW BUTTON PRESS IN ROOM '{room.title}'
239+
FROM '{person.displayName}'
240+
"""
241+
)
247242

248243
api.messages.create(
249244
room.id,
250245
parentId=message_id,
251-
markdown=f'This is the data sent from the button press. A more robust app would do something cool with this:\n```\n{attachment_action.to_json(indent=2)}\n```'
246+
markdown=f"This is the data sent from the button press. A more "
247+
f"robust app would do something cool with this:\n"
248+
f"```\n{attachment_action.to_json(indent=2)}\n```"
252249
)
253250

254-
def respond_to_message(api, webhook):
251+
252+
def respond_to_message(webhook):
255253
"""Respond to a message to our bot"""
256254

257255
# Some server side debugging
258256
room = api.rooms.get(webhook.data.roomId)
259257
message = api.messages.get(webhook.data.id)
260258
person = api.people.get(message.personId)
261-
print("NEW MESSAGE IN ROOM '{}'".format(room.title))
262-
print("FROM '{}'".format(person.displayName))
263-
print("MESSAGE '{}'\n".format(message.text))
259+
print(
260+
f"""
261+
NEW MESSAGE IN ROOM '{room.title}'
262+
FROM '{person.displayName}'
263+
MESSAGE '{message.text}'
264+
"""
265+
)
264266

265267
# This is a VERY IMPORTANT loop prevention control step.
266268
# If you respond to all messages... You will respond to the messages
267269
# that the bot posts and thereby create a loop condition.
268-
me = api.people.me()
269270
if message.personId == me.id:
270271
# Message was sent by me (bot); do not respond.
271-
return 'OK'
272+
return "OK"
272273

273274
else:
274275
# Message was sent by someone else; parse message and respond.
275-
api.messages.create(room.id, text="All I do is post a sample card. Here it is:")
276+
api.messages.create(
277+
room.id,
278+
text="All I do is post a sample card. Here it is:",
279+
)
276280
api.messages.create(
277281
room.id,
278282
text="If you see this your client cannot render cards",
279283
attachments=[{
280284
"contentType": "application/vnd.microsoft.card.adaptive",
281-
"content": card_content
282-
}]
285+
"content": CARD_CONTENT
286+
}],
283287
)
284-
return 'OK'
288+
return "OK"
285289

286-
# Signal handler to clean up webhooks when we shutdown
287-
def signal_handler(sig, frame):
288-
"""Cleanup webhooks on shutdown"""
289-
print('You pressed Ctrl+C! Cleaning up webhooks...')
290-
delete_webhooks_with_name(api)
291-
sys.exit(0)
292290

293291
# Core bot functionality
294292
# Webex will post to this server when a message is created for the bot
295293
# or when a user clicks on an Action.Submit button in a card posted by this bot
296294
# Your Webex Teams webhook should point to http://<serverip>:<port>/events
297-
@flask_app.route('/events', methods=["POST"])
295+
@flask_app.route("/events", methods=["POST"])
298296
def webex_teams_webhook_events():
299297
"""Respond to inbound webhook JSON HTTP POST from Webex Teams."""
300298
# Create a Webhook object from the JSON data
301299
webhook_obj = Webhook(request.json)
302300

303301
# Handle a new message event
304-
if webhook_obj.resource == MESSAGE_WEBHOOK_RESOURCE and \
305-
webhook_obj.event == MESSAGE_WEBHOOK_EVENT:
306-
respond_to_message(api, webhook_obj)
302+
if (webhook_obj.resource == MESSAGE_WEBHOOK_RESOURCE
303+
and webhook_obj.event == MESSAGE_WEBHOOK_EVENT):
304+
respond_to_message(webhook_obj)
307305

308306
# Handle an Action.Submit button press event
309-
elif webhook_obj.resource == CARDS_WEBHOOK_RESOURCE and \
310-
webhook_obj.event == CARDS_WEBHOOK_EVENT:
311-
respond_to_button_press(api, webhook_obj)
307+
elif (webhook_obj.resource == CARDS_WEBHOOK_RESOURCE
308+
and webhook_obj.event == CARDS_WEBHOOK_EVENT):
309+
respond_to_button_press(webhook_obj)
312310

313311
# Ignore anything else (which should never happen
314312
else:
315-
print("IGNORING UNEXPECTED WEBHOOK:")
316-
print(webhook_obj)
313+
print(f"IGNORING UNEXPECTED WEBHOOK:\n{webhook_obj}")
317314

318-
return 'OK'
315+
return "OK"
319316

320317

321318
def main():
322-
# Tell Python to run the handler() function when SIGINT is recieved
323-
signal(SIGINT, signal_handler)
324-
delete_webhooks_with_name(api)
325-
create_webhooks(api, webhook_url)
326-
# Start the Flask web server
327-
flask_app.run(host='0.0.0.0', port=port)
328-
329-
if __name__ == '__main__':
319+
# Delete preexisting webhooks created by this script
320+
delete_webhooks_with_name()
321+
322+
create_webhooks(webhook_url)
323+
324+
try:
325+
# Start the Flask web server
326+
flask_app.run(host="0.0.0.0", port=port)
327+
328+
except KeyboardInterrupt:
329+
print("You pressed Ctrl+C! Shutting down.")
330+
331+
finally:
332+
print("Cleaning up webhooks...")
333+
delete_webhooks_with_name()
334+
335+
336+
if __name__ == "__main__":
330337
main()

0 commit comments

Comments
 (0)