Coder Social home page Coder Social logo

mhthies / qaqabot Goto Github PK

View Code? Open in Web Editor NEW
5.0 3.0 0.0 858 KB

Telegram bot for playing the question-answer-question-answer party game in Telegram.

License: Apache License 2.0

Python 93.46% Mako 0.30% HTML 5.36% CSS 0.88%
telegram-bot python sqlalchemy cherrypy games

qaqabot's Introduction

Telegram Question-Answer-Question-Answer game bot

A Telegram bot for playing the question-answer-question-answer party game, written in Python 3 and based on the Python Telegram Bot, SQLAlchemy and CherryPy libraries.

The Game / Features

The original game is played with pen and paper: Each player gets a sheet of paper and writes down an arbitrary question. Then, they pass the sheet to the next player, who writes down an answer to the question and fold the sheet to hide the original question. The sheet is passed on to the next player, the task of whom is to find a question that is answered by the given answer. It might be the original question or not. Sometimes not even close—nobody knows until the end of the game. The sheet is folded again and the next player has to answer the new question—without knowing the previous questions and answers.

As soon as one of the sheets is full of questions and answers or the players no longer want to go on, all sheets are finished by making sure they end with an answer. Then, the folding opened up, so everyone can enjoy the amusing chains of questions and answers.

To play this game in Telegram chats, this bot can be added to any number of Telegram groups. In each group, a game can be created (/newgame) and joined by multiple group members (/join). Each player must also start a private chat with the bot.

When the game is started (/start_game), the bot asks every player privately for a question. The player answer with their question and the bot passes it privatly to the next player. If the next player is still working on his previous sheet, all incoming sheets are queued. This way, a player may also play several games in parallel—they are still always asked for one sheet at a time.

The bot supports synchronous and asynchronous games: In a synchronous game (default and /set_synchronous), the sheets are not passed on to the next player until all players have finished writing their question/answer. In an asynchronous game, the sheets are immediately passed on to the next player—as long as they are not busy with another sheet.

When the game is finished—either when the target number of rounds (number of players or /set_rounds) is reached or when manually stopped (/stop_game, /immediately_stop_game)—, the virtual sheets are presented via a web server.

Architecture

The bot is built on the SQLAlchemy ORM framework. The object-relational datamodel is defined in qaqa_bot.model.

The full business logic is contained in qaqa_bot.game, encapsulated in the GameServer class. An instance of this class holds the game config, a database sessionmaker to generate individual database sessions, as well as a way to send outgoing messages (in the form of a callback function). It provides specific methods for all interaction events that update the game state in the database and send messages as required.

The interaction with Telegram is provided by the quqa_bot.bot module, using the Python Telegram Bot library. Instances of its Frontend class hold an Updater object to interact with the Telegram API and an GameServer object to do the game logic. The class has different handlers for any kind of Telegram update (esp. commands and text messages), that are automatically registered with the Updater on initialization and use the GameServer's interaction methods to carry out the actions.

In addition, a web server is included for serving the resulting sheets of finished games. (We first tried to deliver them as messages to the group chat, which unfortunately triggered Telegrams flood prevention reproducibly.) The web frontend is built with the CherryPy web framework and its included WSGI web server. In the qaqa_bot.web module, the Controller classes with endpoint handlers are defined. The HTML templates are rendered with Mako and are located in qaqa_bot/templates/.

Incoming updates from the Telegram API (esp. incoming messages) are handled either by the Frontend on its own or with help of the GameServer: Typically, response messages that do not require interaction with the database, are sent by the Frontend immediately. (It may use the GameServer's get_translations() or translate_string() methods to get the correct translation according to the chat's preferred language.) In the other case, the Frontend's handler method identifies the correct game action, determines the action's arguments from the update data, and calls the respective method of the GameServer. The response message/s are sent by the GameServer.

We use Alembic to manage database migrations. To allow easy packaging and automatic migrations (see Deployment instructions below), the database versions are stored in qaqa_bot/database_versions/. However, you can still use the alembic cli tool normally.

Deployment

Prerequisites

For running this bot, a Python 3 environment (>= Python 3.6) with the dependencies (see requirements.txt) installed is required. It is recommended to use a virtualenv environment for easier management and updating of dependencies.

Additionally, a database in one of the DBMS supported by SQLAlchemy is required, including the appropriate Python driver library. See https://docs.sqlalchemy.org/en/13/dialects/index.html for a list of supported database systems and instructions.

For a small installation, development and testing, an SQLite database is sufficient, which can be run with Python's integrated SQLite support. For this purpose, use sqlite:////path/to/your/database.db as database connection string in the bot's config.toml. However, we recommend to use a proper™ database server for production use.

For the web frontend, an HTTP reverse proxy with HTTPS support should be configured, which makes the internal HTTP port publicly available. Typically, one would chose Apache (with a ProxyPass directive) or Nginx (with a proxy_pass command) and configure a Let's Encrypt TLS certificate.

Setup

Currently, the easiest setup is to clone this Git Repository and run the qaqa_bot python module from within it. We may add proper Python packaging later, to make installation via pip possible.

  1. clone and enter repository
    git clone https://gitea.nephos.link/michael/QAQABot.git
    cd QAQABot/
  2. setup virtualenv
    python3 -m virtualenv -p python3 venv
  3. install requirements
    venv/bin/pip install -r requirements.txt
  4. create a Telegram Bot account: follow the instructions at https://core.telegram.org/bots#creating-a-new-bot
  5. create config.toml
    cp config.example.toml config.toml
    $EDITOR config.toml
    Insert
    • the bot's username,
    • the bot's API token (recived from the BotFather),
    • your personal Telegram username (will be used in the bot's description and help texts), and
    • the connection URL of your database.
    • the public base URL of your web server (virtual host), which is used for links to the web frontend
    • the internal HTTP listening port
  6. compile i18n message catalouges
    pybabel compile -d qaqa_bot/i18n/ -D qaqa_bot

Running

Run the following command in the repository directory:

venv/bin/python -m qaqa_bot

At startup, the database will be initialized or upgraded automatically. Additionally, the Telegram bot will be configured using the Telegram API. To avoid this behaviour, add --no-init to the run command.

To run the database and bot setup manually, use --init-only. You may also run database migrations with Alembic for full control: alembic upgrade head.

Updating

Simply update the git repository, comple the new translations and restart the bot for updating:

git pull
venv/bin/pybabel compile -d qaqa_bot/i18n/ -D qaqa_bot
venv/bin/python -m qaqa_bot

As explained above, the database and Telegram bot settings will be updated automatically at startup.

License

This project is released under the Terms of the Apache License v2.0. See NOTICE and LICENSE files for more information.

Development

Creating a new database version:

alembic upgrade head
# do changes to model.py
alembic revision --autogenerate -m "do data things"
# revisit qaqa_bot/database_versions/*_do_data_things.py
git add qaqa_bot/model.py qaqa_bot/database_versions

Updating i18n translation files:

pybabel extract -F babel.cfg -k "GetText" -k "NGetText:1,2" -o qaqa_bot.pot .
pybabel update -i qaqa_bot.pot -d qaqa_bot/i18n/ -D qaqa_bot

Compiling i18n translation files:

pybabel compile -d qaqa_bot/i18n/ -D qaqa_bot

qaqabot's People

Contributors

feanya avatar mhthies avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

qaqabot's Issues

/set_lang's buttons do not work

When clicking a language button of the /set_lang inline keyboard, the following exception is logged:

qaqa_bot.bot - ERROR - Error while update {'update_id': 570115936, 'callback_query': {'id': '356326171076547235', 'chat_instance': '<snip>', 'message': {'message_id': 19, 'date': 1588425785, 'chat': {'id': <snip>, 'type': 'private', 'username': '<snip>', 'first_name': '<snip>', 'last_name': '<snip>'}, 'text': 'Please choose:', 'entities': [], 'caption_entities': [], 'photo': [], 'new_chat_members': [], 'new_chat_photo': [], 'delete_chat_photo': False, 'group_chat_created': False, 'supergroup_chat_created': False, 'channel_chat_created': False, 'reply_markup': {'inline_keyboard': [[{'text': '🇩🇪', 'callback_data': 'de'}, {'text': '🇬🇧', 'callback_data': 'en'}]]}, 'from': {'id': 1223479557, 'first_name': 'QAQA-Test-Bot (Michael)', 'is_bot': True, 'username': 'qaqagame_michael_test_bot'}}, 'data': 'en', 'from': {'id': <snip>, 'first_name': '<snip>', 'is_bot': False, 'last_name': '<snip>', 'username': '<snip>', 'language_code': 'de'}}, '_effective_user': {'id': <snip>, 'first_name': '<snip>', 'is_bot': False, 'last_name': '<snip>', 'username': '<snip>', 'language_code': 'de'}, '_effective_chat': {'id': <snip>, 'type': 'private', 'username': '<snip>', 'first_name': '<snip>', 'last_name': '<snip>'}, '_effective_message': {'message_id': 19, 'date': 1588425785, 'chat': {'id': <snip>, 'type': 'private', 'username': '<snip>', 'first_name': '<snip>', 'last_name': '<snip>'}, 'text': 'Please choose:', 'entities': [], 'caption_entities': [], 'photo': [], 'new_chat_members': [], 'new_chat_photo': [], 'delete_chat_photo': False, 'group_chat_created': False, 'supergroup_chat_created': False, 'channel_chat_created': False, 'reply_markup': {'inline_keyboard': [[{'text': '🇩🇪', 'callback_data': 'de'}, {'text': '🇬🇧', 'callback_data': 'en'}]]}, 'from': {'id': 1223479557, 'first_name': 'QAQA-Test-Bot (Michael)', 'is_bot': True, 'username': 'qaqagame_michael_test_bot'}}}
Traceback (most recent call last):
  File "/home/michael/Documents/Freizeit/Programme/python/QAQAbot/venv/lib/python3.8/site-packages/telegram/ext/dispatcher.py", line 343, in process_update
    handler.handle_update(update, self, check, context)
  File "/home/michael/Documents/Freizeit/Programme/python/QAQAbot/venv/lib/python3.8/site-packages/telegram/ext/handler.py", line 117, in handle_update
    return self.callback(update, context)
  File "/home/michael/Documents/Freizeit/Programme/python/QAQAbot/qaqa_bot/bot.py", line 235, in button
    command = query.message.reply_to_message.text
AttributeError: 'NoneType' object has no attribute 'text'

Improve bot response after editing the message

The response should contain the new message text (instead of the old message text) to be less confusing.

The optimal solution would be word- or character-wise diff view, using underlining and strikethrough to highlight insertions and deletions.

status: send option for more detailed status pages to individual players

When individually requesting a status from the bot you get a list of all games you are currently playing and a count of your current sheets.

We could provide links/inline-keyboards/something else to allow the individual user to receive the status overview that you get from the central game group, so you do not need to spam that group.

Improve setting rounds

Expected behaviour: nice way to control setting the amount of roungs
Actual behaviour: annoying way to type because clicking on the command sends it without possibility to write the amount of the rounds

Add "blind poem" mode

This feature would be similar to #25 but more sophisticated: Each player is required to submit two entries (verses) per round (except for the first and last round of a sheet), where only the second entry is passed to the next player.

Synchronous game gets stalled under certain conditions

Report:

Beim ersten Mal machte der Bot nichts nachdem alle die erste Antwort geschrieben hatten und beim zweiten Mal nach der zweiten Antwort. Wir konnten also nicht weiterspielen obwohl alle Antworten gegeben waren.

Den status haben wir gefragt, er hat auf niemanden gewartet. Wir probieren gerade den Asynchronmodus, das scheint bisher besser zu funktionieren.

Und das passiert nur im synchronmodus

status: improve /status information in groups

Per default the number of rounds is the number of players. The bot should also show that number so you don't have to manually count players.

Users should be identified via their name, with the @-handle in parenthesis behind that, for more visual clarity.

Entry authors should always be referenced with first name

… even if they have a Telegram username. We could use links to make the first name unambiguos and interactive.

This requires that we always store the first name in the database. Perhaps, it also requires that we store usernames additionally for the links. (Maybe, we can add links by account id?)

Finalizing a game does not work

Expected behavior: after the last round the bot presents the results and ends the game.
Actual behavior: most games can not be finalized due to running into Telegrams flood limits.

/help does not work

Implement /help command in group chats and private chats.

/help in private chats should show to the following link to add the bot to a group: https://t.me/${bot_username}?startgroup=now

Add adminstrator privileges

Only allow group administrators to

  • set game options: synchronous mode, rounds, game mode (when #25 or #30 are implemented), display of names
  • set language
  • start game
  • stop game
  • stop game immediately

Add "emoji whispers" mode

Add a game mode, where text messages and emoji-only messages are passed on alternately, instead of questions and answers.

This would only require modified messages as well as a modification of the game logics, such that a page usually has an odd number of entries and starts and ends with a text message (instead of starting with a question and ending with an answer).

We could also constrain the character set of the emoji messages to refuse alphabetic characters.

Add "continuation story" mode

The bot could easily adapted to play "continuation story" games, where players have to continue a story with one sentence each, but only know the current last sentence.

We would need to

  • introduce a new game setting
  • introduce a new EntryType
  • introduce a Bot command to change the setting
  • adapt the _format_for_next() method
  • adapt the result web pages

Game start/end timestamps should be stored

We could replace the is_started and is_finished boolean fields with datetime fields.

We can show the timestamps in the /status commands response and on the web result pages.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.