Skip to content

Fix ondemand.s.js URL parsing for new X response format#410

Open
pothitos wants to merge 1 commit intod60:mainfrom
pothitos:patch-1
Open

Fix ondemand.s.js URL parsing for new X response format#410
pothitos wants to merge 1 commit intod60:mainfrom
pothitos:patch-1

Conversation

@pothitos
Copy link
Copy Markdown
Contributor

@pothitos pothitos commented Mar 19, 2026

X changed the ondemand.s.js format. The response now uses a numeric index and a separate hash for the filename instead of the previous pattern. Update ON_DEMAND_FILE_REGEX and add ON_DEMAND_HASH_PATTERN to resolve the script URL correctly; adjust INDICES_REGEX and group index for key_byte_indices.

Credits to the contributors of the below issues.

Resolves #408 #409

Summary by Sourcery

Update ondemand.s.js parsing to support X’s new response format and correctly resolve key byte indices.

Bug Fixes:

  • Fix ondemand.s.js URL extraction to work with the new indexed response structure and separate hash-based filenames.
  • Correct key_byte_indices parsing by adjusting the indices regex and captured group.

Summary by CodeRabbit

  • Bug Fixes

    • Improved transaction data extraction reliability by refining pattern matching and validation logic for client transactions.
  • Refactor

    • Optimized transaction processing to consolidate response parsing and reduce redundant operations.

X changed the ondemand.s.js format (Mar 2026). The response now uses a
numeric index and a separate hash for the filename instead of the
previous pattern. Update ON_DEMAND_FILE_REGEX and add
ON_DEMAND_HASH_PATTERN to resolve the script URL correctly; adjust
INDICES_REGEX and group index for key_byte_indices.

Resolves d60#408 d60#409
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Mar 19, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Adjusts the ondemand.s.js discovery and URL construction to support X’s new response format that uses numeric indices and separate filename hashes, and updates the byte-index extraction logic accordingly.

Sequence diagram for updated ondemand.s.js URL resolution in get_indices

sequenceDiagram
    participant ClientTransaction
    participant HomePageResponse as home_page_response
    participant Session as session
    participant XFrontend as x_homepage
    participant AbsCdn as abs_twitter_cdn

    ClientTransaction->>HomePageResponse: validate_response(home_page_response)
    HomePageResponse-->>ClientTransaction: response_str

    ClientTransaction->>ClientTransaction: ON_DEMAND_FILE_REGEX.search(response_str)
    alt ondemand index found
        ClientTransaction->>ClientTransaction: Extract on_demand_file_index (group 1)
        ClientTransaction->>ClientTransaction: Build hash_regex from ON_DEMAND_HASH_PATTERN and index
        ClientTransaction->>ClientTransaction: hash_regex.search(response_str)
        alt hash match found
            ClientTransaction->>ClientTransaction: Extract filename hash (group 1)
            ClientTransaction->>ClientTransaction: Build ondemand.s.js URL with filename hash
            ClientTransaction->>AbsCdn: GET ondemand.s.<hash>a.js
            AbsCdn-->>ClientTransaction: on_demand_file_response.text

            ClientTransaction->>ClientTransaction: INDICES_REGEX.finditer(on_demand_file_response.text)
            ClientTransaction->>ClientTransaction: Append each group 1 to key_byte_indices
        else hash match not found
            ClientTransaction->>ClientTransaction: key_byte_indices remains empty
        end
    else ondemand index not found
        ClientTransaction->>ClientTransaction: key_byte_indices remains empty
    end

    ClientTransaction->>ClientTransaction: if not key_byte_indices -> raise Exception
    ClientTransaction->>ClientTransaction: map key_byte_indices to int and return
Loading

File-Level Changes

Change Details Files
Update ondemand.s.js locator and hash extraction for new X response format.
  • Replace the old regex that captured the ondemand.s.js hash directly with a regex that captures the numeric index of the ondemand.s entry in the response.
  • Introduce ON_DEMAND_HASH_PATTERN to dynamically build a regex that looks up the hash value associated with the captured ondemand.s index.
  • Use the captured index and hash pattern to resolve the correct ondemand.s.a.js URL instead of relying on the previous inline hash format.
