* Surface retry-after seconds for plain rate-limit failures
libsignal-service's RateLimitException exposes retryAfterMilliseconds
for HTTP 413 responses, but signal-cli only forwarded retry-after for
ProofRequired (428) failures. Clients had no signal for when it was
safe to retry plain rate-limited sends, so every failed retry
potentially extended the server-side window.
SendMessageResult now carries an optional rateLimitRetryAfterSeconds,
populated from the upstream Optional<Long>. JsonSendMessageResult
exposes it for RATE_LIMIT_FAILURE type. Text output includes the
window when known. Aggregate RateLimitErrorException now carries the
real nextAttemptTimestamp (was hardcoded to 0).
Closes#1996.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address review: include proof-required retry-after and ceiling-round millis
Codex adversarial review flagged two issues in the phase 1 retry-after
plumbing:
* Aggregate retry-after ignored proof-required failures. Because
isRateLimitFailure is true for proof-required cases but
rateLimitRetryAfterSeconds was only populated from plain 413s, an
all-proof-required batch (or a mixed batch where the proof-required
delay was longer) could flow into outputResult() and produce a
RateLimitException(0), telling callers to retry immediately.
* Millisecond Retry-After values were truncated by integer division,
so 1..999ms became 0 and non-second-aligned values lost up to 999ms.
A retry suggested from the floored value can land before the
server's real deadline and re-trigger the limit.
SendMessageResult.from(...) now populates rateLimitRetryAfterSeconds
from either the proof-required seconds or the plain rate-limit ms
(converted via ceiling division), giving maxRateLimitRetryAfterSeconds
a single source of truth. JsonSendMessageResult.from(...) reads the
unified field. New millisToCeilingSeconds helper plus boundary test.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Preserve source compat and document retry-after field change
Add a non-canonical 8-arg SendMessageResult constructor that delegates
to the canonical form with null retry-after. This keeps source
compatibility for any downstream code that constructs the record
directly (tests, mocks) without changing the canonical shape. Records
permit additional constructors alongside the canonical one.
Document the retryAfterSeconds meaning change in the CHANGELOG. The
field was previously populated only for proof-required failures; it
is now populated whenever the server sends a Retry-After header. The
canonical proof-required discriminator is still token != null.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement two new CLI flags to disable downloading avatars and sticker
packs during message reception, following the existing pattern of
--ignore-attachments and --ignore-stories flags.
Changes:
- Add --ignore-avatars and --ignore-stickers flags to ReceiveCommand,
DaemonCommand, and JsonRpcDispatcherCommand
- Extend ReceiveConfig record with ignoreAvatars and ignoreStickers
fields
- Pass ignoreAvatars as explicit boolean parameter to ProfileHelper,
SyncHelper, and GroupHelper methods (per maintainer feedback)
- Gate avatar downloads in ProfileHelper (profile avatars), SyncHelper
(contact avatars), and GroupHelper (group avatars for V1 and V2)
- Gate sticker pack downloads in IncomingMessageHandler for both
direct sticker messages and sync sticker pack operations
- Update handleSignalServiceDataMessage and handleSyncMessage to pass
full ReceiveConfig instead of individual boolean flags
- Update man page (signal-cli.1.adoc) with flag documentation
- Add entries to CHANGELOG.md
When these flags are set, the respective content is not downloaded
during message reception. Metadata (avatar paths, sticker pack IDs)
is still stored, and existing FileNotFoundException handling will
surface if content is later requested but wasn't downloaded.
Fixes#1903Closes#1904