docs: add contributing guidelines and update README for environment variables
All checks were successful
CI / lint-and-test (push) Successful in 16s
All checks were successful
CI / lint-and-test (push) Successful in 16s
- Created a new `CONTRIBUTING.md` file outlining development setup, testing, and commit message conventions. - Updated `README.md` to clarify environment variable usage, including new variables for HTTP port, CORS origins, and duty display timezone. - Enhanced documentation for bot commands and API endpoints to improve user understanding and onboarding. - Added example configurations in `.env.example` for better clarity on optional settings.
This commit is contained in:
@@ -22,3 +22,9 @@ ADMIN_USERNAMES=admin1,admin2
|
||||
|
||||
# Default UI language when user language is unknown: en or ru (default: en).
|
||||
# DEFAULT_LANGUAGE=en
|
||||
|
||||
# Reject Telegram initData older than this (seconds). 0 = do not check (default).
|
||||
# INIT_DATA_MAX_AGE_SECONDS=0
|
||||
|
||||
# Comma-separated CORS origins; leave unset for *.
|
||||
# CORS_ORIGINS=
|
||||
|
||||
56
CONTRIBUTING.md
Normal file
56
CONTRIBUTING.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Contributing to Duty Teller
|
||||
|
||||
## Development setup
|
||||
|
||||
1. **Clone the repository**
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd duty-teller
|
||||
```
|
||||
|
||||
2. **Create a virtual environment**
|
||||
```bash
|
||||
python -m venv venv
|
||||
source venv/bin/activate # Linux/macOS
|
||||
# or: venv\Scripts\activate # Windows
|
||||
```
|
||||
|
||||
3. **Install dependencies**
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
pip install -r requirements-dev.txt
|
||||
```
|
||||
|
||||
4. **Configure**
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
Set `BOT_TOKEN` and any other variables as needed (see README).
|
||||
|
||||
## Running tests and linters
|
||||
|
||||
- **Tests** (from repository root; package is `duty_teller`, no `src/`):
|
||||
```bash
|
||||
pytest
|
||||
```
|
||||
Use `PYTHONPATH=.` if imports fail.
|
||||
|
||||
- **Lint** (ruff):
|
||||
```bash
|
||||
ruff check duty_teller tests
|
||||
```
|
||||
|
||||
- **Security** (bandit):
|
||||
```bash
|
||||
bandit -r duty_teller -ll
|
||||
```
|
||||
|
||||
## Commit messages
|
||||
|
||||
Use [Conventional Commits](https://www.conventionalcommits.org/), e.g.:
|
||||
|
||||
- `feat: add /pin_duty command`
|
||||
- `fix: correct timezone in pinned message`
|
||||
- `docs: update README env section`
|
||||
|
||||
Submit changes via pull requests (Gitea Flow); reviews consider functionality, code quality, and security.
|
||||
36
README.md
36
README.md
@@ -1,6 +1,6 @@
|
||||
# Duty Teller (Telegram Bot)
|
||||
|
||||
A minimal Telegram bot boilerplate using [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) v22 with the `Application` API.
|
||||
A minimal Telegram bot boilerplate using [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) v22 with the `Application` API. The bot and web UI support **Russian and English** (language from Telegram or `DEFAULT_LANGUAGE`).
|
||||
|
||||
## Get a bot token
|
||||
|
||||
@@ -41,10 +41,14 @@ A minimal Telegram bot boilerplate using [python-telegram-bot](https://github.co
|
||||
|
||||
6. **Optional env**
|
||||
- `DATABASE_URL` – DB connection (default: `sqlite:///data/duty_teller.db`).
|
||||
- `HTTP_PORT` – HTTP server port (default: `8080`).
|
||||
- `MINI_APP_BASE_URL` – Base URL of the miniapp (for documentation / CORS).
|
||||
- `MINI_APP_SKIP_AUTH` – Set to `1` to allow `/api/duties` without Telegram initData (dev only; insecure).
|
||||
- `INIT_DATA_MAX_AGE_SECONDS` – Reject Telegram initData older than this (e.g. `86400` = 24h). `0` = disabled (default).
|
||||
- `CORS_ORIGINS` – Comma-separated allowed origins for CORS, or leave unset for `*`.
|
||||
- `CORS_ORIGINS` – Comma-separated allowed origins for CORS; leave unset for `*`.
|
||||
- `ALLOWED_PHONES` / `ADMIN_PHONES` – Access by phone (user sets via `/set_phone`); comma-separated; comparison uses digits only.
|
||||
- `DUTY_DISPLAY_TZ` – Timezone for the pinned duty message in groups (e.g. `Europe/Moscow`).
|
||||
- `DEFAULT_LANGUAGE` – Default language when user language is unknown: `en` or `ru` (default in code: `en`).
|
||||
- `EXTERNAL_CALENDAR_ICS_URL` – 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.
|
||||
|
||||
## Run
|
||||
@@ -61,6 +65,14 @@ duty-teller
|
||||
|
||||
The bot runs in polling mode. Send `/start` or `/help` to your bot in Telegram to test.
|
||||
|
||||
## Bot commands
|
||||
|
||||
- **`/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.
|
||||
- **`/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
|
||||
|
||||
Ensure `.env` exists (e.g. `cp .env.example .env`) and contains `BOT_TOKEN`.
|
||||
@@ -77,16 +89,28 @@ Ensure `.env` exists (e.g. `cp .env.example .env`) and contains `BOT_TOKEN`.
|
||||
```
|
||||
For production deployments you may use Docker secrets or your orchestrator’s env instead of a `.env` file.
|
||||
|
||||
The image is built from `Dockerfile`; on start, `entrypoint.sh` runs Alembic migrations then starts the app as user `botuser`.
|
||||
|
||||
**Production behind a reverse proxy:** When the app is behind nginx/Caddy etc., `request.client.host` is usually the proxy (e.g. 127.0.0.1). The "private IP" bypass (allowing requests without initData from localhost) then applies to the proxy, not the real client. Either ensure the Mini App always sends initData, or forward the real client IP (e.g. `X-Forwarded-For`) and use it for that check. See `api/app.py` `_is_private_client` for details.
|
||||
|
||||
## API
|
||||
|
||||
The HTTP server is FastAPI; the miniapp is served at `/app`.
|
||||
|
||||
- **`GET /api/duties`** — List of duties (date params; auth via Telegram initData or, in dev, `MINI_APP_SKIP_AUTH` / private IP).
|
||||
- **`GET /api/calendar-events`** — Calendar events (including external ICS when `EXTERNAL_CALENDAR_ICS_URL` is set).
|
||||
|
||||
For production, initData validation is required; see the reverse-proxy paragraph above for proxy/headers.
|
||||
|
||||
## Project layout
|
||||
|
||||
- `main.py` – Entry point: builds the `Application`, registers handlers, runs polling and FastAPI in a thread. Calls `duty_teller.config.require_bot_token()` so the app exits with a clear message if `BOT_TOKEN` is missing.
|
||||
- `main.py` – Entry point: calls `duty_teller.run:main`. Alternatively, after `pip install -e .`, run the console command **`duty-teller`** (see `pyproject.toml` and `duty_teller/run.py`). The runner builds the `Application`, registers handlers, runs polling and FastAPI in a thread, and calls `duty_teller.config.require_bot_token()` so the app exits with a clear message if `BOT_TOKEN` is missing.
|
||||
- `duty_teller/` – Main package (install with `pip install -e .`). Contains:
|
||||
- `config.py` – Loads `BOT_TOKEN`, `DATABASE_URL`, `ALLOWED_USERNAMES`, etc. from env; no exit on import; use `require_bot_token()` in `main` when running the bot. Optional `Settings` dataclass for tests. `PROJECT_ROOT` for webapp path.
|
||||
- `config.py` – Loads `BOT_TOKEN`, `DATABASE_URL`, `ALLOWED_USERNAMES`, etc. from env; no exit on import; use `require_bot_token()` in the entry point when running the bot. Optional `Settings` dataclass for tests. `PROJECT_ROOT` for webapp path.
|
||||
- `api/` – FastAPI app (`/api/duties`, `/api/calendar-events`), `dependencies.py` (DB session, auth, date validation), static webapp mounted from `PROJECT_ROOT/webapp`.
|
||||
- `db/` – SQLAlchemy models, session (`session_scope`), repository, schemas.
|
||||
- `handlers/` – Telegram command and chat handlers; register via `register_handlers(app)`.
|
||||
- `i18n/` – Translations and language detection (ru/en); used by handlers and API.
|
||||
- `services/` – Business logic (group duty pin, import); accept session from caller.
|
||||
- `utils/` – Shared date, user, and handover helpers.
|
||||
- `importers/` – Duty-schedule JSON parser.
|
||||
@@ -114,7 +138,7 @@ To add commands, define async handlers in `duty_teller/handlers/commands.py` (or
|
||||
|
||||
## Tests
|
||||
|
||||
Install dev dependencies and run pytest:
|
||||
Run from the repository root (no `src/` directory; package is `duty_teller` at the root). Use `PYTHONPATH=.` if needed:
|
||||
|
||||
```bash
|
||||
pip install -r requirements-dev.txt
|
||||
@@ -122,3 +146,5 @@ pytest
|
||||
```
|
||||
|
||||
Tests cover `api/telegram_auth` (validate_init_data, auth_date expiry), `config` (is_admin, can_access_miniapp), and the API (date validation, 403/200 with mocked auth, plus an E2E auth test without auth mocks).
|
||||
|
||||
**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/`).
|
||||
|
||||
Reference in New Issue
Block a user