Files
Benjamin Liu 488e407a65 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>
2026-05-19 19:34:30 +08:00
..