Ranked choice voting telegram bot - try it at @ranked_choice_voting_bot
Bot and webapp backends were written in Python3.12 using the python-telegram-bot bot
library, and the peewee SQL ORM. Voting webapp interface integration was written in
Typescript using React and react-telegram-web-app components.
Poll results are conducted using the https://github.com/milselarch/trie_rcv
crate via a maturin bridge.
/start
- start bot/user_details
- Shows your username and user id/create_poll ...
- Creates a new poll/create_poll @user_1 @user_2 ... @user_n: poll title poll option 1 poll option 2 ... poll option m
/create_group_poll ...
Creates a new poll that chat members can self-register for/create_group_poll @user_1 @user_2 ... @user_n: poll title poll option 1 poll option 2 ... poll option m
/register_user_id {poll_id} {user_id}
- registers a user by user_id for a poll/whitelist_chat_registration {poll_id}
whitelists the current chat so that chat members can self-register for the poll specified by poll_id within the chat group/blacklist_chat_registration {poll_id}
whitelists the current chat so that chat members can self-register for the poll specified by poll_id within the chat group/view_poll {poll_id}
- Shows poll details givenpoll_id
/vote ...
- Vote for the poll with the specifiedpoll_id
requires that the user is one of the registered voters of the poll/vote {poll_id}: {option_1} > {option_2} > ... > {option_n} /vote {poll_id} {option_1} > {option_2} > ... > {option_n} /vote {poll_id} {option_1} {option_2} ... {option_n}
The last option of the ranked vote can also accept 2 special values,abstain
andwithhold
:- Vote
withhold
to cast a vote of no-confidence and boycott all the options in the poll
(In this scenario, no vote will be given to any of the candidates, but the voter will still be counted towards the total number of voters needed to achieve a majority in the polling result calculation) - Vote
abstain
to effectively exclude yourself from the poll
(In this scenario, no vote will be given to any of the candidates, and the voter will no longer be counted towards the total number of voters needed to achieve a majority in the polling result calculation)
- Vote
/poll_results {poll_id}
- Returns poll results if the poll has been closed/has_voted {poll_id}
- Tells you if you've voted for the poll with the specified poll_id/close_poll {poll_id}
- Close the poll with the specified poll_id.
note that only the poll's creator is allowed to issue this command to close the poll/view_votes {poll_id}
- View all the votes entered for the poll with the specified poll_id. This can only be done after the poll has been closed first/view_voters {poll_id}
- Show which voters have voted and which have not/about
- View miscellaneous information about the bot/view_polls
- View all polls created by you/delete_poll {poll_id}
- Delete poll by poll_id/help
- View commands available to the bot
Commands for testing and debugging purposes:
/vote_admin ...
- Casts a vote on behalf of the specified user/vote_admin @{username} {poll_id}: {option_1} > {option_2} > ... > {option_n}
/close_poll_admin {poll_id}
- Close a poll (typically only the poll's author is allowed to do this)/unclose_poll_admin {poll_id}
- Reopen a poll (typically a poll cannot be reopened)/lookup_from_username_admin {username}
- Resolve user ID(s) given username/insert_user_admin {user_id} {username}
- Insert a user with user_id and username
/insert_user_admin {user_id} {username}
/insert_user_admin {user_id} {username} --force
Project was built using Python3.12
- Create a database and database user for the bot program to use as follows:
create database ranked_choice_voting; create user 'rcv_user'@'localhost' identified by <YOUR_MYSQL_PASSWORD>; GRANT create, alter, delete, index, insert, select, update, references ON ranked_choice_voting.* TO 'rcv_user'@'localhost'; GRANT reload ON *.* TO 'rcv_user'@'localhost';
- Create a config.yml file at the project root using config.example.yml as a template, and fill it up with MySQL credentials and telegram bot API token
- Create a new virtual env at the project root and activate it
$ python3.12 -m venv venv $ source venv/bin/activate
- Install dependencies and do database initialisation
(venv) $ python -m pip install -r requirements.txt (venv) $ python -m database.py
- Setup
pyo3
ranked choice voting algorithm integration
5.1. Install rust dependencies
sudo apt install rustc cargo
5.2. Generate python stubs
cargo run --bin stub_gen
5.3. Build the rust library
maturin develop --bindings pyo3 --release
- Install and run Redis cache
sudo apt update
sudo apt install redis-server -y
sudo vim /etc/redis/redis.conf
- Change
supervised no
tosupervised systemd
- Change
sudo systemctl restart redis
sudo systemctl status redis
sudo systemctl enable --now redis-server
- Run the bot
(venv) $ python bot.py
- Run the webapp backend server
8.1. development
python webapp.py --port <YOUR_PORT_NUMBER>
8.2. production (requires ASGI configuration as well)
uvicorn webapp:app --host 0.0.0.0 --port <YOUR_PORT_NUMBER>
Database ORM definition can be found in database.py
Uses node v20.10.0
Installation:
nvm use v20.10.0
cd telegram-webapp
npm install
Development run instructions:
cd telegram-webapp
npm run start
Production build instructions:
firebase init
npm run build
firebase deploy