feat(news): auto-push published news to the in-game Town Cryer News gump (2.1)

Phase 4: sync the site's published news posts into the Protocol 2.1 News gump.

- uoLinkClient.postNews / deleteNews.
- utils/newsGump.js — a STATE SYNC (not a one-shot announce leg): an article
  stays in the gump while its post is published news and is pulled when it leaves
  that state. buildArticle renders a compact gump-HTML block (centred title +
  plain-text excerpt — the gump supports only a small HTML subset) with a
  "more info" link to /site/news and an optional gump image from the
  `news_gump_image` setting. Every call is best-effort / never-throws.
- Hooked into the posts pipeline alongside the existing announce enqueue:
  syncPost on create/update/publish (fresh publish announces; edits refresh
  silently; leaving published-news pulls the article), removePost on delete.
- reassertAll() runs in uoLinkSocket.backfill on every WS (re)connect —
  reconciles the gump to our source of truth and recovers any article whose
  original live push failed (silent, so a reconnect never re-proclaims old news).

Server 185/185, swagger regenerated. Refs .plans/protocol2-integration.md (Phase 4).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 15:40:47 -05:00
parent 2957708bab
commit 55a3adea99
5 changed files with 218 additions and 0 deletions

View File

@@ -17,6 +17,7 @@ const uoLinkConfig = require('../model/uoLinkConfig/uoLinkConfig.model')
const uoLinkClient = require('./uoLinkClient')
const shardIngest = require('./shardIngest')
const shardState = require('../model/shardState/shardState.model')
const newsGump = require('./newsGump')
const log = require('./logger')('uo-link-socket')
const BACKOFF_MIN_MS = 1000
@@ -102,6 +103,12 @@ async function backfill() {
await shardState.setPresence(presence.data)
log.info('snapshotted online population from /online', { count: presence.data.count })
}
// Re-assert our published news into the in-game Town Cryer News gump. The
// website is the source of truth; this reconciles the gump on every
// (re)connect (and recovers any article whose original live push failed).
// Silent (announce:false) so a reconnect never re-proclaims old news.
await newsGump.reassertAll()
} catch (err) {
log.warn('backfill failed (continuing on live feed)', { message: err.message })
}