Skip to content

Commit df650be

Browse files
committed
merge hc main
# Conflicts: # AGENTS.md # src/components/Hyperchat.svelte # src/components/MembershipItem.svelte # src/components/Message.svelte # src/components/PaidMessage.svelte # src/scripts/chat-injector.ts # src/scripts/chat-interceptor.ts # src/ts/chat-constants.ts # src/ts/chat-parser.ts # src/ts/messaging.ts # src/ts/storage.ts # src/ts/typings/ytc.d.ts
2 parents 403ae4b + 1313231 commit df650be

31 files changed

Lines changed: 1228 additions & 194 deletions

.eslintrc.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ module.exports = {
1818
'@typescript-eslint'
1919
],
2020
overrides: [
21+
{
22+
files: ['*.js', 'utils/*.js'],
23+
rules: {
24+
'@typescript-eslint/no-var-requires': 'off',
25+
}
26+
},
2127
{
2228
files: ['*.svelte'],
2329
processor: 'svelte3/svelte3',
@@ -29,8 +35,9 @@ module.exports = {
2935
],
3036
// Causes false positives with reactive and auto subscriptions
3137
'@typescript-eslint/strict-boolean-expressions': 'off',
38+
'@typescript-eslint/no-unsafe-argument': 'off',
3239
'no-sequences': 'off',
33-
'svelte/missing-declaration': "off",
40+
'svelte/missing-declaration': 'off',
3441
}
3542
}
3643
],

