mirror of
https://github.com/TheFunny/TelegramTwitterMediaBot.git
synced 2026-09-23 23:32:05 +00:00
Three gaps the last audit list named, all in the "resource growth, background timers and limits nobody watches" class. **Idle inline-query entries are pruned.** `DebounceStates` had no eviction at all: one entry per user who ever used inline mode, forever, while the rate limiter's buckets and the chat store both prune in the 300s sweep. Entries now carry a `last_seen` stamp and `prune_idle_states()` drops the ones idle past 300s — the window Telegram caches an inline answer for (`cache_time(300)`), after which a repeat reaches the bot again and has to be answered fresh, so the entry would only suppress a fetch the user is waiting for. The boundary is tested through `prune_idle_at(now, idle_for)` so it does not depend on ageing a monotonic clock. **The 300s sweep is a function, and tested.** It was an inline `tokio::spawn` block: the expiry edit (the only part that talks to Telegram) had no test at all. It is now `periodic_sweep(sender, chat_store, link_cache, task_queue, config, stop)`, which also prunes the inline entries, driven in a test with `start_paused` — the loop's own timer fires the tick, exactly one expired prompt is rewritten in place, a live one keeps its record and buttons. The interval is pinned as a constant because no assertion on the edits can see it (a shorter one produces the same single edit; the paused clock can jump past the boundary while a tick's DB work is in flight). To make the edit reachable at all, `edit_message_text` joined the `MediaSender` trait (Bot impl + mock recording), which is also what keeps `main.rs`'s remaining `Bot` calls unambiguous. `main.rs` leaves the "untested modules" list except for startup/shutdown and the dispatcher tree. **The bot-wide send budget exists.** Telegram throttles a bot in total (~30 msg/s) as well as per chat; only the per-chat bucket existed, so a batch forward fanned out over many chats was unguarded and earned 429s the queue then retried. `acquire_global` charges the same spend against a single shared bucket at the three paced sites (`send_media_group`, `send_animation`, `copy_messages`). The unpaced ones (`send_message`, the edits, the toasts) stay unpaced on purpose: they are one call per action, far below the ceiling, and pacing a user-visible reply would delay it. Not covered: that the send paths call it (they need a real `Bot`), which is the same structural gap as the dispatcher tree. Also: the startup token-exchange decision is now `startup_validation(result)` instead of living inside the `Site::validate` future, so "a 5xx while the container comes up must not disable pixiv" is asserted as a decision — the message the admin gets plus `enabled()` unchanged. The rejected-credential half is deliberately not exercised: it calls `disable()`, a process-wide flag with no reset, and a test touching it would order-couple every other pixiv test. Verified: `cargo fmt`, `cargo clippy --workspace --all-targets --locked -- -D warnings`, `cargo test --workspace --locked` (184 passed, 14 ignored) — plus mutations, each confirmed to fail the relevant test: the sweep not being driven on its timer, the interval shortened to 60s, and (earlier) the queue sweep's missing wake-up. Dropped an empty leftover `crates/x-media/tests/` directory while there (never tracked by git).