For chats where we sent an opening message but the recipient never
accepted, /sync drops the room with no heroes and the recipient is
in state as m.room.member with membership:'invite' — not 'join'.
chat.reddit.com's web UI labels these rooms with the invitee anyway,
because it reads the full room state.
- roomToActiveChat now accepts 'invite' membership in addition to
'join' when scanning member-join state events.
- enrichUsernames pass 2 hits /_matrix/client/v3/rooms/{id}/state
first (cheap, returns all member events including invites) and
only falls back to /messages history if /state yields nothing.
- Added MatrixClient.getRoomFullState to back that lookup.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
api.reddit.com/api/user_data_by_account_ids returns 404 without an
OAuth token — which is why every chat in the dashboard sat on
t2_xxx or unknown. The same path on www.reddit.com (with a .json
suffix) is served unauthenticated and returns the expected user
map. Switch the resolver to that host so enrichUsernames actually
succeeds.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reddit's homeserver does not expose /_matrix/client/v3/rooms/{id}/joined_members
(it returns 'page not found'), so the previous fallback was always a 404.
Two replacements:
- roomToActiveChat: include timeline event senders as candidate MXIDs
alongside heroes / member-join state. /sync omits heroes for idle
rooms but always carries full sender MXIDs in any timeline events
it does return, so we recover a t2 ID whenever the delta sees a
message.
- enrichUsernames: split into three passes — apply known mappings
first (so re-created delta chats get upgraded without a restart),
then page /messages 'b' once per still-unknown room to mine a t2
from the recipient's history, then resolve any remaining raw t2s
via the bulk user_data endpoint. Failed history lookups are cached
in unresolvableRooms so we don't retry every sync.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reddit's homeserver returns 404 from /_matrix/client/v3/rooms/{id}/joined_members
for rooms we've left, been kicked from, or never fully joined — which is
roughly every chat that wasn't accepted by the recipient. The previous
enrichUsernames pass retried these on every delta sync, flooding the
dashboard log with debug-level 404s. Track failures in an in-memory
unresolvable set so each room is tried at most once per process.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reddit's homeserver sometimes returns a room in /sync with no
m.heroes and no joined m.room.member state events, leaving us with
no t2 ID to resolve and the chat stuck on 'unknown'. enrichUsernames
now hits /_matrix/client/v3/rooms/{roomId}/joined_members for any
chat whose username is 'unknown' or a bare MXID, stamps the t2 ID,
and lets the existing resolver fill in the real Reddit username.
Also treats a cached 'unknown' username as stale in roomToActiveChat
so subsequent deltas re-attempt the lookup instead of pinning it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
enrichUsernames() was only invoked once at startup, so any rooms that
arrived via a later delta /sync stayed pinned to their raw t2_xxx
identifier — which the dashboard then rendered as a bare Matrix-style
ID. Call enrichUsernames() at the tail of syncOnce so new rooms get
resolved to real Reddit usernames before the UI reads them.
Also pass the unformatted username to openConversation() and format
the modal title in the frontend, so lookups by username keep working
after the display sanitisation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a durable mapping so the UI never has to show raw t2_xxx IDs or
MXIDs just because the in-memory cache is cold after a restart.
ChatService seeds from the table on construction and writes through
every resolution path; cached chats whose username is still t2_xxx
get upgraded from the DB on loadCache.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Threads MessagesRepository through ChatService so first outgoing chat
messages are persisted for stats, and extends the repo + DB client to
support chat-room-keyed lookups. Adds tests covering the new behavior.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Blur NSFW media gallery images until hover/reveal
- Surface chat sync progress in the dashboard header
- Extend chat service, cache, and monitor with delta-media handling
- Add tests for chat cooldown, delta media, and monitor flows
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Split monolithic chat service into modular structure with Matrix client
support, extract web server into per-resource route modules, introduce
bootstrap/main-loop separation, and add comprehensive vitest coverage
across services, repositories, and utilities. Adds project docs and
CLAUDE.md agent instructions.