|
| 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 |
0 commit comments