Commit Graph
479 Commits
Author SHA1 Message Date
YoursFunny 3a944c1361 fix(queue): keep a running attempt through a failed lease heartbeat
A heartbeat write error abandoned the attempt at once. The lease is still
valid until locked_until, so the attempt was dropped for nothing: the row
was recovered later and the task re-ran from the start, although its side
effects (a sent album, a copied post) may already have happened. A failed
beat now retries at the next tick; the attempt is abandoned only once the
next beat could land at or past the last confirmed expiry.
2026-10-14 22:10:35 +08:00
YoursFunny 7fbe144b57 fix(ci): gate docker publish on the offline test suite
docker.yml pushed the image for tags and master without waiting for the
test job, so a red commit could still be published. ci.yml is now also a
workflow_call target; docker depends on its result. The gate run has its
own concurrency group and never cancels a superseded run, so a docs-only
follow-up cannot drop the image of the code commit before it. The live
job is skipped for the gate call.
2026-10-14 22:10:35 +08:00
YoursFunny a7d4d1af8c fix: derive the photo decode cap so one photo's peak fits the memory budget 2026-10-14 22:10:35 +08:00
YoursFunny 51f451d78f fix: default compose WEBHOOK to false and leave RUST_LOG unset 2026-10-14 22:10:35 +08:00
YoursFunny c937a66e5f fix: admit adapter temp media in the local-path guard 2026-10-14 22:10:34 +08:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> b42d41eaa4 build(deps): bump the cargo-patch group across 1 directory with 2 updates (#5)
Bumps the cargo-patch group with 2 updates in the / directory: [thiserror](https://github.com/dtolnay/thiserror) and [rand](https://github.com/rust-random/rand).


Updates `thiserror` from 2.0.20 to 2.0.21
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/2.0.20...2.0.21)

Updates `rand` from 0.10.2 to 0.10.3
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/compare/0.10.2...0.10.3)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-version: 2.0.21
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-patch
- dependency-name: rand
  dependency-version: 0.10.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-05 18:57:46 +08:00
