> For the complete documentation index, see [llms.txt](https://bot-docs.yopro.studio/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bot-docs.yopro.studio/getting-started/twitter-to-bluesky-mirror.md).

# Twitter to Bluesky Mirror

## What It Does

This project is a self-hosted bot that continuously monitors a target Twitter/X account and mirrors its posts to a Bluesky account. When a new tweet is detected, the bot:

1. Downloads any attached media (images/videos)
2. Optionally tralstaed the tweet text via RapidAPI
3. Posts the content to Bluesky using the AT Protocol (`atproto`)
4. Saves state so it never reposts a tweet it has already mirrored

The bot is operated entirely from the command line and is configured through a single `.env` file.

***

## Two-Phase Operation

The script operates in two distinct phases that must be run in order.

{% code lineNumbers="true" expandable="true" %}

```mermaid
flowchart LR

    subgraph Phase_1_One_Time
        A[python setup.py] --> B[env file written]
    end

    subgraph Phase_2_Continuous
        C[python main.py] --> D[load_config]
        D --> E[init_twitter_app]
        D --> F[init_bluesky_client]
        E --> G[monitor_tweets]
        F --> G
        G --> G
    end

    B --> C
```

{% endcode %}

<table><thead><tr><th>Phase</th><th>Script</th><th width="250">Frequency</th><th>Output</th></tr></thead><tbody><tr><td>Setup</td><td><code>setup.py</code></td><td>Once</td><td><code>.env</code> file with all credentials</td></tr><tr><td>Runtime</td><td><code>main.py</code></td><td>Continuously</td><td>Posts to Bluesky, <code>state.json</code>,<code>events.log</code></td></tr></tbody></table>

***

## Repository Layout

| File               | Role                                                                    |
| ------------------ | ----------------------------------------------------------------------- |
| `setup.py`         | Interactive configuration wizard; writes `.env`                         |
| `main.py`          | Main bot runtime; polling loop and all core logic                       |
| `updater.py`       | Auto-update mechanism; fetches new `.py` files from GitHub              |
| `requirements.txt` | Python dependency list                                                  |
| `.env`             | Credentials and runtime configuration (generated by `setup.py`)         |
| `state.json`       | Persists `last_tweet_id` and `last_update_check` (generated at runtime) |
| `events.log`       | Runtime log output                                                      |
| `version.txt`      | Current commit SHA for update tracking (if update checking is enabled)  |
| `session.txt`      | Cached Bluesky session token                                            |

***

## External Service Dependencies

The bot integrates with 3 services at runtime

{% code overflow="wrap" fullWidth="false" expandable="true" %}

```mermaid
flowchart TB

    MAIN[main.py]

    %% GitHub
    subgraph GitHub
        GH1[updater.perform_update\ncommits / compare / raw]
    end

    %% RapidAPI — translation/optional
    subgraph RapidAPI_optional
        RA1[free_google_translator translate_text]
        RA2[TRANSLATOR_RAPIDAPI_KEY]
        RA1 --> RA2
    end

    %% Bluesky — AT Protocol
    subgraph Bluesky_AT_Protocol
        B1[atproto Client send_post]
        B2[atproto com atproto repo upload_blob]
        B3[session.txt]
        B1 --> B3
    end

    %% Twitter / X
    subgraph Twitter_X
        T1[tweety ns TwitterAsync]
        T2[cookie auth_token ct0 guest_id twid]
        T3[password auth TWITTER_USERNAME TWITTER_PASSWORD]
        T1 --> T2
        T1 --> T3
    end

    %% Connections from main.py
    MAIN -->|auto update| GH1
    MAIN -->|optional translation| RA1
    MAIN -->|publishes posts and media| B1
    MAIN -->|uploads blobs| B2
    MAIN -->|scrapes posts| T1
```

{% endcode %}

<table><thead><tr><th width="353">Service</th><th width="310.333251953125">Used for</th><th width="108.66650390625">Required</th></tr></thead><tbody><tr><td>Twitter/X</td><td>Scraping target account tweets via <code>tweety-ns</code></td><td>Yes</td></tr><tr><td>Bluesky (AT Protocol)</td><td>Publishing mirrored posts via <code>atproto</code></td><td>Yes</td></tr><tr><td>RapidAPI (<code>free-google-translator</code>)</td><td>Translating tweet text before posting</td><td>No</td></tr><tr><td>Github (<code>Yoproo20/twitter-to-bluesky-template</code>)</td><td>Auto-updating bot scripts</td><td>No</td></tr></tbody></table>

***

## Core Runtime Concepts <a href="#id-1-core-runtime-concepts" id="id-1-core-runtime-concepts"></a>

### Polling Interval

The bot polls Twitter on a schedule via `CHECK_INTERVAL` variable (in seconds). Also checks for a `shutdown_flag` every second for a clean exit via CTRL+C.

### State Persistence

Two files keep the bot idempotent across runs.

* `state.json` - stores `last_tweet_id` and `last_update_check`. Managed by `load_state()` , `save_state()`, `update_last_tweet_id()`, and `update_last_check_time()`
* `session.txt` - stores serialized Bluesky session token. Reused at startup via `get_session()`&#x20;

### Tweet Processing Pipeline

{% code overflow="wrap" expandable="true" %}

```mermaid
flowchart TB

    A[monitor_tweets app_get_tweets]

    A --> B{tweet_id greater than last_tweet_id}

    %% YES branch
    B -->|yes| C[update_last_tweet_id state.json]
    C --> D[process_tweet]

    D --> E[clean_tweet_text remove_tco_links]
    D --> F[download_tweet_media returns images and videos]
    D --> G[remove temp image and video files]

    E --> H[post_to_bluesky]
    F --> H

    H --> I[upload_media upload_blob]
    I --> J[bluesky_client send_post]

    J --> K{ENABLE_TRANSLATION}

    K -->|yes| L[translate_text RapidAPI POST]
    L --> M[send_translation_reply bluesky_client send_post reply]

    %% NO branch from decision
    B -->|no skip| N[interruptible_sleep CHECK_INTERVAL]

    %% Cleanup also flows to sleep
    G --> N
```

{% endcode %}

***

## (Key) Configuration Variables

<table><thead><tr><th width="228.3333740234375">Variable</th><th width="104.9998779296875">Default</th><th>Effect</th></tr></thead><tbody><tr><td><code>TARGET_USER</code></td><td><em>(required)</em></td><td>Twitter handle to monitor (no <code>@</code>)</td></tr><tr><td><code>CHECK_INTERVAL</code></td><td><code>300</code></td><td>Seconds between tweet polls</td></tr><tr><td><code>ENABLE_TRANSLATION</code></td><td><code>false</code></td><td>Enables RapidAPI translation</td></tr><tr><td><code>TRANSLATION_FROM</code></td><td><code>es</code></td><td>Source language code</td></tr><tr><td><code>TRANSLATION_TO</code></td><td><code>en</code></td><td>Target language code</td></tr><tr><td><code>AUTO_UPDATE</code></td><td><code>true</code></td><td>Enables automatic script updates from GitHub</td></tr><tr><td><code>UPDATE_CHECK_INTERVAL</code></td><td><code>86400</code></td><td>Seconds between GitHub update checks</td></tr></tbody></table>

For the full variable reference, including authentication, see <a href="/pages/ZCwqpdTWmnnheUXuxeDK" class="button primary">Enviroment Variable Reference</a>.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://bot-docs.yopro.studio/getting-started/twitter-to-bluesky-mirror.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
