Kick: most of what chat says renders as words, because 7TV and BTTV are unknown #195

Closed
opened 2026-09-11 22:26:03 -04:00 by Salastil · 2 comments
Owner

nobilis/src/backend/kick/emotes.rs knows exactly one source of emotes:
https://files.kick.com/emotes/... (:81). There is no 7TV, no BTTV and no
FrankerFaceZ.

On Kick that is most of the emotes actually in use. Channels install a 7TV set and the
chat then talks in it, so a busy channel reads here as a column of bare words where
every other viewer sees pictures - and the words are not even the right words, since a
7TV emote's name is chosen to be typed, not read.

Of everything left on Kick this is the one that changes what the client looks like in
ordinary use, rather than adding something at the edges. The lookup is per-channel and
cacheable, and the existing emote pipeline already turns a token into a picture.

`nobilis/src/backend/kick/emotes.rs` knows exactly one source of emotes: `https://files.kick.com/emotes/...` (`:81`). There is no 7TV, no BTTV and no FrankerFaceZ. On Kick that is most of the emotes actually in use. Channels install a 7TV set and the chat then talks in it, so a busy channel reads here as a column of bare words where every other viewer sees pictures - and the words are not even the right words, since a 7TV emote's name is chosen to be typed, not read. Of everything left on Kick this is the one that changes what the client looks like in ordinary use, rather than adding something at the edges. The lookup is per-channel and cacheable, and the existing emote pipeline already turns a token into a picture.
Salastil added the feature label 2026-09-11 22:26:03 -04:00
Author
Owner

Finished — https://git.salastil.com/Salastil/nobilis/commit/5d408bd, https://git.salastil.com/Salastil/moho/commit/b636c33

The issue was wrong to name three providers. Checked rather than assumed:
api.betterttv.net/3/cached/users/kick/<id> answers 404 where the same path with
twitch answers 200, and FrankerFaceZ has no Kick route at all. Both are Twitch
products that never followed Kick. This is 7TV only.

The part that would have failed silently: 7TV keys its sets by Kick's user id,
not the channel id, and those are different numbers — xqc is user 676 and channel
668, and asking with 668 answers 404 rather than an empty set. moho stored only the
channel id, so user_id had to be captured first. Without that this would have
shipped looking like a client where no channel has any emotes. Verified live: 676
returns 965 emotes, 668 returns 404.

Global set first, the channel's own on top, so a channel overriding a global name
gets its own picture. Best-effort and quiet — most channels have no 7TV set and
answer 404, which is ordinary rather than a failure.

Substitution is whole-word and case-sensitive, because 7TV names collide with
ordinary English constantly (a channel with an emote called "ok" would otherwise turn
half the conversation into pictures). Whitespace is the boundary rather than \b,
since names routinely contain digits and underscores. Lookups go through
hasOwnProperty.call — without it a message containing "toString" or "constructor"
would find a function on the prototype and put it in the page. All of that checked:
GAMBAx and xGAMBA untouched, gamba is not GAMBA, adjacent emotes both draw.

554 tests green. Note #205, which will fold these into the picker.

Finished — https://git.salastil.com/Salastil/nobilis/commit/5d408bd, https://git.salastil.com/Salastil/moho/commit/b636c33 **The issue was wrong to name three providers.** Checked rather than assumed: `api.betterttv.net/3/cached/users/kick/<id>` answers 404 where the same path with `twitch` answers 200, and FrankerFaceZ has no Kick route at all. Both are Twitch products that never followed Kick. This is 7TV only. **The part that would have failed silently:** 7TV keys its sets by Kick's *user* id, not the channel id, and those are different numbers — xqc is user 676 and channel 668, and asking with 668 answers 404 rather than an empty set. moho stored only the channel id, so `user_id` had to be captured first. Without that this would have shipped looking like a client where no channel has any emotes. Verified live: 676 returns 965 emotes, 668 returns 404. Global set first, the channel's own on top, so a channel overriding a global name gets its own picture. Best-effort and quiet — most channels have no 7TV set and answer 404, which is ordinary rather than a failure. Substitution is whole-word and case-sensitive, because 7TV names collide with ordinary English constantly (a channel with an emote called "ok" would otherwise turn half the conversation into pictures). Whitespace is the boundary rather than `\b`, since names routinely contain digits and underscores. Lookups go through `hasOwnProperty.call` — without it a message containing "toString" or "constructor" would find a function on the prototype and put it in the page. All of that checked: `GAMBAx` and `xGAMBA` untouched, `gamba` is not `GAMBA`, adjacent emotes both draw. 554 tests green. Note #205, which will fold these into the picker.
Author
Owner

Reverted — https://git.salastil.com/Salastil/nobilis/commit/7e15621, https://git.salastil.com/Salastil/moho/commit/5819345

7TV on Kick is a browser extension, not a Kick feature. Kick's own chat shows the
bare word, so this was drawing pictures where every viewer without that extension sees
text, and offering those words in the picker where sending one posts something most of
the channel reads as a word.

That is worse than not having it. A client that renders more than the service does is
not being generous — it is showing a different conversation from the one everybody else
is in, and only this client's reader is confused about which.

Removed: the module, the per-channel fetch, the word map, the kickWordEmotes event,
the formatter substitution, and user_id on the Kick channel, which was captured only
because 7TV keyed on it. Kick's own [emote:id:name] is untouched — those render in
Kick and were never in question.

Closing as won't implement rather than reopening: the feature worked, it just should
not exist.

Reverted — https://git.salastil.com/Salastil/nobilis/commit/7e15621, https://git.salastil.com/Salastil/moho/commit/5819345 7TV on Kick is a **browser extension, not a Kick feature**. Kick's own chat shows the bare word, so this was drawing pictures where every viewer without that extension sees text, and offering those words in the picker where sending one posts something most of the channel reads as a word. That is worse than not having it. A client that renders more than the service does is not being generous — it is showing a different conversation from the one everybody else is in, and only this client's reader is confused about which. Removed: the module, the per-channel fetch, the word map, the `kickWordEmotes` event, the formatter substitution, and `user_id` on the Kick channel, which was captured only because 7TV keyed on it. Kick's own `[emote:id:name]` is untouched — those render in Kick and were never in question. Closing as won't implement rather than reopening: the feature worked, it just should not exist.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Salastil/moho#195