docs: update README and configuration documentation for clarity
All checks were successful
CI / lint-and-test (push) Successful in 19s
All checks were successful
CI / lint-and-test (push) Successful in 19s
- 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.
This commit is contained in:
28
README.md
28
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.
|
||||
|
||||
@@ -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`. |
|
||||
|
||||
|
||||
@@ -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": ";;В;;;Н;;О;;В;;"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user