This commit is contained in:
yash 2026-02-26 16:30:56 +03:00
parent 8a76cca5fb
commit 7eb4977b99
6 changed files with 537 additions and 5 deletions

71
CLAUDE.md Normal file
View file

@ -0,0 +1,71 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
```bash
# Run the app (requires CONFIG_PATH env var)
make run
# or explicitly:
CONFIG_PATH=./internal/config/local.yml go run ./cmd/app/main.go
# Build binary
go build -o crypto_alert_bot ./cmd/app/main.go
# Start PostgreSQL via Docker
docker compose up -d
# Create a new migration (NAME=migration_name)
make migrate_create NAME=add_alerts_user_id
# Edit the generated .up.sql and .down.sql in internal/repository/postgresql/migrations/
# Migrations run automatically on startup — no manual migrate up needed
```
There are no tests in this codebase.
## Architecture
Go Telegram bot that monitors cryptocurrency prices from Bybit and notifies users when price thresholds are hit.
### Layer structure (clean architecture)
```
cmd/app/main.go → entrypoint: wires config, logger, storage, usecase, bot, alerter
internal/config/ → Config struct loaded from YAML via CONFIG_PATH env var
internal/entities/ → domain types: User, Pair, Price, Alert, Candle (no business logic)
internal/provider/ → Provider interface + Bybit REST implementation
internal/repository/ → Storage interface + PostgreSQL implementation (pgxpool)
internal/usecase/ → business logic; depends on repository.Storage interface
internal/service/alerter/ → background price-checking service; fires and disables alerts
internal/bot/telegram/ → Telegram bot: command handlers, inline keyboards, multi-step flows
internal/logger/ → wraps charmbracelet/log returning *slog.Logger
```
### Key design decisions
- **Config**: YAML at `$CONFIG_PATH`. Local dev config at `internal/config/local.yml`. Sections: `logger`, `postgresql`, `telegram`, `providers.bybit`.
- **Migrations**: Embedded via `go:embed` in `internal/repository/postgresql/migrations/embed.go`, applied automatically on startup via `golang-migrate`.
- **Alerter service** (`internal/service/alerter/`): Runs a goroutine on a 1-minute ticker. On each tick, it fetches OHLC candles (not live price) for the gap since `lastCheckedAt`, using `selectCandleInterval` to pick the coarsest interval that fits in one Bybit request (≤ 1000 candles). Alerts trigger if any candle's High/Low crosses the target. `lastCheckedAt` is persisted to `alerter_state` so missed candles are rechecked after restarts.
- **In-memory alert cache** (`alertsCache`): Keyed by `AlertID` and `InstrumentID`. Only instruments with cached alerts have candles fetched. Cache is loaded from DB on startup via `LoadAlerts`, and kept in sync via `AddAlert`/`RemoveAlert` when the bot creates/removes alerts.
- **Circular dependency** between `Bot` and `Alerter`: resolved by constructing both independently and injecting `Alerter` into `Bot` via `bot.SetAlerter(al)` after both are created. `Bot` implements the `alerter.Notifier` interface (`NotifyAlert`), which the `Alerter` uses to send Telegram messages when an alert fires.
- **Alert condition auto-detection**: When a user sets a target price, the condition (`above`/`below`) is inferred automatically — if target ≥ current ask → `above`, else → `below`. Candle High is checked for `above`, candle Low for `below`.
- **Bot user state**: Per-user `userState` (protected by `sync.Mutex`) tracks multi-step flows (`stepAddAlertPrice`, `stepEditAlertPrice`). State is reset on completion, error, or `/cancel`.
- **Decimal arithmetic**: Prices use `github.com/shopspring/decimal` to avoid floating-point precision issues. Alert prices are stored as `text` in PostgreSQL.
- **Storage interface** (`internal/repository/repository.go`): Decouples usecase layer from PostgreSQL; implement this to swap databases.
### Database schema
- `users` — Telegram users (UUID PK, unique `telegram_id bigint`)
- `currency` — currency symbols (e.g. BTC, USDT); pre-seeded in migration
- `instrument` — trading pairs (base + quote currency FK); pre-seeded with BTC/USDT, ETH/USDT, SOL/USDT
- `alert` — price alerts per user/instrument with `active` flag and `alert_condition` enum; deactivated when triggered
- `alerter_state` — single-row table storing `last_alert_check timestamptz`; persists the timestamp across restarts so the alerter can fetch candles for any missed interval
### Bot commands / flows
- `/start` — register user
- `/instruments` (or "Instruments" button) — list available trading pairs
- `/add_alert` (or "Add Alert" button) — multi-step: select instrument → enter price → alert created
- `/my_alerts` (or "My Alerts" button) — list active alerts with inline Edit/Remove buttons
- `/cancel` — abort current multi-step flow