Commit 4f9e415
authored
Python rewrite with charts and slash commands (#16)
* fix: add timeout to health check endpoints to prevent hanging
- Added 5 second timeout to /health and /health/all endpoints
- If health check takes longer than 5s, returns unhealthy instead of hanging
- Refactored handlers to use timeout utility from tokio::time
- Fixed return type issue where Err variant had wrong format
* fix: use tokio::sync::Mutex instead of std::sync::Mutex in HealthAggregator
The std::sync::Mutex was causing the async tokio runtime to block when
the health check tried to acquire the lock, leading to health check
timeouts and Discord gateway disconnects. Changed to tokio::sync::Mutex
which properly yields to the scheduler when contended.
* fix: add Discord connectivity monitoring and auto-restart logic
- Fix periodic Discord connectivity test: was mathematically impossible (mod 4 vs mod 10)
- Fix loop exit logic: return Err in async block didn't exit loop, use break instead
- Fix fire-and-forget periodic test: now awaits and checks failures synchronously
- Fix Mutex API usage: tokio::sync::Mutex returns guard directly, not Result
- Add time-based connectivity check every 60 seconds
- Add restart triggers: gateway_failures>=5, discord_test_failures>=3, no Discord success >2min
- Remove unreachable!() panic in format_custom_status
* fix: remove unreachable!() panics in format_custom_status
Change % 4 to % 40 for update_count to allow periodic connectivity test
to trigger, but update all match expressions to use update_count % 4
so they cycle properly instead of panicking on values 4-39
* fix: use subquery for DELETE LIMIT to support older SQLite versions
SQLite doesn't support DELETE ... LIMIT until version 3.35.0.
Wrap LIMIT in subquery to delete by rowid instead.
* fix: improve SQLite concurrency settings for better handling of many bots
- Increased busy_timeout to 60s
- Increased pool size from 4 to 16 connections
- Added min_idle of 4 connections
- Added 64MB cache
- Added temp_store = MEMORY
Note: PostgreSQL migration attempted but requires async rewrite of all call sites
* feat: migrate from SQLite to PostgreSQL for better concurrency
- Replace rusqlite/r2d2 with async sqlx and PgPool
- All database methods are now async fn
- Add PostgreSQL service to docker-compose.yml with healthcheck
- DATABASE_URL env var replaces DATABASE_PATH
- Update all code to use .await for database calls
- PostgreSQL eliminates 'database is locked' errors with 24+ bots
* feat: improve stability with task supervision and shared price state
Issue 4 - DB Write Failures:
- Add db_failures counter to HealthState
- Increment on DB write failure, reset on success
- Mark unhealthy if db_failures > 3
Issue 5 - Health Server Bind Failure:
- Add start_health_server_with_retry() with 10 retries
- Health server bind failure is now non-fatal
- Bots continue running even if health server fails
Issues 1 & 6 - Silent Task Death and Supervision:
- All spawned tasks now have supervision loops
- Services auto-restart on unexpected exit
- Store and monitor all service join handles
- Log CRITICAL errors when services die unexpectedly
Issue 2 - Race Condition on prices.json:
- Add SharedPrices with tokio::sync::RwLock
- Price service writes to shared state atomically first
- Bots read from shared state instead of file
- File write happens AFTER atomic state update
* fix: compilation errors in stability migration
- Fix syntax error in database.rs (extra parenthesis)
- Fix Arc/SharedPrices type mismatches across modules
- Fix i64 vs u64 type casts in db_cleanup.rs
- Fix duplicate Arc import in price_service.rs
- Fix private struct re-exports from price_state
- Fix ticker move before use in async spawn loop
- Fix health_server return type in match arm
* feat(v2): Python rewrite with discord.py and asyncpg
- Python bot using discord.py (more stable Discord ecosystem)
- PostgreSQL via asyncpg (native async, connection pooling)
- Pyth Network API for price feeds
- Simple docker-compose with build inside
- Environment-based configuration for bot tokens
* fix: asyncio.run() and idempotent table creation
- Use asyncio.run() to properly run async tasks
- Catch table/index creation errors gracefully
- Allow multiple bots to initialize DB concurrently
* feat: cycle through BTC/ETH/SOL with 1h percentage change
- Status cycles through BTC, ETH, SOL every 30 seconds
- Shows 1h percentage change from database
- Nickname shows crypto and formatted price
* fix: each bot shows its own crypto price, not cycling
* fix: SHANGHAISILVER fallback to cached price if fetch fails
- Shanghai Silver website is JS-rendered, scrape doesn't work
- Only update SHANGHAISILVER if price > 10 (silver is ~30+)
- Otherwise use cached database price
* fix: SHANGHAISVER - goldsilver.ai is fully JS-rendered, no live data available
- Site uses client-side JavaScript rendering, cannot scrape
- Rust version may have worked with different site configuration
- SHANGHAISILVER now shows no price until alternative source found
- Other cryptos (BTC, ETH, SOL, etc.) work fine with Pyth Network
* feat: SHANGHAISILVER price fetch works!
- Fixed extraction to find dollar amounts after 'Shanghai Spot'
- Pattern: find prefix, then look for $ followed by number
- Shanghai Silver now fetches ~$92 from goldsilver.ai
* fix: uppercase ticker names in Discord nicknames
* feat: DXY via Yahoo Finance (DX-Y.NYB)
- Added Yahoo Finance API support for DXY
- Uses chart API endpoint with regularMarketPrice
- DXY now shows ~98 in Discord
* feat: cycle status between BTC value and 1h percentage
- Nickname: CRYPTO
- Status cycles: BTC value (e.g., ₿0.024421) then 1h change, then BTC, etc.
- For BTC bot, only shows 1h percentage (no self-conversion)
* fix: show BTC value as '0.030582 BTC' instead of ₿ symbol
* feat: cycle through BTC, ETH, SOL conversions and 1h%
- Status cycles: BTC value, ETH value, SOL value, 1h%, then repeat
- Each crypto shows its value in terms of the other three
- For BTC, shows ETH, SOL, 1h% (no self-conversion)
- For ETH, shows BTC, SOL, 1h% (no self-conversion)
- Same for SOL
* docs: update README for Python rewrite
- Clean documentation without emojis or special characters
- Updated architecture diagram for PostgreSQL
- Removed Rust/SQLite/Plotters references
- Added DXY, SHANGHAISILVER, GOLD/SILVER sources
- Document status cycling behavior
- Added project structure section
* Add chart generation commands and improve price display
- Added chart_service.py with matplotlib for beautiful dark-themed charts
- Added /chart price command showing 24h/7d/30d price charts with high/low markers
- Added /price current command with USD price, 24h/7d/30d percentage changes, and BTC/ETH/SOL conversions
- Changed SHANGHAISILVER ticker to SSILVER for brevity
- Added font dependencies to Dockerfile for matplotlib
- Updated README with slash commands documentation
- Chart commands scoped per-bot (BTC bot shows BTC charts only, etc.)
* Fix /price command to default to bot's own ticker
* Enable /chart command for all bots
* Add timeframe presets to /chart command
* Update README with SSILVER display name
* Expand /chart price documentation in README
* Add autocomplete timeframe input to /chart command
* Switch to Alpine for smaller image size (266MB vs 350MB)
* Rewrite README with clean, professional documentation
* Fix code review findings: chart dates, resource cleanup, remove dead code
* Security fixes: non-root user, fail-fast DATABASE_URL, env-based postgres credentials
* Enforce strong postgres password via env vars
* Add CI/CD with ruff linting and docker smoke test
* Fix CI: use docker compose v2 syntax, update action versions
* Fix CI: lint cleanup, fallback postgres password for CI
* Fix lint errors and CI env file setup
* Update CI: checkout@v5, opt into Node.js 24
* Fix PR review: row limits, downsampling, save conversion prices
* Implement 5-year retention with tiered aggregation
- Raw data: last 24h
- 5-min aggregates: 7 days
- Hourly aggregates: 30 days
- Daily aggregates: 1 year
- Weekly aggregates: 5 years
- Older data: auto-deleted
Chart queries auto-select appropriate aggregation level.
Maintenance runs hourly, cleanup runs daily.
* Fix PR review: DOUBLE PRECISION for prices, SSILVER fallback logic
* Fix lint errors in database.py1 parent 7bda853 commit 4f9e415
31 files changed
Lines changed: 1315 additions & 9114 deletions
File tree
- .cargo
- .factory
- .github/workflows
- src
This file was deleted.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
| 1 | + | |
| 2 | + | |
3 | 3 | | |
4 | | - | |
5 | | - | |
6 | | - | |
7 | | - | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | | - | |
12 | | - | |
| 4 | + | |
| 5 | + | |
13 | 6 | | |
14 | 7 | | |
15 | 8 | | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | 9 | | |
39 | | - | |
40 | | - | |
41 | | - | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
8 | 11 | | |
9 | 12 | | |
10 | | - | |
| 13 | + | |
| 14 | + | |
11 | 15 | | |
12 | 16 | | |
13 | | - | |
14 | | - | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
20 | | - | |
21 | | - | |
| 17 | + | |
22 | 18 | | |
23 | | - | |
24 | | - | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
25 | 23 | | |
26 | | - | |
27 | | - | |
| 24 | + | |
| 25 | + | |
28 | 26 | | |
29 | | - | |
30 | | - | |
| 27 | + | |
| 28 | + | |
31 | 29 | | |
32 | 30 | | |
| 31 | + | |
33 | 32 | | |
34 | 33 | | |
35 | | - | |
| 34 | + | |
36 | 35 | | |
37 | | - | |
38 | | - | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
39 | 40 | | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
46 | 52 | | |
47 | | - | |
| 53 | + | |
48 | 54 | | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
This file was deleted.
0 commit comments