YoursFunny 8063777962 chore: bump version to 1.9.2 v1.9.2 2026-09-25 03:12:07 +08:00
YoursFunny ac10b8715c docs: sync template limits and live-test count 2026-09-25 02:12:17 +08:00
YoursFunny 517e1362a5 docs: document template storage limits 2026-09-25 01:49:31 +08:00
YoursFunny 50299bacbb fix: redact user-controlled log fields 2026-09-25 01:49:31 +08:00
YoursFunny 9d40f3bc50 fix: bound template storage and callback data 2026-09-25 01:49:31 +08:00
YoursFunny 2045c3cea1 fix: keep photo memory permit until processing ends 2026-09-25 01:49:30 +08:00
YoursFunny 09656810fe fix: prune persisted expired prompts 2026-09-25 01:49:30 +08:00
YoursFunny 786bafca42 fix: account for photo processing peak memory 2026-09-25 01:49:30 +08:00
YoursFunny 18da6e97c2 fix: reject oversized local media before upload 2026-09-25 01:49:29 +08:00
YoursFunny 7b816d4153 fix: split long message forwards 2026-09-25 01:49:29 +08:00
YoursFunny d8b9a06453 fix: bound direct command fetches 2026-09-25 01:40:09 +08:00
YoursFunny 4e0f1b834e fix: restrict media download hosts 2026-09-25 01:40:09 +08:00
YoursFunny 21437d1843 fix: bound queue panic retries 2026-09-25 01:11:28 +08:00
YoursFunny a54029c19b fix: propagate startup repair database errors 2026-09-25 01:06:09 +08:00
YoursFunny c6120b7cc0 fix: make sqlite migrations atomic 2026-09-24 21:47:48 +08:00
YoursFunny 7b4e273533 fix: bound site JSON response bodies 2026-09-24 21:25:03 +08:00
YoursFunny 6a87583cc0 fix: honor bsky HLS retry-after 2026-09-24 21:01:36 +08:00
YoursFunny 1ba64e858d fix: enforce inline result limit centrally 2026-09-24 21:00:00 +08:00
YoursFunny 008d31b9bd test: cover inline answer edge cases 2026-09-24 20:57:08 +08:00
YoursFunny 66ec14662c style: simplify inline answer returns 2026-09-24 20:57:08 +08:00
YoursFunny 8ae7d9947d fix: honor media retry-after delay 2026-09-24 20:57:08 +08:00
YoursFunny 28e8fec68d fix: rate limit plain chat operations 2026-09-24 20:57:08 +08:00
YoursFunny b2b01721b2 fix: debounce inline generations and cap results 2026-09-24 20:40:27 +08:00
YoursFunny 85c3be5ef5 fix: always answer inline query 2026-09-24 20:33:23 +08:00
YoursFunny 5e7a87c0dd fix: send text posts as HTML 2026-09-24 20:29:04 +08:00
YoursFunny 6a2ba6a50a fix: handle empty media and prompt persistence 2026-09-24 19:51:50 +08:00
YoursFunny fb354c9434 test: handle chat state Result in callback 2026-09-24 19:46:23 +08:00
YoursFunny bc7f249c20 fix: filter local animation cache sources 2026-09-24 19:44:48 +08:00
YoursFunny 403b84996f test: cover ugoira expansion budgets 2026-09-24 19:42:50 +08:00
YoursFunny f08b187964 fix: drop unsafe upstream media entries 2026-09-24 19:39:46 +08:00
YoursFunny 8428468712 fix: surface chat state read failures 2026-09-24 19:37:29 +08:00
YoursFunny 412359e84c fix: do not cache local media paths 2026-09-24 18:52:18 +08:00
YoursFunny 438ced278f fix: align CI tags and webhook healthcheck 2026-09-24 18:49:20 +08:00
YoursFunny 704b14f05a fix: bound ffmpeg encoding processes 2026-09-24 18:46:09 +08:00
YoursFunny 1f9da14772 fix: bound ugoira archive expansion 2026-09-24 18:44:21 +08:00
YoursFunny 4694e60ee3 fix: reject upstream local media paths 2026-09-24 18:41:23 +08:00
YoursFunny 88dc0ccb9e fix: stop persistent queue before URL workers 2026-09-24 18:37:17 +08:00
YoursFunny b563b949bb fix: avoid overwriting chat state after read failure 2026-09-24 18:35:52 +08:00
YoursFunny e7855b02fe fix: fence queue dead-letter side effects 2026-09-24 18:33:21 +08:00
YoursFunny 5e265f2413 chore(log): align levels with the documented convention
Three places disagreed with AGENTS' own level rules: get_chat/get_chat_administrators failures during /set_channel printed at error even though the usual cause is a mistyped channel name the user fixes on the spot (error is reserved for dead letters and the like — now warn, same as other user-served degradations); the edit-before-forward callback arrival line logged at info while the sibling arrival lines for commands and inline queries log at debug (now debug, still carries the escaped payload); and the two upload-fallback lines logged at info while photo.rs's parallel degradations log at warn (now warn both — Telegram failing to fetch our media is a degradation that leaves the user served). AGENTS' convention sentence is now explicit about all three instead of the ambiguous '(fallback, retry enqueue, dead-letter is error)'.
2026-09-24 15:38:25 +08:00
YoursFunny 5060b760ba fix(fetch): cap one fetch at a 900-second total budget
The per-request CLIENT timeout and the per-chunk DOWNLOAD_IDLE_TIMEOUT both reset on progress, so neither bounded the fetch as a whole: a URL that keeps dripping (a trickle the idle timeout reads as life) could hold one of the eight FETCH_SLOTS for effectively ever, and the queue behind the slots is what then stalls. The attempts loop now runs inside tokio::time::timeout(900 s) — generous for a genuinely large ugoira zip on a honest slow link (minutes), fatal for a drip — and answers Transient with the cache key, so the retry and its jittered backoff take over. The permit is taken outside the timeout: the slot is released on either path. Audit low finding 'url_workers long tail' (process-wide total-deadline option).
2026-09-24 15:38:25 +08:00
YoursFunny 7019f34801 fix(send): refuse a file id at the upload fallback's door
download_to_temp guarded against a file id, but prepare_upload_item answered a non-http media_url first — and item_url maps FileId to the id itself, so the id was presented as a local *path* and died later with a confusing file-open error instead of a classification. The guard now sits at the entry, download_to_temp takes the already-narrowed URL (the inner guard deleted as dead), and the test pins the refusal as Permanent before any I/O. The file also carries item10's download-classification arm (RateLimited is retryable there too) — same file, landed here to keep each tree compiling.
2026-09-24 15:35:55 +08:00
YoursFunny d05450d88a fix(fetch): jitter the retry backoff and honor a429's Retry-After
Two gaps in fetch_with_attempts: the sleep was the bare 1 << attempt, so every worker that failed together (a source coming back, a shared proxy blip) also recovered on the same tick and re-stamped the source; and a429 arrived as an anonymous Transient, its Retry-After header — the one place a source tells you exactly how long it wants silence — dropped on the floor.

retry_wait(attempt, roll, err) is the pure decision: doubling base plus a random slice of itself ([base, 2x base)), floored at a named Retry-After. status_error now takes the response, reads the seconds-form header and returns the new FetchError::RateLimited { site, retry_after_secs } (HTTP-date parses to None and stays transient); the pure table moved to classify_status so tests still build it from bare status codes. The delay is capped at MAX_RETRY_AFTER_SECS = 60 — the header is server-supplied and must not park one of the eight fetch slots.

Everywhere a Transient meant 'retryable' the new variant joins: the Site trait default, pixiv's override (plus its ugoira-zip mapping), bsky's HLS second attempt, the download classifier, and the user-facing message arm. Tests pin the429 rows (with and without the header, cap included) and retry_wait's math; AGENTS' retries bullet and variant list follow.
2026-09-24 15:35:55 +08:00
YoursFunny 053ae0ec25 fix(send): charge the photo download window to the memory budget
A non-photo body charged the process-wide budget for as long as download_to_temp held it; a photo charged nothing until its prepare step, so the download itself — up to MAX_PHOTO_DOWNLOAD_BYTES per item, six items per batch, eight prep slots process-wide — was memory the MEMORY_BUDGET comment promised was bounded but was not (~6x32 MiB on top of the accounted512 MiB). Photos now reserve their own download cap for that window; the prepare step still charges header probe + decode buffer, the only overlap that is actually held in memory together. Net accounting stays within the declared budget instead of exceeding it whenever a batch of large photos downloads at once (audit: upload.rs photo window uncharged).
2026-09-24 15:29:17 +08:00