- Ported advanced error detection (`is_auth_error`, `is_transient_error`, `is_network_error`) from the daemon script to accurately classify exceptions.
- Implemented `get_rate_limit_wait_seconds` to intelligently parse HTTP rate-limit headers (`retry-after`, `ratelimit-reset`) instead of relying on static backoffs.
- Increased default login retry attempts to 5, with jitter and bounded delays.
- Updated `login_with_backoff` to fail fast on invalid credentials while gracefully handling transient network issues and rate limits.
- New standalone script `bsky_post.py` to post text + optional image
or video to a Bluesky PDS instance. Supports --image, --video (mutually
exclusive), --alt, --lang, --service, --username, --password flags.
Reuses the same login-with-backoff pattern as rss2bsky and the Twitter bot.
- New Jenkinsfile that triggers every Thursday at 07:15 (cron: 15 7 * * 4),
reads credentials from Jenkins secret text entries, and runs bsky_post.py
with the configured image and Catalan language tag.
- Changed `post_lang: str` to `post_langs: List[str]` in `run_once`
and `try_send_post_with_variants` to support multiple BCP-47 language
tags passed to Bluesky's `langs=` field.
- Updated `--lang` CLI argument to accept comma-separated language codes
(e.g. `--lang ca,es`), parsed into a list at startup. Defaults to `["ca"]`.
- Added startup log line reporting the configured language(s) per run.
- Add `page.remove_listener("response", handle_response)` in
`extract_video_url_from_tweet_page` finally block to detach the
network listener before page close, preventing ghost callbacks
from leaking captured URLs across tweet iterations.
- Confirmed `build_media_fingerprint` already uses `canonicalize_tweet_url`
as stable video identifier instead of unreliable `media_url_https`,
avoiding false fingerprint collisions between different video tweets.
- Confirmed `sync_feeds` already guards against `None` video URL after
extraction, ensuring no silent fallthrough to stale captures.
- truncate_text_safely: slice to full max_length, drop trailing ...
- truncate_text_preserving_tail: remove ... prefix/infix, adjust
reserve from +4 to +1
- build_dynamic_alt: slice to DYNAMIC_ALT_MAX_LENGTH, drop ...
- make_rich: remove … from hashtag rstrip chars
- sync_feeds: remove conditional ... suffix in dry-run log