Skip to content

Conversation

@heysurfer
Copy link

@heysurfer heysurfer commented Oct 19, 2025

Castle Token generation required for Twitter client login

Summary by Sourcery

Add Castle Token support to the Twikit Client by exposing user_agent and castle_api_key options, introducing the CastleToken helper for fetching and caching tokens, and injecting those tokens into onboarding tasks.

New Features:

  • Provide optional user_agent parameter for HTTP requests
  • Provide optional castle_api_key parameter to configure rate limits for Castle Token generation
  • Implement CastleToken class to generate and cache tokens from castle.botwitter.com
  • Automatically inject Castle tokens into onboarding settings_list payloads

Summary by CodeRabbit

  • New Features
    • Added Castle token authentication support to enhance API security and request reliability
    • Automatic token generation with intelligent caching to optimize performance
    • New optional Castle API key parameter for authentication configuration
    • Improved API request handling with Castle token validation and integration

Castle Token generation required for Twitter client login
@sourcery-ai
Copy link

sourcery-ai bot commented Oct 19, 2025

Reviewer's Guide

Integrates Castle Token support by adding a new CastleToken service, extending the Client to accept and initialize an optional API key, and injecting generated tokens into the onboarding workflow.

Sequence diagram for CastleToken generation and onboarding injection

sequenceDiagram
    participant Client
    participant CastleToken
    participant HTTP
    participant "Twitter Onboarding"
    Client->>CastleToken: get_castle_token()
    CastleToken->>CastleToken: Check cache/expiry
    alt Token expired or not cached
        CastleToken->>CastleToken: generate_castle_token()
        CastleToken->>HTTP: POST /generate-token (with cuid, userAgent, API key)
        HTTP-->>CastleToken: Castle token response
        CastleToken->>CastleToken: Cache token
    end
    CastleToken-->>Client: Castle token
    Client->>"Twitter Onboarding": Inject castle_token into subtask_inputs
Loading

Entity relationship diagram for Client and CastleToken

erDiagram
    CLIENT ||--o| CASTLE_TOKEN : initializes
    CLIENT {
        string castle_api_key
    }
    CASTLE_TOKEN {
        string api_key
        string _castle_token
        string _cuid
        float _token_timestamp
    }
Loading

Class diagram for CastleToken integration

classDiagram
    class Client {
        +castle_api_key: str | None
        +castle_token: CastleToken
    }
    class CastleToken {
        +client: Client
        +api_key: str | None
        +_castle_token: str | None
        +_cuid: str | None
        +_token_timestamp: float | None
        +_generate_cuid() str
        +generate_castle_token() str
        +get_castle_token() str
    }
    Client --> CastleToken
Loading

File-Level Changes

Change Details Files
Extend Client to accept and initialize Castle API key
  • Documented user_agent and castle_api_key parameters in Client docstring
  • Added castle_api_key argument to Client.init signature
  • Initialized self.castle_token using the new CastleToken class
twikit/client/client.py
Inject CastleToken in onboarding tasks
  • Loop through subtask_inputs to locate settings_list entries
  • Retrieve a fresh castle_token via get_castle_token()
  • Attach castle_token into each settings_list before request
twikit/client/v11.py
Introduce CastleToken service for token generation and caching
  • Created CastleToken class to generate and cache tokens with a 60-second TTL
  • Implement CUID generation, cookie setting, and API POST to castle.botwitter.com
  • Support optional API key header and JSON response parsing
twikit/castle_token/castle_token.py
twikit/castle_token/__init__.py

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
Contributor

coderabbitai bot commented Oct 19, 2025

Walkthrough

Introduces CastleToken, a new class for managing castle token generation and caching for Twitter API requests, integrating it into the Client initialization and using it within the onboarding task method to conditionally inject tokens into request payloads.

Changes

Cohort / File(s) Summary
CastleToken module
twikit/castle_token/__init__.py, twikit/castle_token/castle_token.py
New CastleToken class that generates and caches castle tokens via POST to castle.botwitter.com. Generates a 32-character CUID, builds a payload with userAgent and CUID, caches tokens with 60-second expiry. Re-exported from package __init__.py for public API access.
Client integration
twikit/client/client.py
Adds castle_api_key optional parameter to Client.init(), initializes CastleToken instance as self.castle_token with the client and API key.
Onboarding task enhancements
twikit/client/v11.py
Extends onboarding_task() to scan subtask_inputs; when a dict with settings_list is found, fetches castle token and injects it into subtask_input['settings_list']['castle_token'] before request execution.

