Skip to content

fix(mod-chat): show direct post URLs and live reaction counts#208

Merged
ohld merged 1 commit intoproductionfrom
fix/mod-chat-stat-live-counts-and-post-urls
Apr 28, 2026
Merged

fix(mod-chat): show direct post URLs and live reaction counts#208
ohld merged 1 commit intoproductionfrom
fix/mod-chat-stat-live-counts-and-post-urls

Conversation

@ohld
Copy link
Copy Markdown
Member

@ohld ohld commented Apr 28, 2026

Why

Moderator forwarded meme #10133049 to the mod chat. The bot replied:

👁 0 · 👍 0 · 👎 0
src: https://t.me/matroskina_meme

Both halves were wrong:

  1. Source link was channel-only, not the post. The current query did
    LEFT JOIN meme_raw_telegram ON mrt.post_id = m.raw_meme_id, but
    meme.raw_meme_id is the meme_raw_telegram.id (autoincrement PK),
    not the Telegram post_id. The join silently failed, so we fell
    back to meme_source.url (the channel root).

  2. Counts read from meme_stats, which is on a 15-min cron. Memes
    served right before the forward have no row yet → COALESCE(..., 0)
    reports 0 views / 0 likes / 0 dislikes even though the user clearly
    received the meme.

What changed

src/tgbot/handlers/chat/mod_chat_stat.py:

  • Fix the join: mrt.id = m.raw_meme_id (gated by msrc.type='telegram').
  • Add a meme_raw_vk join so VK memes get their direct post URL too.
  • Build the TG post URL from msrc.url || '/' || mrt.post_id. The stored
    mrt.url has the /s/ preview prefix (https://t.me/s/channel/12345),
    which is the embedded-channel-view variant — not what you want when
    clicking through to "this exact post".
  • Aggregate views/likes/dislikes live from user_meme_reaction so the
    reply reflects reality immediately, no cron lag.

sec_to_react still comes from meme_stats (median over a window —
not worth recomputing inline).

Test plan

  • After deploy, forward a recently-served meme to the mod chat and
    confirm the reply shows non-zero views and a https://t.me/<channel>/<post_id>
    link that opens the exact post.
  • Forward an older popular meme and confirm counts match what
    meme_stats would have reported (within rounding).
  • Forward a VK-sourced meme (if any are still active) and confirm
    src: is the ?w=wall... post link, not the community root.

The mod-chat reply was wrong on two axes:

1. JOIN bug: `mrt.post_id = m.raw_meme_id` never matched because
   `meme.raw_meme_id` references `meme_raw_telegram.id` (PK), not
   `post_id` (TG message id). The query silently fell back to the
   channel-only `meme_source.url` (e.g. `https://t.me/matroskina_meme`).

2. Stale counts: views/likes/dislikes came from `meme_stats`, which is
   refreshed by a 15-min cron. Memes served right before the moderator
   forwarded them showed 0/0/0.

Fix:
- JOIN on `mrt.id = m.raw_meme_id` (also gated by `msrc.type='telegram'`).
- Add `meme_raw_vk` join so VK memes get their direct post URL.
- Build the TG post link from `msrc.url || '/' || mrt.post_id`
  (the stored `mrt.url` has a `/s/` preview prefix — not what we want
  for a "click to see the original post" link).
- Aggregate views/likes/dislikes live from `user_meme_reaction` so the
  reply reflects reality immediately, not whatever the cron last saw.
@ohld
Copy link
Copy Markdown
Member Author

ohld commented Apr 28, 2026

STAFF ENGINEER REVIEW: APPROVED

Diff verified against schema:

  • ✅ JOIN fix is correct: meme.raw_meme_id is the autoincrement id of meme_raw_telegram (Identity PK), not the Telegram post_id. The old query silently produced an empty join almost every time. New mrt.id = m.raw_meme_id is right.
  • ✅ VK join mirrors the same pattern; meme_raw_vk.url is the canonical post URL (vk.com/<comm>?w=wall<from>_<id>).
  • ✅ TG canonical URL via msrc.url || '/' || mrt.post_id is right — mrt.url has the /s/ channel-preview prefix that the parser injects.
  • ✅ Live counts: reaction_id=1/=2 matches the convention used everywhere in src/stats/ and src/recommendations/. COUNT(*) over user_meme_reaction is "memes_sent" semantically (rows are inserted at send time, then updated on reaction).
  • ✅ All params are bind-style; no SQL injection.
  • ✅ Codex /codex review concurs: no P1 blockers.

Two P2 notes (non-blocking — admin-only handler, fires a few times per day):

  1. msrc.url trailing-slash collision. If a moderator pasted https://t.me/foo/, the result becomes https://t.me/foo//123. TG redirects, but it's ugly. Fix is one-line: wrap in rtrim(msrc.url, '/') or sanitize at insert time.

  2. No meme_id-only index on user_meme_reaction. Composite PK (user_id, meme_id) is leading on user_id, so WHERE meme_id = :mid falls to a parallel seq scan. With ~tens-of-millions of rows this could climb to multi-second latency. Acceptable for a low-frequency admin reply, but worth a follow-up CREATE INDEX CONCURRENTLY if this handler ever fires from a hotter path.

Neither blocks landing. Squash-merging via --auto.

@ohld ohld merged commit 628406d into production Apr 28, 2026
3 checks passed
@ohld
Copy link
Copy Markdown
Member Author

ohld commented Apr 28, 2026

✅ Approved + squash-merged. CI was already green when --auto ran.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant