What is Voting Bot
How to run
Installing & running locally
AWS Lambda / Zappa Notes
What I've learned, interesting parts of the code
Help, contribute
Poll (Voting) Bot is a demonstration of how Buttons & Cards can be used in a Webex Space. The major benefits are:
Once the Bot is up and running and has its Webhooks active, the user adds the Bot to a Webex Space. Bot sends a settings card for language selection and then sends a welcome card which allows to start a voting session.
If there are moderators in the Space, only they are allowed to start/end the session or start voting. If there are no moderators, any Space member can manage the session. Once the session is started, Bot sends another card which allows to start a particular voting. Topic and time limit has to be set for the voting. Present button allows to record a user's presence. Once the user is "present", his vote is recorded no matter if he actively clicks during the voting. If the user doesn't vote, his vote is recorded as "abstained". Once the user actively participates in the voting, no matter if he pressed the Present, he is taken as "present" until the end of the session.
Voting card is sent to the Space and all Space members can click the buttons. Last click is taken as valid for each user.
At the end of the time limit or if a user clicks End voting, the voting card is deleted and no more votes can be cast. Voting summary with optional Excel sheet is sent to the Space.
Multiple votings can be run during the session. Once the user decides to end the session,
Excel file with all votings summary is sent the Space. As the file is a part of the Space content, it is available for download to all Space members.
The Poll (Voting) Bot is designed to run in Amazon Lambda. Use Zappa to deploy it. It's using DynamoDB to store its data and runs in Flask WSGI. In development mode it can run locally as DynamoDB is provided as a Docker container and Flask can be started in development mode.
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
docker run -p 8000:8000 amazon/dynamodb-local
ngrok http 5050
.env_sample
to .env_local
, paste Access Token to WEBEX_TEAMS_ACCESS_TOKEN
curl https://webexapis.com/v1/people/me -H "Authorization: Bearer <paste_bot_access_token>"
. Get the value of id. Paste the Bot id to BOT_ID
in .env_local.dotenv -f .env_local run python poll_bot.py
The Bot is using python-dotenv to pass sensitive information, like Access Token, to the Python script. AWS Lambda with Zappa allows to run multiple instances of the same application. For example dev, production, etc. In order to allow the Vote Bot to run in such an environment the Bot allows Zappa to pass DOT_ENV_FILE environment variable to the script. DOT_ENV_FILE is a filename which contains the environment variables loaded by Dotenv. If there is no DOT_ENV_FILE the Bot loads the variables from .env file. So if you used .env_local to run the Bot locally, copy it to .env before loading the script to AWS. Or you can set the DOT_ENV_FILE in zappa_settings.json to use a different .env file for each application instance. For example:
{
"dev": {
...
"environment_variables": {
"DOT_ENV_FILE": ".env_local"
},
},
"prod": {
...
"environment_variables": {
"DOT_ENV_FILE": ".env_prod"
}
}
}
Consult the Zappa documentation on how to set it up and get it working
with your AWS account.
I'm not a professional programmer, coding is for me a way to play and learn. This section is an unordered list of things I've learned when creating the Bot.
I use AWS to host my creations, so for a database storage I decided to use DynamoDB. Which is NoSQL. When trying to learn how to use it effectively, I came across this article. Based on that I've implemented ddb_single_table_obj.py. It doesn't implement all DynamoDB features and some parts are not fully tested but it serves the purpose and it helped me to learn a bit how to use NoSQL.
Webhooks registered to the Webex platform need to be hosted on URLs which are accessible via Internet. When your application is hosted in a public cloud like AWS, you can't easily chose the public hostname at which your webhook is listening. The URL creation is automated and is part of the installation process. And you can't set it upfront in the application configuration (unless you want to pay an extra fee). Typically you learn the hostname (and URL) after the application is installed. Webhook is using HTTP POST method, so GET is available for something different. For example to receive a manual request from your web browser.
The GET/POST implementation in the Bot in spark_webhook()
distinguishes between GET and POST. If it's a POST it expects the Webhook data, if it's GET a webhook setup procedure is run. First it learns the URL to which the GET was delivered (and at which the Bot is hosted) and then runs the webhook setup in create_webhook()
. If you move the Bot from lab to production you do not need to change anything in the configuration. After the Bot is installed or its credentials changed, just copy the Bot URL which you learned from the installation process and paste it to your web browser. The Bot changes the webhook setup and sends you a greeting page.
Use python-dotenv and venv always and everywhere. Even for a small project. Venv makes the code easily portable to another platform by exporting/importing the requirements. Dotenv is a way to keep your sensitive information safe. No need to paste Access Token or other credentials to your code.
Finite-state machine (FSM) is one of the classical software design patterns. The Bot has a couple of states (session active/inactive, voting active/inactive, etc.). After exploring a few dead ends I figured out that FSM is the way to go. Since many of buttons&cards remain in the Space, users can click them any time. So it's important that the Bot responds only to the clicks (events) which are related to the current state.
Apart of the official Adaptive cards designer there is Webex Buttons & Cards Designer which provides the set of features implemented in Webex and is using the Webex UI look end feel. The designer output is in JSON format so in order to import it into Python you can do just a copy&paste and then replace true with True and you end up with a native Python dict. This is useful for removing duplicities. In the code the cards can be composed of a skeleton structure and then a couple of references to commonly used blocks. See for example how SETTINGS_BLOCK
is used in bot_buttons_cards.py.
On top of that I've changed all strings to be referenced indirectly and put in place using bot_buttons_cards.nested_replace()
. Any string in a message or a card can be referenced by {{keyword}}
. nested_replace()
converts the card or an original string to its final value. This makes the localization easy - localization strings are in localization_strings.py. And cards can be filled with the current information - for example a session name or voting topic.
The Bot is created as multi-lingual. If you want to add a localization to your language, submit your change to localization_strings.py. Of course other modifications are welcome.
Code Exchange Community
Get help, share code, and collaborate with other developers in the Code Exchange community.View Community