[morethanwords/tweb/master](https://github.com/morethanwords/tweb/tree/master) • [79d6a8f](https://github.com/morethanwords/tweb/commit/79d6a8f952c8d4da31565ae805551b55cc3aa3ac) • _4 files, +276/-24_
Stop replaying dice in a chat you have already read

A group/channel message loaded without its dialog carries no `unread`
flag, so renderMessage falls back to the peer's read cursor. It asked
getReadMaxIdIfUnread for it — but that one answers 0 once the chat has
nothing unread, and `0 < mid` holds for every bubble. Every message of a
fully read chat therefore looked unread: dice and the other interactive
emoji rolled again on every chat open, sticker and message effects
re-played, and every bubble got a pointless read observer.

Extract the cursor itself as getInboxReadMaxId and read that from the
renderer. Own messages are excluded — the inbox cursor tracks incoming
ones and stays below them, so a dice we sent looked unread too. An
unknown cursor (no dialog loaded yet) keeps the old conservative answer:
the read observer is the only thing that marks a history as read, so it
must never be skipped on a guess.

getReadMaxIdIfUnread keeps its contract for the unread delimiter, the
jump target and the sponsored cursor; a 648-case matrix pins it to the
previous implementation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

[morethanwords/tweb/master](https://github.com/morethanwords/tweb/tree/master) • [a2662b6](https://github.com/morethanwords/tweb/commit/a2662b60daa8a8d16c831c0621e0034342f23c13) • _3 files, +249/-4_
Validate the URL an Instant View link decodes to

wrapUrl's `iv` branch pulled the target out of `[tg://iv?url=...`](tg://iv?url=...%60) and handed it
straight to the anchor href, skipping the protocol filter every other branch in
the same function applies. The branch is gated on `safe`, which
wrapTelegramRichText sets from `textUrl.webpage_id &#33;= 0` — so the href was only
ever as trustworthy as that one wire field, with no client-side backstop.

Run the decoded URL through matchUrlProtocol the same way the incoming url is
handled a few lines above. Real IV targets are absolute http/https and come out
unchanged; schemeless ones stop resolving relative to our own origin.

Also stop reading a stringified '0' webpage_id as a real webpage: that only
worked because fetchLong keeps longs numeric while they fit in the safe-integer
range, which is not something this call site should depend on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

[#webk](?q=%23webk)