{"id":50112485,"url":"https://github.com/core-euler/quiz_bot","last_synced_at":"2026-05-23T13:05:48.727Z","repository":{"id":327746370,"uuid":"1096445325","full_name":"core-euler/quiz_bot","owner":"core-euler","description":null,"archived":false,"fork":false,"pushed_at":"2026-03-30T18:33:37.000Z","size":255,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-30T20:29:25.594Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/core-euler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-14T12:43:32.000Z","updated_at":"2026-03-30T18:33:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/core-euler/quiz_bot","commit_stats":null,"previous_names":["okoloboga/quiz_bot","core-euler/quiz_bot"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/core-euler/quiz_bot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/core-euler%2Fquiz_bot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/core-euler%2Fquiz_bot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/core-euler%2Fquiz_bot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/core-euler%2Fquiz_bot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/core-euler","download_url":"https://codeload.github.com/core-euler/quiz_bot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/core-euler%2Fquiz_bot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33396642,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T04:15:53.637Z","status":"ssl_error","status_checked_at":"2026-05-23T04:15:53.242Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-23T13:05:45.286Z","updated_at":"2026-05-23T13:05:48.714Z","avatar_url":"https://github.com/core-euler.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Telegram Quiz Bot\n\nA configurable Telegram bot for conducting quizzes, with questions, settings, and results managed through a Google Sheet. The bot is designed to be robust, featuring timed questions, error limits, and a sophisticated question selection mechanism.\n\n## Features\n\n- **Dynamic Configuration**: All quiz parameters (number of questions, error limits, cooldown periods, etc.) are configured directly in a Google Sheet, allowing for easy adjustments without code changes.\n- **Google Sheets Integration**: Seamlessly reads questions and settings from a Google Sheet and writes back the results in real-time.\n- **Timed Questions**: Each question has a configurable time limit for answering.\n- **Error Limiting**: The quiz automatically ends if a user exceeds the configured number of incorrect answers.\n- **Cooldown Mechanism**: Prevents users from retaking the test for a configurable number of hours.\n- **Proportional Question Distribution**: A smart algorithm selects questions proportionally from different categories, ensuring the quiz composition reflects the overall structure of the question database.\n- **State Management**: Uses Redis to manage user sessions, making the quiz process resilient.\n- **Easy Deployment**: Can be run locally with Python or as a containerized application using Docker and Docker Compose.\n\n## How It Works\n\nThe bot guides the user through a structured quiz process from start to finish.\n\n### 1. Starting the Quiz\n- The user initiates the interaction with the `/start` command.\n- The bot immediately reads the configuration from the \"⚙️Настройки\" (Settings) sheet in Google Sheets.\n- It performs initial checks to ensure that the configuration is valid and that there are enough questions available to build a test.\n\n### 2. User Identification\n- The bot prompts the user to enter their full name (ФИО), which is required to proceed.\n- The user confirms the entered name before the test begins.\n\n### 3. Test Preparation\n- **Cooldown Check**: The bot checks the \"📊Результаты\" (Results) sheet to find the user's last attempt. If the cooldown period (e.g., 24 hours) has not yet passed, the bot informs the user how much time is remaining.\n- **Question Selection**: The core logic for question selection is triggered. The bot fetches all questions and distributes them according to the algorithm described below.\n- **Session Creation**: A new quiz session is created and stored in Redis, containing the selected questions, user's score, and timers.\n\n### 4. The Quiz\n- The bot sends questions one by one, each with a custom inline keyboard for the answers.\n- A timer runs for each question. If the user doesn't answer in time, the quiz ends.\n- The bot tracks the number of correct and incorrect answers. If the user exceeds the maximum number of allowed errors, the quiz ends.\n\n### 5. Finishing the Test\n- Once the quiz is complete (either by answering all questions, running out of time, or making too many mistakes), the bot displays the final result.\n- The result (user's name, Telegram ID, date, score, etc.) is written as a new row in the \"📊Результаты\" (Results) sheet.\n- The user's session is cleared from Redis.\n\n## Proportional Question Distribution\n\nInstead of picking a fixed number of questions from a few random categories, the bot uses a more balanced and fair algorithm:\n\n1.  **Grouping**: All questions from the \"❓Вопросы\" (Questions) sheet are grouped by their specified category.\n2.  **Proportional Quotas**: The bot calculates a \"quota\" for each category based on its share of the total number of questions. For example, if 50% of all questions belong to \"Category A\", then approximately 50% of the questions in the quiz will be drawn from \"Category A\".\n3.  **Adjustment**: The algorithm intelligently handles cases where a category might not have enough questions to fulfill its quota, borrowing the deficit from other available categories.\n4.  **Random Selection**: Once quotas are determined, the bot randomly selects the required number of questions from each category.\n5.  **Final Shuffle**: The final list of selected questions is shuffled to ensure a random order for the user.\n\nThis approach ensures that the quiz is always a representative sample of the entire question database, automatically adapting as new questions or categories are added.\n\n## Google Sheets Setup\n\nTo use the bot, you need to create a Google Sheet with three specific tabs:\n\n### 1. `⚙️Настройки` (Settings)\nThis sheet holds the main configuration. It must contain a header row and a data row.\n\n| количество вопросов | количество допустимых ошибок | как часто можно проходить тест (часов) | количество секунд на одно задание |\n| ------------------- | ---------------------------- | -------------------------------------- | --------------------------------- |\n| 20                  | 2                            | 24                                     | 60                                |\n\n- `количество вопросов`: Total questions per quiz.\n- `количество допустимых ошибок`: Max incorrect answers allowed.\n- `как часто можно проходить тест (часов)`: Cooldown period in hours.\n- `количество секунд на одно задание`: Time limit per question in seconds.\n\n### 2. `❓Вопросы` (Questions)\nThis sheet contains the question database.\n\n| Категория | Вопрос | Ответ 1 | Ответ 2 | Ответ 3 | Ответ 4 | Правильный ответ |\n| --------- | ------ | ------- | ------- | ------- | ------- | ---------------- |\n| History   | ...    | ...     | ...     | ...     | ...     | 2                |\n| Geography | ...    | ...     | ...     | ...     | ...     | 4                |\n\n- `Категория`: The category of the question.\n- `Вопрос`: The text of the question.\n- `Ответ 1` - `Ответ 4`: The answer options.\n- `Правильный ответ`: A number from 1 to 4 indicating the correct answer column.\n\n### 3. `📊Результаты` (Results)\nThis sheet is where the bot writes the quiz results. The bot will create and populate this sheet automatically. The columns are:\n\n- Telegram ID\n- Display Name (Username or First/Last Name)\n- Test Date\n- Full Name (from user input)\n- Result (Passed/Failed)\n- Correct Count\n- Notes (e.g., if the test timed out)\n\n## Installation and Setup\n\n### Prerequisites\n- Python 3.9+\n- Docker and Docker Compose (for containerized deployment)\n- A Telegram Bot Token\n- Google Cloud Service Account credentials with access to the Google Sheets API.\n\n### 1. Clone the Repository\n```bash\ngit clone \u003crepository-url\u003e\ncd quiz_bot\n```\n\n### 2. Configure Environment Variables\nCreate a `.env` file in the project root and fill it with your credentials:\n\n```env\n# --- Telegram ---\nTELEGRAM_TOKEN=\"YOUR_TELEGRAM_BOT_TOKEN\"\n\n# --- Google Sheets ---\nSHEET_ID=\"YOUR_GOOGLE_SHEET_ID\"\n# Your Google credentials JSON, as a single line string or a path to the file.\nGOOGLE_CREDENTIALS='{\"type\": \"service_account\", \"project_id\": \"...\", ...}'\n\n# --- Redis ---\nREDIS_URL=\"redis://redis:6379/0\"\n\n# --- Logging ---\nLOG_LEVEL=\"INFO\"\n\n# --- PlanDriver ---\nPLANDRIVER_ENABLED=true\nPLANDRIVER_BASE_URL=\"https://prog.lagrangegroup.ru\"\nPLANDRIVER_TOKEN=\"YOUR_PLANDRIVER_BOT_TOKEN\"\nPLANDRIVER_POLL_INTERVAL_MINUTES=5\nPLANDRIVER_DB_PATH=\"/absolute/path/to/plandriver.db\"\nPLANDRIVER_TEST_MAPPING='{\"1\":\"Логистика\",\"2\":\"Логистика\",\"3\":\"Логистика\",\"5\":\"Техническая часть\",\"7\":\"Правила компании\",\"11\":\"Правила компании\",\"12\":\"Правила компании\",\"13\":\"Работа с документацией\",\"14\":\"Экономия топлива\"}'\n```\n\n### 3. Running the Bot\n\n#### Option A: With Docker (Recommended)\nThis is the easiest way to get the bot and its Redis dependency running.\n\n```bash\ndocker-compose up --build\n```\n\nTo run in the background:\n```bash\ndocker-compose up -d --build\n```\n\n#### Option B: Locally with Python\n1.  **Set up a virtual environment:**\n    ```bash\n    python3 -m venv venv\n    source venv/bin/activate\n    ```\n2.  **Install dependencies:**\n    ```bash\n    pip install -r requirements.txt\n    ```\n3.  **Run a local Redis instance:**\n    You need a running Redis server. If you have Docker, you can run:\n    ```bash\n    docker run -d -p 6379:6379 redis:alpine\n    ```\n    Then, update your `REDIS_URL` in the `.env` file to `redis://localhost:6379/0`.\n\n4.  **Start the bot:**\n    ```bash\n    python main.py\n    ```\n\n## PlanDriver Integration\n\nTo verify the backend path to PlanDriver, the bot only needs valid env values and access to the target Google Sheet.\n\nMinimal steps:\n\n1. Set `PLANDRIVER_ENABLED=true`\n2. Fill `PLANDRIVER_TOKEN`\n3. Start the bot\n4. Watch logs for:\n   - successful `PlanDriver Sync`\n   - response from `GET /api/bot/pending-tests`\n   - driver mapping result\n   - assignment creation in local SQLite\n   - Telegram delivery result\n\nRecommended live mapping:\n\n```json\n{\n  \"1\": \"Логистика\",\n  \"2\": \"Логистика\",\n  \"3\": \"Логистика\",\n  \"5\": \"Техническая часть\",\n  \"7\": \"Правила компании\",\n  \"11\": \"Правила компании\",\n  \"12\": \"Правила компании\",\n  \"13\": \"Работа с документацией\",\n  \"14\": \"Экономия топлива\"\n}\n```\n\nGoogle Sheets rules for the `❓Вопросы` sheet:\n\n- use the `категория` column\n- use only these canonical category names:\n  - `Логистика`\n  - `Техническая часть`\n  - `Правила компании`\n  - `Работа с документацией`\n  - `Экономия топлива`\n- do not put numeric violation codes into `категория`\n\nImportant:\n\n- live API uses numeric violation codes `\"1\"` ... `\"14\"`\n- critical codes are `4`, `6`, `8`, `9`, `10`\n- non-critical codes for online training are `1`, `2`, `3`, `5`, `7`,\n  `11`, `12`, `13`, `14`\n\n## Project Structure\n\n- `main.py`: The main entry point for the application.\n- `config.py`: Handles loading and validation of environment variables.\n- `handlers/`: Contains the bot's command and message handlers (e.g., `start`, FIO processing, test logic).\n- `services/`: Contains services for interacting with external systems like Google Sheets (`google_sheets.py`) and Redis (`redis_service.py`).\n- `utils/`: Includes utility functions, such as the question distribution algorithm.\n- `models.py`: Defines the data structures (dataclasses) used throughout the application.\n- `docker-compose.yml`: Defines the services for containerized deployment (the bot and Redis).\n- `Dockerfile`: Instructions for building the bot's Docker image.\n- `requirements.txt`: A list of Python dependencies.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcore-euler%2Fquiz_bot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcore-euler%2Fquiz_bot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcore-euler%2Fquiz_bot/lists"}