Skip to content

feat: add sendTweet with media upload support - #193

Open
Faareoh wants to merge 250 commits into
the-convocation:mainfrom
Faareoh:feat/send-tweet
Open

feat: add sendTweet with media upload support#193
Faareoh wants to merge 250 commits into
the-convocation:mainfrom
Faareoh:feat/send-tweet

Conversation

@Faareoh

@Faareoh Faareoh commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

⚠️ This PR was entirely generated by Claude (claude-sonnet-4-6), inspired by
the reference implementation at https://github.com/0x4337/agent-twitter-client-grokky.

Summary

  • src/api-data.ts — adds mutationEndpoints.CreateTweet (current queryId + x.com URL)
  • src/tweets.ts — implements uploadMedia() (chunked video + simple image) and sendTweet() returning Tweet | null
  • src/scraper.ts — exposes sendTweet() as a public method on the Scraper class
  • src/_module.ts — exports the new MediaData type
  • src/tweet-mutations.test.ts — unit tests + opt-in integration tests for the mutation

Key details

  • Uses the CreateTweet GraphQL mutation (queryId: S1qcGUn68_U0lDKdMlYSGg) via POST x.com/i/api/graphql/…
  • Injects __typename: 'Tweet' on the response when absent (the CreateTweet API omits it) so the existing parser can handle it
  • Generates x-client-transaction-id when the experimental.xClientTransactionId option is enabled, to avoid Twitter's 226 "looks automated" errors
  • MediaData accepts { data: Buffer, mediaType: string }; videos use the chunked INIT/APPEND/FINALIZE flow with status polling

Test plan

  • npm test — all unit tests pass
  • Integration tests run with real credentials: set TWITTER_* env vars and run npx jest tweet-mutations --testNamePattern=integration

karashiiro and others added 30 commits July 1, 2023 12:23
…on#24)

- remove deprecation warning by using `JestConfigWithTsJest`
- allow jest to use local .env file by providing `dotenv` to config.
- make email variable optional when running auth test.
- fixed fetchTweets returning an empty array lol :]
The following properties on the `Tweet` interface are now `false` as default. This most likely fixes any method that relies on checking these.

- isQuoted
- isRetweet
- isReply
- isPin
- sensitiveContent

Tweet tests have been updated accordingly.
karashiiro and others added 28 commits November 25, 2025 20:17
Updates the SearchTimeline GraphQL endpoint to fix 404 errors when using
fetchSearchTweets and related search functionality.

Changes:
- Update query ID from bshMIjqDk8LTXTq4w91WKw to nK1dw4oV3k4w5TdtcAdSww
- Add missing feature flags required by Twitter's API:
  - responsive_web_graphql_exclude_directive_enabled
  - tweetypie_unmention_optimization_enabled
  - responsive_web_media_download_video_enabled
  - rweb_lists_timeline_redesign_enabled

Fixes the-convocation#77
Related to the-convocation#137, the-convocation#165

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Removed NPM_AUTH_TOKEN from publish step environment variables.
- Updates all GraphQL endpoints
- Overhauls login flow to deal with anti-bot mechanisms
  - Adds a preflight request to the request lifecycle
  - Properly implements the JS instrumentation subtask
  - Castle token spoofing
- Updates a bunch of old API endpoints
…n#186)

- Update 9 of 10 endpoint query IDs extracted from Twitter's
  current main.js bundle (Feb 20, 2026)
- Sync feature flags to match current frontend expectations
- Remove deprecated flags: creator_subscriptions_quote_tweet_preview,
  graphql_exclude_directive, tweetypie_unmention_optimization,
  media_download_video, lists_timeline_redesign
- Fixes the-convocation#185 (SearchTimeline returning 404)
Twitter has started blocking GET requests to the SearchTimeline
GraphQL endpoint, returning 404 for all search queries. This change
switches the search endpoint to use POST with a JSON body instead
of URL query parameters, matching the fix discovered in twikit#412.

Changes:
- api.ts: Add optional `body` parameter to requestApi(), set
  content-type header and stringify body for POST requests
- api-data.ts: Add toRequestBody() method to ApiRequest class
  for converting variables/features/fieldToggles to JSON body
- search.ts: Change getSearchTimeline from GET to POST, pass
  request body via toRequestBody() instead of URL via toRequestUrl()

Fixes the-convocation#188
Adds scraper.sendTweet() for posting tweets via the CreateTweet GraphQL
mutation, including image/video media upload through the chunked upload API.

- Add mutationEndpoints (CreateTweet queryId + URL) to api-data.ts
- Implement uploadMedia() with chunked video upload + polling and simple
  image upload paths
- Implement sendTweet() returning Tweet | null; injects __typename when
  absent from the CreateTweet API response to satisfy the parser
- Add optional x-client-transaction-id generation to avoid 226 errors
- Expose sendTweet on the Scraper class and export MediaData from _module.ts
- Add unit + opt-in integration tests in tweet-mutations.test.ts
@Morad37

Morad37 commented Jun 6, 2026

Copy link
Copy Markdown

The sendTweet function and its integration with media upload is a solid addition. One area that could use hardening: the media/upload step's error response is not shown being validated in the available diff. If X returns a non-JSON response or a response missing media_id_string, the downstream code would attempt to use undefined as the media ID, which would likely produce a confusing API error from the CreateTweet endpoint. Consider validating that the upload response contains a valid string media_id_string before continuing to the tweet mutation, and returning a clear error if it doesn't. The test mock always returns { media_id_string: 'mock_media_id' }, which is fine for happy-path coverage but means an upload failure would not be exercised. A test case with a mock that returns an error from media/upload would fill that gap.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.