twikit/x_client_transaction/transaction.py
Adjust key byte indices parsing from ondemand.s.js contents.
  • Simplify INDICES_REGEX to match array index patterns like "[], 16" that appear in the new script format.
  • Change the get_indices loop to append group(1) from the updated regex (the numeric index) instead of group(2), then convert them to integers as before.
  • Ensure that the response body used for regex operations is stored once as a string (response_str) to avoid repeated str() conversion and to share between file index and hash parsing.
twikit/x_client_transaction/transaction.py

Assessment against linked issues

Issue Objective Addressed Explanation
#408 Update ondemand.s.js URL discovery/parsing in ClientTransaction so it matches the new X (Twitter) response format and correctly resolves the script URL.
#408 Update extraction of KEY_BYTE indices from ondemand.s.js (regex and group handling) so that key_byte_indices are successfully populated and ClientTransaction no longer raises "Couldn't get KEY_BYTE indices" or breaks before setting its key.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f823afc8-9c54-472e-a7e1-521fc09d26b0

📥 Commits

Reviewing files that changed from the base of the PR and between c3b7220 and ed20f93.

📒 Files selected for processing (1)
  • twikit/x_client_transaction/transaction.py

📝 Walkthrough

Walkthrough

This PR fixes regex pattern matching in ClientTransaction to handle Twitter's updated ondemand.s.js structure. New regex patterns extract indices using different capture groups, a new hash pattern constant is introduced, and the get_indices method is refactored to dynamically construct hash regex and conditionally fetch indices.

Changes

Cohort / File(s) Summary
Transaction Regex & Logic Fix
twikit/x_client_transaction/transaction.py
Updated ON_DEMAND_FILE_REGEX and INDICES_REGEX patterns to match Twitter's new structure; added ON_DEMAND_HASH_PATTERN constant. Refactored get_indices to convert response to string once, dynamically build hash regex, and use new capture group indices when parsing ondemand script. Added conditional layer requiring both ondemand index and hash filename match before fetching indices.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 The regex patterns danced and changed,
Twitter's structure rearranged,
But with new captures, fresh and bright,
The indices now parse just right!
ClientTransaction hops again free,
A rabbit's fix for all to see! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary change: fixing URL parsing for ondemand.s.js to handle a new X response format.
Linked Issues check ✅ Passed The PR addresses the core issue #408 by updating regexes to extract KEY_BYTE indices from the new ondemand.s.js format, restoring library functionality.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the ondemand.s.js parsing issue; no out-of-scope modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can disable the changed files summary in the walkthrough.

Disable the reviews.changed_files_summary setting to disable the changed files summary in the walkthrough.

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • response_str = str(response) relies on the object's __str__ implementation and may mask encoding/format issues; consider using the raw body (e.g. response.text or an explicit attribute) to make the parsing more predictable.
  • The two-step lookup (index via ON_DEMAND_FILE_REGEX and hash via ON_DEMAND_HASH_PATTERN) fails silently and then raises a generic "Couldn't get KEY_BYTE indices" error; consider adding more granular error messages or early returns to indicate whether the index lookup, hash lookup, or script fetch failed.
  • hash_regex = re.compile(ON_DEMAND_HASH_PATTERN.format(on_demand_file_index)) recompiles the pattern on each call; consider either using re.search directly on the formatted string or caching/parameterizing the pattern to avoid repeated compilation in hot paths.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- `response_str = str(response)` relies on the object's `__str__` implementation and may mask encoding/format issues; consider using the raw body (e.g. `response.text` or an explicit attribute) to make the parsing more predictable.
