Weekly discordbot-refresh timer output, committed from the server.
Adds Nagusa (Swimsuit); nothing removed.
Co-Authored-By: Claude Opus 5 <[email protected]>
Steam's GetNumberOfCurrentPlayers occasionally answers 200 with
player_count: 0 for a valid appid. Both Strinova stat channels were
renamed to "0" on the same 09:48 tick on 2026-10-20 (the only zero in
~300 renames over 3 days), and the name sat wrong until the next
10-minute tick.
Skip the rename on a non-positive count so the channel keeps its last
good value, and log it so the next one is visible. !strinova had the
same bug and would reply "0 players online right now".
Co-Authored-By: Claude Opus 5 <[email protected]>
RSSHub's guid is the timeline entry's own id (a retweet's id), so ids follow
the order the account posted, while feed order and pubDate follow the
original tweet. A fresh retweet of an old post can sort to the bottom; it is
now posted, and only ids older than every known one are treated as deletion
slide-ins. !leaks sorts by id too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The RSSHub feed isn't strictly chronological, so 'post everything above the
last guid' could skip tweets. Now a single poller fans out to both webhooks,
each remembering posted guids (data/twitter_seen.json, atomic writes): new
tweets anywhere above the oldest known one are posted oldest-first, old tweets
sliding in after a deletion are ignored, failed posts retry next poll and
Discord 429s are honoured. Links go out as fxtwitter.com for real embeds.
The owner gets a DM after 90 min of broken feed and again on recovery.
Night polling is now 1-2 h.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The feed is fetched with a real X account's cookie, so avoid a fixed
24/7 cadence: random 4-8 min by day, 20-35 min 01:00-06:00 Istanbul.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Both forwarders (main + mao server) read TWITTER_RSS_URL, now pointed at
RSSHub on yuuka. Guard on_ready so reconnects don't start duplicate pollers.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
fixes that were dropping messages:
- d->tg sent parse_mode=Markdown with an unescaped body, so any message
with a stray * _ ` or [ got a 400 from telegram and vanished. now html
with proper escaping + a plain-text retry
- no allowed_mentions on webhook posts, so telegram could @everyone
- forwarder.start() ran from on_ready, which fires again on every gateway
reconnect -> second poller on the same token -> 409s and dupes
- document.mime_type is nullable and was called directly
- sticker finally block hit UnboundLocalError when mkstemp raised
- ffmpeg timeouts leaked temp files and orphaned the process
stickers, per format instead of one blanket gif convert:
- static .webp now passes through (the gif convert crushed 228 alpha
levels to 2 and made the file 4x bigger)
- video .webm -> animated webp, keeps 8-bit alpha, ~7x smaller than gif.
needs -c:v libvpx-vp9 before -i or ffmpeg silently drops the alpha
- .tgs is gzipped lottie, ffmpeg exits 183 on it and we were uploading
the raw file. renders via optional lottie lib, else thumbnail + emoji
added: telegram profile photos as webhook avatars (re-hosted in a private
channel, AVATAR_CACHE_CHANNEL_ID), reply threading both ways, edit sync
both ways, delete sync d->tg, album batching, voice/poll/location/contact/
dice/forward attribution, utf-16-safe entity conversion, mention and custom
emoji resolution, message splitting instead of truncation, shared aiohttp
session with 429 backoff, route discovery from env, !bridge status
main.py just loads the extension now; routes come from credentials.env
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RXrxDFpyq3VjvxWGuW3CZe