Sequence Diagram

sequenceDiagram
    participant Client
    participant CastleToken
    participant CastleAPI as castle.botwitter.com
    participant V11 as onboarding_task

    Client->>CastleToken: __init__(client, api_key)
    
    V11->>CastleToken: get_castle_token()
    alt Token cached and valid (< 60s)
        CastleToken-->>V11: return cached token
    else Token expired or missing
        CastleToken->>CastleAPI: POST /generate-token (cuid, userAgent)
        CastleAPI-->>CastleToken: token response
        CastleToken->>CastleToken: cache token + timestamp
        CastleToken-->>V11: return token
    end
    
    V11->>V11: inject token into settings_list['castle_token']
    V11->>V11: proceed with request
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

The changes involve new CastleToken class logic with caching and timestamp management, integration into Client initialization, and conditional token injection in v11.py. While the patterns are consistent and relatively straightforward, the distributed nature across multiple files and the introduction of external API communication warrant careful review.

Poem

🐰 A castle token guards the way,
With caching tricks that save the day,
Through botwitter's gates we leap so high,
Fresh tokens generated on the fly,
The Client hops with newfound grace! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Add Castle Token support for Twitter login / onboarding" is fully aligned with the changeset's primary objective. The entire PR focuses on implementing Castle Token functionality—including a new CastleToken class, integration into the Client initialization, and injection of the token during the onboarding task—all specifically to support Twitter authentication and onboarding flows. The title is concise, clear, and specific enough to convey the main change without vague terminology, making it easily understandable for someone reviewing the project history.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link

@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 there - I've reviewed your changes - here's some feedback:

Blocking issues:

  • Detected a Generic API Key, potentially exposing access to various services and sensitive operations. (link)

General comments:

  • Assign the user_agent parameter to the Client instance (e.g., self._user_agent = user_agent) so generate_castle_token can reliably use it without risking undefined attributes.
  • Add error handling and optional retry/backoff logic around the castle token POST request to handle HTTP failures or invalid responses gracefully instead of returning an empty string.
  • Consider implementing client-side rate limiting or throttling for generate_castle_token calls to avoid exceeding the Castle API’s default quotas.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Assign the user_agent parameter to the Client instance (e.g., self._user_agent = user_agent) so generate_castle_token can reliably use it without risking undefined attributes.
- Add error handling and optional retry/backoff logic around the castle token POST request to handle HTTP failures or invalid responses gracefully instead of returning an empty string.
- Consider implementing client-side rate limiting or throttling for generate_castle_token calls to avoid exceeding the Castle API’s default quotas.

## Individual Comments

### Comment 1
<location> `twikit/castle_token/castle_token.py:78-81` </location>
<code_context>
+
+        # Extract and cache the castle token from response
+        response_data = response.json()
+        self._castle_token = response_data.get('token', '')
+        self._token_timestamp = time.time()
+
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Defaulting to an empty string for missing token may mask API errors.

Consider raising an exception or logging a warning if 'token' is missing to improve error visibility and handling.

```suggestion
        # Extract and cache the castle token from response
        response_data = response.json()
        token = response_data.get('token')
        if not token:
            import logging
            logging.warning("Castle token missing from response: %s", response_data)
            raise ValueError("Castle token missing from API response")
        self._castle_token = token
        self._token_timestamp = time.time()
```
</issue_to_address>

### Comment 2
<location> `twikit/castle_token/castle_token.py:55-58` </location>
<code_context>
+            The generated Castle token
+        """
+        # Generate cuid
+        self._cuid = self._generate_cuid()
+
+        # Set __cuid cookie
+        self.client.http.cookies.set('__cuid', self._cuid)
+
+        # Prepare request data
</code_context>

<issue_to_address>
**question (bug_risk):** Setting the __cuid cookie directly may have concurrency implications.

Parallel requests sharing the same client may overwrite the __cuid cookie, causing race conditions. Evaluate statelessness or per-request cookie handling to avoid this.
</issue_to_address>

### Comment 3
<location> `twikit/client/v11.py:78-87` </location>
<code_context>
             data['subtask_inputs'] = subtask_inputs

+            # Inject castle_token if settings_list is present
+            for subtask_input in subtask_inputs:
+                if isinstance(subtask_input, dict) and 'settings_list' in subtask_input:
+                    castle_token = await self.base.castle_token.get_castle_token()
+                    subtask_input['settings_list']['castle_token'] = castle_token
+
         headers = {
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Directly mutating subtask_inputs may have unintended side effects.

Mutating subtask_inputs in place may affect other parts of the code that reference it. To avoid this, copy the dict before modifying it.

```suggestion
        if subtask_inputs is not None:
            new_subtask_inputs = []
            # Inject castle_token if settings_list is present
            for subtask_input in subtask_inputs:
                if isinstance(subtask_input, dict) and 'settings_list' in subtask_input:
                    subtask_input_copy = subtask_input.copy()
                    castle_token = await self.base.castle_token.get_castle_token()
                    subtask_input_copy['settings_list'] = subtask_input_copy['settings_list'].copy()
                    subtask_input_copy['settings_list']['castle_token'] = castle_token
                    new_subtask_inputs.append(subtask_input_copy)
                else:
                    new_subtask_inputs.append(subtask_input)
            data['subtask_inputs'] = new_subtask_inputs

        headers = {
```
</issue_to_address>

### Comment 4
<location> `twikit/client/client.py:99` </location>
<code_context>
premium_key_abc123def456ghi789
</code_context>

<issue_to_address>
**security (generic-api-key):** Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

*Source: gitleaks*
</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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
twikit/castle_token/castle_token.py (1)

24-29: Consider documenting cookie mutation side effect.

The generate_castle_token method (line 58) mutates self.client.http.cookies by setting the __cuid cookie. This side effect should be documented in the class or method docstring so users understand that creating a CastleToken instance will modify the client's cookie jar.

 class CastleToken:
     """
     Handles Castle Token generation for Twitter API requests.
     The token is cached for 1 minute to avoid unnecessary API calls.
+    
+    Note: Token generation sets the '__cuid' cookie on the client's HTTP session.
 
     Parameters
     ----------
     client : Client
         The Twitter client instance
     api_key : str | None, default=None
         Optional API key for castle.botwitter.com
         - Without API key: 3 requests/second, 100 requests/hour (default rate limits)
         - With API key: Custom rate limits, higher quotas, priority support
     """
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b18105 and babc3c1.

📒 Files selected for processing (4)
  • twikit/castle_token/__init__.py (1 hunks)
  • twikit/castle_token/castle_token.py (1 hunks)
  • twikit/client/client.py (5 hunks)
  • twikit/client/v11.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
twikit/client/client.py (1)
twikit/castle_token/castle_token.py (1)
  • CastleToken (9-102)
twikit/castle_token/__init__.py (1)
twikit/castle_token/castle_token.py (1)
  • CastleToken (9-102)
twikit/client/v11.py (1)
twikit/castle_token/castle_token.py (1)
  • get_castle_token (85-102)
twikit/castle_token/castle_token.py (1)
twikit/client/client.py (2)
  • Client (66-4336)
  • get (221-223)
🪛 Gitleaks (8.28.0)
twikit/client/client.py

[high] 99-99: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (4)
twikit/castle_token/__init__.py (1)

1-3: LGTM!

Clean package initialization following Python conventions.

twikit/castle_token/castle_token.py (1)

85-102: LGTM! Well-implemented token caching.

The caching logic correctly handles token expiration (60 seconds) and regenerates when needed. The implementation efficiently avoids unnecessary API calls.

twikit/client/client.py (2)

61-61: LGTM! Clean integration of Castle Token support.

The CastleToken integration is well-structured:

  • Parameter added to __init__ with proper documentation
  • Instance always created (simplifies usage in v11.py)
  • Example usage provided in docstring

The approach of always instantiating CastleToken (even without an API key) is reasonable since the token generation will still work with default rate limits, and it simplifies the code in onboarding_task.

Also applies to: 83-86, 98-99, 108-108, 125-125


99-99: Static analysis false positive - ignore Gitleaks warning.

The Gitleaks tool flagged line 99 as potentially containing an API key, but this is simply a docstring example ('premium_key_abc123def456ghi789'). This is not a real credential and is clearly示范性的 示例代码.

@alphaleadership
Copy link

@d60

@AritzUMA
Copy link

AritzUMA commented Dec 9, 2025

This need a pay method, not recommended

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.

4 participants