- The two-step lookup (index via `ON_DEMAND_FILE_REGEX` and hash via `ON_DEMAND_HASH_PATTERN`) fails silently and then raises a generic `"Couldn't get KEY_BYTE indices"` error; consider adding more granular error messages or early returns to indicate whether the index lookup, hash lookup, or script fetch failed.
- `hash_regex = re.compile(ON_DEMAND_HASH_PATTERN.format(on_demand_file_index))` recompiles the pattern on each call; consider either using `re.search` directly on the formatted string or caching/parameterizing the pattern to avoid repeated compilation in hot paths.

## Individual Comments

### Comment 1
<location path="twikit/x_client_transaction/transaction.py" line_range="17" />
<code_context>
 from .utils import float_to_hex, is_odd, base64_encode, handle_x_migration

 ON_DEMAND_FILE_REGEX = re.compile(
-    r"""['|\"]{1}ondemand\.s['|\"]{1}:\s*['|\"]{1}([\w]*)['|\"]{1}""", flags=(re.VERBOSE | re.MULTILINE))
-INDICES_REGEX = re.compile(
-    r"""(\(\w{1}\[(\d{1,2})\],\s*16\))+""", flags=(re.VERBOSE | re.MULTILINE))
+    r""",(\d+):["']ondemand\.s["']""", flags=(re.VERBOSE | re.MULTILINE))
+ON_DEMAND_HASH_PATTERN = r',{}:"([0-9a-f]+)"'
+INDICES_REGEX = re.compile(r"\[(\d+)\],\s*16")
</code_context>
<issue_to_address>
**suggestion (bug_risk):** ON_DEMAND_FILE_REGEX supports both quote types, but the hash pattern assumes double quotes only.

Because `ON_DEMAND_HASH_PATTERN` is fixed to `:"..."`, it will fail when the source uses single quotes, even though `ON_DEMAND_FILE_REGEX` would still match. This creates a silent `hash_match is None` path. Consider updating `ON_DEMAND_HASH_PATTERN` to accept both quote types (e.g., `[:]["']([0-9a-fA-F]+)["']`).

```suggestion
ON_DEMAND_HASH_PATTERN = r',{}:["\']([0-9a-fA-F]+)["\']'
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

INDICES_REGEX = re.compile(
r"""(\(\w{1}\[(\d{1,2})\],\s*16\))+""", flags=(re.VERBOSE | re.MULTILINE))
r""",(\d+):["']ondemand\.s["']""", flags=(re.VERBOSE | re.MULTILINE))
ON_DEMAND_HASH_PATTERN = r',{}:"([0-9a-f]+)"'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): ON_DEMAND_FILE_REGEX supports both quote types, but the hash pattern assumes double quotes only.

Because ON_DEMAND_HASH_PATTERN is fixed to :"...", it will fail when the source uses single quotes, even though ON_DEMAND_FILE_REGEX would still match. This creates a silent hash_match is None path. Consider updating ON_DEMAND_HASH_PATTERN to accept both quote types (e.g., [:]["']([0-9a-fA-F]+)["']).

Suggested change
ON_DEMAND_HASH_PATTERN = r',{}:"([0-9a-f]+)"'
ON_DEMAND_HASH_PATTERN = r',{}:["\']([0-9a-fA-F]+)["\']'

@PawiX25
Copy link
Copy Markdown

PawiX25 commented Mar 23, 2026

Can confirm this works. Applied the patch on my RPi running a Twitter monitoring bot (twikit 2.3.3, Python 3.11) — had all cookie sets failing with Couldn't get KEY_BYTE indices since March 22. After patching transaction.py, everything's back to normal, tweets are being fetched fine.

Please merge 🙏

kerslakechris pushed a commit to kerslakechris/test that referenced this pull request Apr 1, 2026
Twitter changed ondemand.s.js structure in March 2026, breaking
twikit's ClientTransaction.get_indices. Instead of calling the
GraphQL API directly (which requires X-Client-Transaction-Id), we
bring back twikit but patch its regex constants and get_indices
method at import time to match the new JS structure.

Fix sourced from: d60/twikit#410

https://claude.ai/code/session_01TZcmYCvNLAec3dfhBMAzJm
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.

Title: ClientTransaction broken as of March 18 2026 — Couldn't get KEY_BYTE indices

3 participants