Commit 488e407
feat(twitter): add device-follow notification stream command
* feat(twitter): add device-follow command for /i/timeline notification stream (#1628)
Closes #1628. Adds the twitter device-follow command, which reads the
curated tweet list aggregated under a bell-icon "new posts from @usera
and N others" notification. Direct GET /i/timeline redirects to /home,
so the data is only reachable via the legacy v1.1 REST endpoint
/i/api/2/notifications/device_follow.json , none of the existing
twitter commands cover this stream:
- twitter timeline home for-you / following feed (different endpoint)
- twitter notifications the notification list itself, not aggregated
tweets inside any one notification
- twitter search search-based, can't reproduce the aggregation
Endpoint discovery + field-mapping originally proposed by @traddo in
#1628; this PR upstreams a clean implementation that:
- Strategy.COOKIE + ct0 from CDP cookie jar + the public web bearer
token from clis/twitter/utils.js (same auth path as twitter timeline)
- Hits /i/api/2/notifications/device_follow.json directly via
page.evaluate fetch on the x.com origin so SameSite=Lax cookies are
preserved
- Joins each entry.content.item.content.tweet.id to
globalObjects.tweets[id] and resolves the author via
globalObjects.users[tweet.user_id_str]
- Returns the canonical twitter row columns (id, author, text, likes,
retweets, replies, views, created_at, url), matching twitter timeline
minus has_media / media_urls / card / quoted_tweet which the legacy
v1.1 endpoint does not surface
- Sets views: null rather than a 0 sentinel; the legacy endpoint does
not return view counts even with include_ext_views=true, and the
GraphQL TweetResultByRestId round-trip per tweet was judged too
expensive for a list command (typed-errors §3: no scalar sentinels
that lie about real engagement)
- parseLimit enforces strict 1-200 integer validation with no silent
clamping; the only baseline addition is the silent-sentinel on the
"unknown" author fallback, which matches the exact precedent in
twitter/timeline.js:76 that is already baselined
Tests: 17 unit tests in device-follow.test.js cover parseLimit strict
validation, URL parameter shape, entry/tweet join, user-resolution
fallback, dedup via the seen set, empty-stream shape, the canonical
column registration, AuthRequiredError on missing ct0, and
CommandExecutionError on non-2xx fetch.
Live verified the endpoint shape end-to-end against the logged-in
session: HTTP 200 with the expected
{globalObjects: {tweets, users}, timeline: {id: 'tweet_notifications',
instructions: [{addEntries: {entries: []}}]}} envelope. The tester
account has no bell-notification follows enabled, so entries is empty,
but the shape and auth path are confirmed against the documented
spec.
* fix(twitter): harden device-follow typed boundaries
* fix(twitter): fail fast on device-follow drift
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>1 parent e682c1c commit 488e407
5 files changed
Lines changed: 525 additions & 4 deletions
File tree
- clis/twitter
- docs/adapters/browser
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23961 | 23961 | | |
23962 | 23962 | | |
23963 | 23963 | | |
| 23964 | + | |
| 23965 | + | |
| 23966 | + | |
| 23967 | + | |
| 23968 | + | |
| 23969 | + | |
| 23970 | + | |
| 23971 | + | |
| 23972 | + | |
| 23973 | + | |
| 23974 | + | |
| 23975 | + | |
| 23976 | + | |
| 23977 | + | |
| 23978 | + | |
| 23979 | + | |
| 23980 | + | |
| 23981 | + | |
| 23982 | + | |
| 23983 | + | |
| 23984 | + | |
| 23985 | + | |
| 23986 | + | |
| 23987 | + | |
| 23988 | + | |
| 23989 | + | |
| 23990 | + | |
| 23991 | + | |
| 23992 | + | |
| 23993 | + | |
| 23994 | + | |
| 23995 | + | |
| 23996 | + | |
| 23997 | + | |
| 23998 | + | |
| 23999 | + | |
| 24000 | + | |
| 24001 | + | |
| 24002 | + | |
| 24003 | + | |
23964 | 24004 | | |
23965 | 24005 | | |
23966 | 24006 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
0 commit comments