Twitter rewrites every URL to a t.co address of a fixed 23 characters,
so counting the raw characters rejected messages Twitter would have
accepted: 100 characters of text plus a 50 character YouTube link
counted as 150 and was refused, where Twitter sees 123.
The punctuation trimmed off the end of a link keeps counting as ordinary
text. Only links written with a scheme are recognised; Twitter also
shortens bare hosts like example.com/foo, but detecting those without
mistaking Node.js for a link is not worth it here, and counting them in
full errs toward refusing rather than posting something too long.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deleting a message on the watched channel now deletes the posts it
produced. Making that possible reshapes how a message reaches an output:
- OutputInterface takes an output.Post and returns an output.Ref, the
identifier the platform gave the new post (tweet ID for X, URI and
CID for bluesky). It also gained Delete(Ref).
- The discord package no longer forwards raw MessageCreate values but
a discord.Event carrying the kind of change, so the MessageDelete
handler can use the same channel. Deleted messages arrive without an
author, so only the channel ID can be filtered on.
- The new store package remembers Discord message ID -> per output Ref.
Set POST_STORE to a path to keep that mapping across restarts;
without it the mapping is memory only and a restart makes older
messages undeletable. It holds the most recent 1000 messages.
main.go grows a distributor type to hold the outputs and the store
rather than threading them through the event loop.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>