docs/YOUTUBE_ACTIONS.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# YouTube Actions (Dev Notes)
2+
3+
This repo implements YouTube "chat actions" (block, report, delete/retract, and future mod actions) by calling Innertube endpoints based on data from the message + its context menu.
4+
5+
This doc exists so we do not re-learn the same YouTube quirks every time.
6+
7+
## Rule 0: Copy The Real Request
8+
9+
If native YouTube can do it and HyperChat cannot, assume our request is missing a header, context field, or tracking param. Do not "guess until it works".
10+
11+
When in doubt, capture:
12+
13+
- One request flow in native UI (extension off)
14+
- The same flow in HyperChat (extension on)
15+
16+
Then diff the request bodies + headers and make HyperChat match.
17+
18+
## Request Inputs You Must Preserve
19+
20+
YouTube actions almost always depend on these fields. If you drop any of them, you get silent no-ops, missing menu items, or opaque errors.
21+
22+
- `context`: from `ytcfg` (`INNERTUBE_CONTEXT`)
23+
- API key: `INNERTUBE_API_KEY`
24+
- `clickTrackingParams`: from the UI element that spawned the action
25+
- Message-specific params: whatever YouTube gives you for that message/menu item (`params`, `trackingParams`, etc.)
26+
- Account identity: `x-goog-authuser` must match the active YouTube account (multi-login breaks without it)
27+
- Visitor identity: `x-goog-visitor-id`
28+
- Client identity: `x-youtube-client-name` and `x-youtube-client-version`
29+
- Delegated channel identity: `x-goog-pageid` from `DELEGATED_SESSION_ID` when present
30+
31+
If you are unsure where a value comes from, stop and find it in:
32+
33+
- `ytcfg` on the page (`ytcfg.get(...)`)
34+
- the context menu response tree
35+
- the message renderer tree that created the menu
36+
37+
## Auth: SAPISIDHASH Still Matters
38+
39+
Some actions require a valid `Authorization: SAPISIDHASH ...` header (computed from cookies).
40+
41+
Do not remove SAPISIDHASH support just because a specific action seems to work without it on your machine. It can break on:
42+
43+
- different accounts
44+
- different regions
45+
- different browsers
46+
- multi-login sessions
47+
48+
Treat "native works" as the ground truth: if native sends SAPISIDHASH for that call, we should too.
49+
50+
## Endpoint Discovery: Never Hardcode Indices
51+
52+
YouTube reorders context menu items. Never assume "block is item 3".
53+
54+
Instead:
55+
56+
- request `get_item_context_menu`
57+
- search the response tree for endpoint *types*
58+
- prefer endpoint/type checks over label checks
59+
60+
Examples:
61+
62+
- block: `moderateLiveChatEndpoint` (and friends)
63+
- report: `getReportFormEndpoint` (flow can be multi-step)
64+
- delete/retract: `moderateLiveChatEndpoint` from the delete/retract menu item
65+
66+
If an endpoint is missing, log enough context to diagnose:
67+
68+
- which endpoint types we found
69+
- which ones we did not
70+
- which message/menu payload we used to ask for the menu
71+
72+
## Delete / Retract Flow
73+
74+
Delete/retract is not a separate obvious top-level API. It is a context-menu action:
75+
76+
1. Use the message's `contextMenuEndpoint.liveChatItemContextMenuEndpoint.params`.
77+
2. Call `live_chat/get_item_context_menu` with those params and the same Innertube identity headers YouTube uses.
78+
3. Search the response tree for `menuServiceItemRenderer.serviceEndpoint.moderateLiveChatEndpoint`.
79+
4. Prefer candidates whose icon is `DELETE`.
80+
5. Fall back to label text only after endpoint and icon checks, because labels are localized and less stable.
81+
6. POST the selected endpoint params to `live_chat/moderate`.
82+
83+
For streamer/mod deletes, do not infer capability from author id alone. YouTube exposes the delete affordance on each message via `inlineActionButtons`; parse that into `canDelete` and use it with the message's context-menu params.
84+
85+
For self retraction, use the same endpoint discovery path. It may still be a `moderateLiveChatEndpoint`; the important distinction is the menu item YouTube returned for that message/account state, not a different hardcoded endpoint.
86+
87+
## Keep Requests Correlated
88+
89+
If you proxy Innertube calls through a background/service worker, keep request/response events correlated by request id.
90+
91+
Do not use global listeners or "last response wins" patterns. Two actions can overlap, and the wrong response breaks the UI in confusing ways.
92+
93+
## UI State: Be Honest
94+
95+
When we apply an action locally, separate request success from YouTube's later chat-state echo.
96+
97+
For delete/retract:
98+
99+
- on local request success: mark the message as pending deleted, but keep the original runs available locally
100+
- on `markChatItemAsDeletedAction`: replace with YouTube's deleted-state message
101+
- on `removeChatItemAction`: treat the target message as pending deleted if YouTube only tells us to remove the item
102+
- on `markChatItemsByAuthorAsDeletedAction`: apply the deleted-state message to messages from that author
103+
- if YouTube provides `showOriginalContentMessage`, keep it as a view-original toggle for moderator contexts
104+
- on failure: keep it visible and surface an error
105+
106+
Do not mutate the parsed message text in place. Keep the original parsed runs and choose the displayed runs at the render edge. Otherwise pending state, "view deleted message", and later YouTube confirmation can clobber each other.
107+
108+
## HAR + DevTools Tips (So You Do Not Lose The Payload)
109+
110+
- Enable "Preserve log" before doing the flow.
111+
- Export as "HAR with content" so request/response bodies are included.
112+
- If a body looks truncated, open the request and use the raw view in DevTools (Chrome often still has it).
113+
114+
## Where This Usually Breaks
115+
116+
- Missing `x-goog-authuser` (multi-account sessions)
117+
- Dropped `clickTrackingParams` / message `params`
118+
- Wrong Innertube client name/version (YouTube serves different schemas)
119+
- SAPISIDHASH removed or computed for the wrong origin
120+
- Context menu parsing tied to item index instead of endpoint types

package-lock.json

Lines changed: 11 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"is-dark-color": "^1.2.0",
4848
"npm-run-all": "^4.1.5",
4949
"patch-package": "^8.0.0",
50-
"postcss": "^8.4.31",
50+
"postcss": "^8.5.10",
5151
"postcss-extend": "^1.0.5",
5252
"postcss-import": "^14.0.2",
5353
"postcss-input-range": "^4.0.0",

postcss.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const safelistSelectors = [
66
'body',
77
'stroke-primary',
88
'mode-dark',
9+
'line-through',
910
// Components with custom color prop might need its color to be whitelisted too
1011
'bg-blue-500',
1112
'hover:bg-blue-400'

src/assets/outline.svg

Lines changed: 18 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)