Skip to content
EvilNekoPublic

About

极简、零依赖的 Nitter 兼容层:用 X 自己的 GraphQL 接口提供 RSS 与 HTML,给 RSS 阅读器和 AstrBot 插件用

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

xnitter

极简、零依赖的 Nitter 兼容层:直接用 X 自己的 GraphQL 接口,对外提供 /{user}/rss 与 /{user} HTML,给 RSS 阅读器和 astrbot_plugin_nitter_tweets 这类还需要 Nitter 的下游用。

2026 年 9 月,X 向 Nitter 发出 cease-and-desist,上游停更,公共实例要么 451、要么返回空 feed。xnitter 不是 Nitter 的 fork,而是按下游的解析判据反推出的最小兼容实现:只实现真正被调用的少数端点,数据不再从 Nitter 的抓取管道来,而是直连 X 的 Web GraphQL。

特点

  • 零依赖:只用 Python 3.9+ 标准库,无 pip 包、无 venv,一个进程跑完。
  • 无需凭据:X 的 GraphQL 只校验 cookie 的「格式」,用一个公开的 Web Bearer + 格式合法的假 cookie 即可,因此不存在登录态过期的问题。
  • 轻:常驻内存约 15 MiB,单核 VPS 上无压力。
  • 输出严格对齐 Nitter 契约:timeline-item、tweet-content、Min-Id 游标、RFC 822 pubDate、无 DOCTYPE、新→旧排序。

端点

路径 用途
/{user}/rss 用户时间线 RSS,新→旧,默认 20 条
/{user}/media/rss 只含带媒体推文的 RSS(走 UserMedia)
/{user1,user2}/rss 多用户合并(逗号连接)
/i/lists/{id}/rss 列表 RSS
/{user} 时间线 HTML,?cursor= 翻页
/i/lists/{id} 列表 HTML
/search?f=tweets&q=... 搜索(f=top 为热度序)
/hashtag/{tag} 标签兜底
/{user}/status/{id} 单条推文页
/pic/media/{file} 媒体代理(转发 pbs.twimg.com,支持视频封面子路径)
/css/style.css 页面样式

同一个用户 + 同一种输出默认缓存 60 秒,命中时响应 1~2 ms。

快速开始

git clone https://github.com/EvilNeko/xnitter.git
cd xnitter
sudo bash deploy/install.sh          # 默认装到 /opt/xnitter,端口 8090,仅监听 127.0.0.1

装完先自测(会真连 X,能验证取数链路和输出契约):

sudo -u xnitter python3 /opt/xnitter/xn_test.py

然后用 nginx 把域名反代到 127.0.0.1:8090:

server {
    listen 443 ssl;
    server_name nitter.example.com;

    location / {
        proxy_pass http://127.0.0.1:8090;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

配置

全部走环境变量,systemd 读 /etc/xnitter.env(见 deploy/xnitter.env.example)。

变量 默认 说明
XNITTER_HOST 127.0.0.1 监听地址
XNITTER_PORT 8090 监听端口
XNITTER_BASE_URL http://127.0.0.1:8090 对外域名,RSS 里的绝对链接靠它
XNITTER_NAME xnitter 实例名,出现在 RSS title / 首页
XNITTER_TIMEOUT 15 单次请求 X 的超时(秒)
XNITTER_RETRIES 2 失败重试次数
XNITTER_CACHE_TTL 60 响应缓存秒数
XNITTER_PAGE_SIZE 20 单页条数
XNITTER_RSS_MAX_ITEMS 25 RSS 单次输出上限
XNITTER_BEARER / XNITTER_COOKIE / XNITTER_CSRF 内置假凭据 一般不用改;X 更换 Bearer 时再覆盖
XNITTER_LOG_LEVEL INFO 日志级别

配合 AstrBot

插件 astrbot_plugin_nitter_tweets 的 WebUI 里把实例指向本服务,并把抓取后端切成 nitter(mix 会优先走 FxTwitter,日志里会一直显示 生效实例: FxTwitter):

{
  "basic": {
    "instances": ["https://nitter.example.com"],
    "fetch_backend": "nitter"
  }
}

配置文件的编码是 UTF-8 BOM,读写记得用 utf-8-sig。

工作原理

  1. screen_name → 数字 uid:先用 api.fxtwitter.com/{name},失败再走 UserByScreenName。
  2. 时间线:POST SearchTimeline,变量 {"rawQuery": "from:<user>", "product": "Latest"},从 TimelineTimelineCursor 拿翻页游标。
  3. 媒体时间线:POST UserMedia(这个接口的返回是严格时间序,且带媒体)。
  4. 所有 GraphQL 请求都必须用 POST——GET 一律返回 {"errors":[{"message":"Could not authenticate you","code":32}]}。
  5. queryId 会随 X 发版漂移。权威表在 fa0311/twitter-openapi 的 src/config/placeholder.json,失效时替换 xn_api.py 顶部的 OPERATIONS 即可。

已知限制

  • 时间线接口不返回 media,普通 RSS 里的推文没有图;要图/视频封面只能用 /{user}/media/rss。
  • 媒体代理只转发封面,不代理视频本体。
  • 翻页游标由 X 决定,偶尔缺失,那就只有第一页。
  • 上游 queryId 或风控变化时会失效,需要更新 OPERATIONS 或降低频率。
  • Cloudflare 会缓存 RSS(边缘命中时不回源),最长滞后取决于你的缓存策略。

免责声明

本项目仅用于个人自托管、低频读取公开内容。请自行确认你的使用方式符合 X 的服务条款与当地法律;作者不对任何账号风控、封禁或数据问题负责。

License

MIT

About

极简、零依赖的 Nitter 兼容层:用 X 自己的 GraphQL 接口提供 RSS 与 HTML,给 RSS 阅读器和 AstrBot 插件用

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages