From 9486f7004d63a18e7b4b2958c1819a186eea05ee Mon Sep 17 00:00:00 2001 From: Nikolay Tatarinov Date: Fri, 20 Feb 2026 16:26:47 +0300 Subject: [PATCH] docs: update README and configuration documentation for clarity - Revised the README to improve clarity in instructions for accessing the calendar miniapp, including changes to the phrasing for consistency. - Updated the configuration documentation to enhance the description of the `EXTERNAL_CALENDAR_ICS_URL` setting, ensuring users understand its purpose and usage. - Improved the import format documentation by translating terms to English for better accessibility to a wider audience. - Enhanced the runbook with clearer instructions regarding access issues when using direct links, emphasizing the importance of using the bot's menu button. --- README.md | 28 ++++++++++++++-------------- docs/configuration.md | 2 +- docs/import-format.md | 8 ++++---- docs/runbook.md | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 9c6b8ab..ab7c4b1 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ A minimal Telegram bot boilerplate using [python-telegram-bot](https://github.co 5. **Miniapp access (calendar)** Set `ALLOWED_USERNAMES` (and optionally `ADMIN_USERNAMES`) to allow access to the calendar miniapp; if both are empty, no one can open it. Users can also be allowed by phone via `ALLOWED_PHONES` / `ADMIN_PHONES` after setting a phone with `/set_phone`. **Mini App URL:** When configuring the bot's menu button or Web App URL (e.g. in @BotFather or via `setChatMenuButton`), use the URL **with a trailing slash**, e.g. `https://your-domain.com/app/`. A redirect from `/app` to `/app/` can cause the browser to drop the fragment that Telegram sends, which breaks authorization. - **How to open:** Users must open the calendar **via the bot's menu button** (⋮ → «Календарь» or the configured label) or a **Web App inline button**. If they use «Open in browser» or a direct link, Telegram may not send user data (`tgWebAppData`), and access will be denied. + **How to open:** Users must open the calendar **via the bot's menu button** (⋮ → "Calendar" or the configured label) or a **Web App inline button**. If they use "Open in browser" or a direct link, Telegram may not send user data (`tgWebAppData`), and access will be denied. **BOT_TOKEN:** The server that serves `/api/duties` (e.g. your production host) must have in `.env` the **same** bot token as the bot from which users open the Mini App. If the token differs (e.g. test vs production bot), validation returns "hash_mismatch" and access is denied. 6. **Other options** @@ -63,7 +63,7 @@ The bot runs in polling mode. Send `/start` or `/help` to your bot in Telegram t - **`/start`** — Greeting and user registration in the database. - **`/help`** — Help on available commands. - **`/set_phone [number]`** — Set or clear phone number (private chat only); used for access via `ALLOWED_PHONES` / `ADMIN_PHONES`. -- **`/import_duty_schedule`** — Import duty schedule (only for `ADMIN_USERNAMES` / `ADMIN_PHONES`); see «Импорт расписания» below for the two-step flow. +- **`/import_duty_schedule`** — Import duty schedule (only for `ADMIN_USERNAMES` / `ADMIN_PHONES`); see **Duty schedule import** below for the two-step flow. - **`/pin_duty`** — Pin the current duty message in a group (reply to the bot’s duty message); time/timezone for the pinned message come from `DUTY_DISPLAY_TZ`. ## Run with Docker @@ -121,16 +121,16 @@ High-level architecture (components, data flow, package relationships) is descri To add commands, define async handlers in `duty_teller/handlers/commands.py` (or a new module) and register them in `duty_teller/handlers/__init__.py`. -## Импорт расписания дежурств (duty-schedule) +## Duty schedule import (duty-schedule) -Команда **`/import_duty_schedule`** доступна только пользователям из `ADMIN_USERNAMES` или `ADMIN_PHONES`. Импорт выполняется в два шага: +The **`/import_duty_schedule`** command is available only to users in `ADMIN_USERNAMES` or `ADMIN_PHONES`. Import is done in two steps: -1. **Время пересменки** — бот просит указать время и при необходимости часовой пояс (например `09:00 Europe/Moscow` или `06:00 UTC`). Время приводится к UTC и используется для границ смен при создании записей. -2. **Файл JSON** — отправьте файл в формате duty-schedule. +1. **Handover time** — The bot asks for the shift handover time and optional timezone (e.g. `09:00 Europe/Moscow` or `06:00 UTC`). This is converted to UTC and used as the boundary between duty periods when creating records. +2. **JSON file** — Send a file in duty-schedule format. -Формат: в корне JSON — объект **meta** с полем `start_date` (YYYY-MM-DD) и массив **schedule** с объектами `name` (ФИО) и `duty` (строка с разделителем `;`, символы **в/В/б/Б** — дежурство, **Н** — недоступен, **О** — отпуск). Количество дней задаётся длиной строки `duty`. При повторном импорте дежурства в том же диапазоне дат для каждого пользователя заменяются новыми. +Format: at the root of the JSON — a **meta** object with `start_date` (YYYY-MM-DD) and a **schedule** array of objects with `name` (full name) and `duty` (string with separator `;`; characters **в/В/б/Б** = duty, **Н** = unavailable, **О** = vacation). The number of days is given by the length of the `duty` string. On re-import, duties in the same date range for each user are replaced by the new data. -**Полное описание формата и пример JSON:** [docs/import-format.md](docs/import-format.md). +**Full format description and example JSON:** [docs/import-format.md](docs/import-format.md). ## Tests @@ -145,12 +145,12 @@ Tests cover `api/telegram_auth` (validate_init_data, auth_date expiry), `config` **CI (Gitea Actions):** Lint (ruff), tests (pytest), security (bandit). If the workflow uses `PYTHONPATH: src` or `bandit -r src`, update it to match the repo layout (no `src/`). -## Разработка (Contributing) +## Contributing -- **Коммиты:** в формате [Conventional Commits](https://www.conventionalcommits.org/): `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:` и т.д. -- **Ветки:** по [Gitea Flow](https://docs.gitea.io/en-us/workflow-branching/): основная ветка `main`, фичи и фиксы — в отдельных ветках. -- **Изменения:** через **Pull Request** в Gitea; перед мержем рекомендуется запустить линтеры и тесты (`ruff check .`, `pytest`). +- **Commits:** Use [Conventional Commits](https://www.conventionalcommits.org/): `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`, etc. +- **Branches:** Follow [Gitea Flow](https://docs.gitea.io/en-us/workflow-branching/): main branch `main`, features and fixes in separate branches. +- **Changes:** Via **Pull Request** in Gitea; run linters and tests (`ruff check .`, `pytest`) before merge. -## Логи и ротация +## Logs and rotation -Для соответствия политике хранения логов (не более 7 дней) настройте ротацию логов при развёртывании: например [logrotate](https://manpages.ubuntu.com/logrotate), настройки логирования systemd или Docker (ограничение размера/времени хранения). Храните логи приложения не дольше 7 дней. +To meet the 7-day log retention policy, configure log rotation at deploy time: e.g. [logrotate](https://manpages.ubuntu.com/logrotate), systemd logging settings, or Docker (size/time retention limits). Keep application logs for no more than 7 days. diff --git a/docs/configuration.md b/docs/configuration.md index dff4717..4fdcfd1 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -15,7 +15,7 @@ All configuration is read from the environment (e.g. `.env` via python-dotenv). | **MINI_APP_SKIP_AUTH** | `1`, `true`, or `yes` | *(unset)* | If set, `/api/duties` is allowed without Telegram initData (dev only; insecure). | | **INIT_DATA_MAX_AGE_SECONDS** | integer | `0` | Reject Telegram initData older than this many seconds. `0` = disabled. Example: `86400` for 24 hours. | | **CORS_ORIGINS** | comma-separated list | `*` | Allowed origins for CORS. Leave unset or set to `*` for allow-all. Example: `https://your-domain.com`. | -| **EXTERNAL_CALENDAR_ICS_URL** | string (URL) | *(empty)* | URL of a public ICS calendar (e.g. holidays). If set, those days are highlighted on the duty grid; users can tap «i» on a cell to see the event summary. Empty = no external calendar. | +| **EXTERNAL_CALENDAR_ICS_URL** | string (URL) | *(empty)* | URL of a public ICS calendar (e.g. holidays). If set, those days are highlighted on the duty grid; users can tap "i" on a cell to see the event summary. Empty = no external calendar. | | **DUTY_DISPLAY_TZ** | string (timezone name) | `Europe/Moscow` | Timezone for the pinned duty message in groups. Example: `Europe/Moscow`, `UTC`. | | **DEFAULT_LANGUAGE** | `en` or `ru` (normalized) | `en` | Default UI language when the user's Telegram language is unknown. Values starting with `ru` are normalized to `ru`, otherwise `en`. | diff --git a/docs/import-format.md b/docs/import-format.md index 2db9c7c..129785a 100644 --- a/docs/import-format.md +++ b/docs/import-format.md @@ -21,9 +21,9 @@ The **duty-schedule** format is used by the `/import_duty_schedule` command. Onl | Value | Meaning | Notes | |-------|----------------|--------------------------| -| **в**, **В**, **б**, **Б** | Duty (дежурство) | Any of these four | -| **Н** | Unavailable (недоступен) | Exactly `Н` | -| **О** | Vacation (отпуск) | Exactly `О` | +| **в**, **В**, **б**, **Б** | Duty | Any of these four | +| **Н** | Unavailable | Exactly `Н` | +| **О** | Vacation | Exactly `О` | | (empty/space/other) | No event | Ignored for import | The number of days in the schedule is the maximum length of any `duty` string when split by `;`. If `duty` is empty or missing, it is treated as an empty list of cells. @@ -38,7 +38,7 @@ The number of days in the schedule is the maximum length of any `duty` string wh }, "schedule": [ { - "name": "Иванов Иван", + "name": "Ivanov Ivan", "duty": ";;В;;;Н;;О;;В;;" }, { diff --git a/docs/runbook.md b/docs/runbook.md index 416d9da..52d2b55 100644 --- a/docs/runbook.md +++ b/docs/runbook.md @@ -49,7 +49,7 @@ On container start, `entrypoint.sh` runs Alembic migrations then starts the app ### Miniapp "Open in browser" or direct link — access denied - **Cause:** When users open the calendar via “Open in browser” or a direct URL, Telegram may not send `tgWebAppData` (initData). The API requires initData (or `MINI_APP_SKIP_AUTH` / private IP in dev). -- **Action:** Users should open the calendar **via the bot’s menu button** (e.g. ⋮ → «Календарь») or a **Web App inline button** so Telegram sends user data. +- **Action:** Users should open the calendar **via the bot’s menu button** (e.g. ⋮ → "Calendar") or a **Web App inline button** so Telegram sends user data. ### 403 "Open from Telegram" / no initData