diff --git a/rss2bsky.py b/rss2bsky.py index c6e0b68..2b4b849 100644 --- a/rss2bsky.py +++ b/rss2bsky.py @@ -37,7 +37,7 @@ DEFAULT_COOLDOWN_STATE_PATH = "rss2bsky_cooldowns.json" @dataclass(frozen=True) class LimitsConfig: dedupe_bsky_limit: int = 30 - bsky_text_max_length: int = 275 + bsky_text_max_length: int = 300 external_thumb_max_bytes: int = 750 * 1024 external_thumb_target_bytes: int = 500 * 1024 @@ -402,7 +402,7 @@ def process_title(title: str) -> str: return title_text -def build_post_text_variants(title_text: str, link: str): +def build_post_text_variants(title_text: str, link: str, max_length: int = 300): title_text = clean_whitespace(title_text) link = canonicalize_url(link) or link or "" @@ -415,16 +415,34 @@ def build_post_text_variants(title_text: str, link: str): seen.add(cleaned) variants.append(cleaned) + # Variant 1: títol + link (si cap sencer) if title_text and link: - add_variant(f"{title_text}\n\n{link}") + full = f"{title_text}\n\n{link}" + if len(full) <= max_length: + add_variant(full) + else: + # Trunca el títol per fer-hi lloc al link + # Reserva espai per "\n\n" + link + reserve = len(link) + 2 + available = max_length - reserve + if available > 20: + truncated_title = title_text[:available - 3].rstrip() + "..." + add_variant(f"{truncated_title}\n\n{link}") + + # Variant 2: només títol (truncat si cal) if title_text: - add_variant(title_text) + if len(title_text) <= max_length: + add_variant(title_text) + else: + truncated = title_text[:max_length - 3].rstrip() + "..." + add_variant(truncated) + + # Variant 3: només link (si no hi ha títol) if link and not title_text: add_variant(link) return variants - def is_x_or_twitter_domain(url: str) -> bool: try: hostname = (urlparse(url).hostname or "").lower() @@ -800,7 +818,7 @@ def try_send_post_with_variants(client: Client, text_variants: List[str], embed, reset_str = format_cooldown_until(get_global_post_cooldown_until(cooldown_path)) raise RuntimeError(f"Posting skipped because global post cooldown is active until {reset_str}") - logging.info(f"📝 Trying post text variant {idx}/{len(text_variants)} (length={len(variant)})") + logging.info(f"📝 Trying post text variant {idx}/{len(text_variants)} "f"(length={len(variant)} chars)") rich_text = make_rich(variant) result = client.send_post(text=rich_text, embed=embed, langs=[post_lang]) return result, variant @@ -1019,7 +1037,7 @@ def fetch_feed_content(feed_url: str, http_client: httpx.Client, cfg: AppConfig) return response.content.decode("utf-8", errors="ignore") -def build_candidates_from_feed(feed) -> List[EntryCandidate]: +def build_candidates_from_feed(feed, max_length: int = 300) -> List[EntryCandidate]: candidates: List[EntryCandidate] = [] for item in getattr(feed, "entries", []): @@ -1043,7 +1061,7 @@ def build_candidates_from_feed(feed) -> List[EntryCandidate]: published_at=published_at.isoformat() if published_at else None, published_arrow=published_at, entry_fingerprint=entry_fingerprint, - post_text_variants=build_post_text_variants(title_text, link), + post_text_variants=build_post_text_variants(title_text, link, max_length=max_length), )) except Exception as e: @@ -1168,7 +1186,7 @@ def run_once( with httpx.Client() as http_client: feed_content = fetch_feed_content(rss_feed, http_client, cfg) feed = fastfeedparser.parse(feed_content) - candidates = build_candidates_from_feed(feed) + candidates = build_candidates_from_feed(feed, max_length=cfg.limits.bsky_text_max_length) logging.info(f"📰 Prepared {len(candidates)} feed entry candidates for duplicate comparison.")