# twitter-cli 全量 AI 参考文档 > 版本:1.0.0 / 2026-04-17 > 适用对象:AI agent、自动化脚本、开发者 > stdout 严格 JSONL;stderr 为 tracing 日志(默认关闭) --- # 1. 概述 `twitter-cli` 是 x-api-rs 库附带的命令行工具,面向 AI agent 自动化场景设计。 核心特性: - stdout 仅输出机器可读的 JSONL,无任何人类美化装饰 - stderr 承载 tracing 日志,与 stdout 完全分离 - 所有错误包含结构化 code、retryable、recovery_actions 字段 - 12 种确定性退出码,便于 shell 脚本条件判断 - 批量命令采用三段式 header/item/summary 格式,支持流式处理 当前实装 15 个 action,覆盖 6 个功能模块(dm / posts / user / search / upload / settings)。 --- # 2. 快速开始 ## 安装 ```bash cargo install x-api-rs --features cli # 首次编译约 10-15 分钟 twitter-cli --version # twitter-cli 1.0.0 ``` ## 获取 cookies 1. 浏览器登录 twitter.com 2. 开发者工具(F12)→ Network 标签 → 任意请求的 Cookie 头 3. 复制包含 ct0、auth_token、twid 的完整 cookies 字符串 4. 保存到文件并设置权限: ```bash echo 'ct0=xxx; auth_token=xxx; twid=u%3Dxxx; ...' > ~/.x-api/cookies.txt chmod 600 ~/.x-api/cookies.txt ``` ## 第一条命令 ```bash twitter-cli --cookies-file cookies.txt user get --screen-name twitter ``` 成功输出(实际为一行 JSONL): ```json {"success":true,"data":{"id":"783214","name":"Twitter","screen_name":"twitter","followers_count":65000000,"verified":true},"error":null,"meta":{"module":"user","action":"get","elapsed_ms":342,"timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` --- # 3. 全局 flags 所有子命令共享以下全局 flags,必须放在子命令名称之前: ``` twitter-cli [全局 flags] <模块> <子命令> [子命令参数] ``` | Flag | 类型 | 默认值 | 说明 | |------|------|--------|------| | `--cookies-file ` | path | — | 从文件读取 cookies(认证优先级最高) | | `--cookies-stdin` | bool flag | false | 显式从 stdin 读取 cookies(5s 超时) | | `--profile ` | string | — | 使用 ~/.x-api/config.toml 中的 profile | | `--proxy ` | string | — | 代理 URL(覆盖 profile.proxy),支持 http/socks5 | | `--request-timeout-secs ` | u64 | 30 | 单次 HTTP 请求超时(秒) | | `--log-level ` | string | off | stderr 日志级别:off\|error\|warn\|info\|debug\|trace | | `--log-format ` | string | json | stderr 日志格式:json\|compact | | `--ja3 ` | bool | true | JA3/TLS 指纹模拟,true=模拟 Chrome 浏览器 | --- # 4. 认证优先级链 CLI 按以下顺序查找认证信息(从高到低,找到即停止): ``` 1. --cookies-file 最高优先级,明确指定文件 2. --cookies-stdin / 非 tty stdin 从 stdin pipe 读取,5 秒超时后报错 3. X_API_COOKIES_FILE 环境变量 指向 cookies 文件路径 4. ~/.x-api/config.toml profile.cookies_file 字段 ``` 如果四种方式都未找到认证信息,CLI 退出码 3(AUTH_FAILED)。 ## Cookies 文件权限规则(Unix) | 文件权限 | 行为 | |----------|------| | `0o600` 或更严格 | 正常运行 | | `0o644`(其他用户可读) | stderr 输出 WARNING,继续运行 | | `0o660` 以上(group/other 可写) | 拒绝运行,退出码 3 | 推荐:`chmod 600 ~/.x-api/cookies.txt` ## config.toml 格式 ```toml [profile.default] cookies_file = "~/.x-api/cookies.txt" proxy = "http://127.0.0.1:8080" # 可选 [profile.account2] cookies_file = "~/.x-api/cookies2.txt" ``` 使用指定 profile:`twitter-cli --profile account2 ...` --- # 5. 输出格式规范 ## 5.1 stdout vs stderr 分离 | 流 | 内容 | 格式 | |----|------|------| | stdout | 命令结果(JSONL) | 严格 JSON,每行一个对象,无额外空白 | | stderr | tracing 日志 | 由 --log-format 控制(json / compact) | AI agent 只解析 stdout,忽略 stderr: ```bash result=$(twitter-cli ... 2>/dev/null) ``` ## 5.2 Envelope 格式(单条命令) 所有非批量命令输出单行 JSON: ``` {"success":bool,"data":T|null,"error":ErrorObj|null,"meta":{...}} ``` ### Envelope 字段 | 字段 | 类型 | 说明 | |------|------|------| | success | bool | true=操作成功,false=失败 | | data | T\|null | 成功时的结果;失败时为 null | | error | ErrorObj\|null | 失败时的错误;成功时为 null | | meta.module | string | 模块名(dm/posts/user/search/upload/settings) | | meta.action | string | 操作名(send/create/get 等) | | meta.elapsed_ms | number | 命令总耗时(毫秒) | | meta.timestamp | string | ISO 8601 UTC 时间戳 | | meta.cli_version | string | CLI 版本(如 "1.0.0") | | meta.schema_version | string | 输出格式版本(如 "1.0") | ### 完整成功示例 ```json {"success":true,"data":{"user_id":"783214","event_id":"1811234567890123456","message":"Message sent successfully","media_id":null},"error":null,"meta":{"module":"dm","action":"send","elapsed_ms":519,"timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` ### 完整失败示例 ```json {"success":false,"data":null,"error":{"code":"AUTH_FAILED","message":"Twitter 返回 401,cookies 可能已过期","retryable":false,"retry_after_secs":null,"docs_url":"https://x-api-rs.es007.com/cli/getting-started/#cookies","recovery_actions":["重新从浏览器导出 cookies","确认 cookies 包含 ct0 和 auth_token 字段"],"issue_url":null,"details":{"http_status":401}},"meta":{"module":"dm","action":"send","elapsed_ms":128,"timestamp":"2026-04-17T10:46:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` ## 5.3 BatchLine 三段式格式(批量命令) 批量命令(`dm send-batch`)输出多行 JSONL,用 `type` 字段区分三种行类型: ### header 行(第 1 行) ```json {"type":"header","command":"dm send-batch","schema_version":"1.0","expected_count":3,"meta":{"module":"dm","action":"send-batch","timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0"}} ``` ### item 行(每个目标一行) 成功: ```json {"type":"item","index":0,"success":true,"data":{"user_id":"783214","event_id":"1811234567890123456","message":"Message sent successfully","media_id":null}} ``` 失败: ```json {"type":"item","index":1,"success":false,"error":{"code":"NOT_FOUND","message":"用户 999999 不存在或无法接收私信","retryable":false,"retry_after_secs":null,"recovery_actions":["确认 user_id 是否正确"]}} ``` ### summary 行(最后 1 行) ```json {"type":"summary","total":3,"success_count":2,"fail_count":1,"elapsed_ms":1820} ``` ### 完整 5 行示例(3 个目标) ```jsonl {"type":"header","command":"dm send-batch","schema_version":"1.0","expected_count":3,"meta":{"module":"dm","action":"send-batch","timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0"}} {"type":"item","index":0,"success":true,"data":{"user_id":"783214","event_id":"1811234567890123456","message":"Message sent successfully","media_id":null}} {"type":"item","index":1,"success":false,"error":{"code":"NOT_FOUND","message":"用户 6253282 不存在","retryable":false,"retry_after_secs":null,"recovery_actions":["确认 user_id 是否正确"]}} {"type":"item","index":2,"success":true,"data":{"user_id":"1234567890","event_id":"1811234567890123457","message":"Message sent successfully","media_id":null}} {"type":"summary","total":3,"success_count":2,"fail_count":1,"elapsed_ms":1820} ``` ## 5.4 ErrorObject 结构 ```json { "code": "INVALID_ARGS", "message": "人类可读的错误描述", "retryable": false, "retry_after_secs": null, "docs_url": "https://...", "recovery_actions": ["步骤1", "步骤2"], "issue_url": null, "details": {"http_status": 422} } ``` | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | code | string | 是 | ErrorCode 枚举值 | | message | string | 是 | 人类可读描述 | | retryable | bool | 是 | 是否值得重试 | | retry_after_secs | number\|null | 否 | 建议等待秒数(来自 Retry-After 响应头) | | docs_url | string\|null | 否 | 相关文档链接 | | recovery_actions | string[] | 是 | 建议修复步骤 | | issue_url | string\|null | 否 | GitHub issue 链接(已知 Bug) | | details | object\|null | 否 | 额外调试信息(如 http_status) | --- # 6. ErrorCode 完整说明 | code | exit_code | retryable | 典型触发场景 | |------|-----------|-----------|-------------| | INVALID_ARGS | 2 | false | 参数缺失/格式错误,如 --text 超过字符限制,--user-id 为空 | | AUTH_FAILED | 3 | false | cookies 过期、ct0 缺失、Twitter 返回 401 | | NETWORK | 4 | true | 连接超时、DNS 解析失败、代理不可达 | | RATE_LIMIT | 5 | true | Twitter 返回 429,触发频率限制 | | NOT_FOUND | 6 | false | 用户不存在、帖子已删除、404 响应 | | SERVER | 7 | true | Twitter 服务端 5xx 错误 | | DUPLICATE | 8 | false | 重复操作(如重复转发同一帖子) | | MEDIA_UPLOAD_FAILED | 9 | false | 图片格式不支持、超过大小限制、上传失败 | | CONTENT_VIOLATION | 10 | false | 内容被判定为垃圾内容或违规 | | DEPRECATED_COMMAND | 11 | false | 调用已废弃的子命令 | | CONFIG_PARSE_ERROR | 12 | false | ~/.x-api/config.toml 格式错误或字段缺失 | | UNKNOWN | 1 | false | 未分类的意外错误 | --- # 7. 退出码 ``` 0 成功 1 UNKNOWN 2 INVALID_ARGS 3 AUTH_FAILED 4 NETWORK 5 RATE_LIMIT 6 NOT_FOUND 7 SERVER 8 DUPLICATE 9 MEDIA_UPLOAD_FAILED 10 CONTENT_VIOLATION 11 DEPRECATED_COMMAND 12 CONFIG_PARSE_ERROR ``` --- # 8. 命令参考 ## 8.1 dm send — 发送单条私信 ``` twitter-cli [全局 flags] dm send \ --user-id \ --text \ [--media-id ] ``` | 参数 | 必填 | 说明 | |------|------|------| | --user-id | 是 | 目标用户数字 ID | | --text | 是 | 消息内容,最大 10000 字符 | | --media-id | 否 | 来自 upload image 的 media_id_string | 成功输出: ```json {"success":true,"data":{"user_id":"783214","event_id":"1811234567890123456","message":"Message sent successfully","media_id":null},"error":null,"meta":{"module":"dm","action":"send","elapsed_ms":519,"timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` 失败输出: ```json {"success":false,"data":null,"error":{"code":"NOT_FOUND","message":"用户 999999 不存在或无法接收私信","retryable":false,"retry_after_secs":null,"recovery_actions":["确认 user_id 是否正确","检查目标用户是否开放私信权限"]},"meta":{"module":"dm","action":"send","elapsed_ms":312,"timestamp":"2026-04-17T10:46:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` data 字段:user_id (string), event_id (string), message (string), media_id (string|null) --- ## 8.2 dm send-batch — 批量发送私信 ``` twitter-cli [全局 flags] dm send-batch \ --user-ids \ (--text | --texts-file ) \ [--media-id ] ``` | 参数 | 必填 | 说明 | |------|------|------| | --user-ids | 是 | 逗号分隔的用户 ID 列表 | | --text | 二选一 | 所有用户统一文案 | | --texts-file | 二选一 | 自定义文案文件(每行一条,行数 = user-ids 数量) | | --media-id | 否 | 所有用户共享的媒体 ID | texts-file 格式(每行一条,UTF-8): ``` 你好,张三!欢迎使用。 你好,李四!感谢支持。 你好,王五!祝使用愉快。 ``` 输出格式:BatchLine 三段式(见第 5.3 节),退出码以 summary 为准(任何失败不改变退出码 0,但 fail_count > 0 时建议检查 item 行)。 成功 header 行: ```json {"type":"header","command":"dm send-batch","schema_version":"1.0","expected_count":3,"meta":{"module":"dm","action":"send-batch","timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0"}} ``` 成功 summary 行: ```json {"type":"summary","total":3,"success_count":3,"fail_count":0,"elapsed_ms":1234} ``` --- ## 8.3 dm inbox — 查询收件箱 ``` twitter-cli [全局 flags] dm inbox [--max ] ``` | 参数 | 默认值 | 说明 | |------|--------|------| | --max | 20 | 返回最大条目数 | 成功输出: ```json {"success":true,"data":{"entries":[{"conversation_id":"783214-6253282","last_message_id":"1811234567890123456","last_message_text":"Hello from twitter-cli!","participants":["783214","6253282"],"unread_count":0}],"next_cursor":"1811234567890123300","entry_count":1},"error":null,"meta":{"module":"dm","action":"inbox","elapsed_ms":421,"timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` 失败输出: ```json {"success":false,"data":null,"error":{"code":"AUTH_FAILED","message":"Twitter 返回 401","retryable":false,"retry_after_secs":null,"recovery_actions":["重新从浏览器导出 cookies"]},"meta":{"module":"dm","action":"inbox","elapsed_ms":128,"timestamp":"2026-04-17T10:46:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` data 字段:entries (array), entries[].conversation_id (string), entries[].last_message_id (string), entries[].last_message_text (string), entries[].participants (string[]), entries[].unread_count (number), next_cursor (string|null), entry_count (number) --- ## 8.4 posts create — 发布帖子 ``` twitter-cli [全局 flags] posts create \ --text \ [--media-ids ] \ [--reply-to ] ``` | 参数 | 必填 | 说明 | |------|------|------| | --text | 是 | 帖子内容,最大 280 字符 | | --media-ids | 否 | 逗号分隔的媒体 ID(最多 4 张图片) | | --reply-to | 否 | 回复的帖子 ID | 成功输出: ```json {"success":true,"data":{"tweet_id":"1811234567890123456","text":"Hello from twitter-cli! #rust","author_id":"6253282","created_at":"2026-04-17T10:45:00Z","url":"https://x.com/i/status/1811234567890123456"},"error":null,"meta":{"module":"posts","action":"create","elapsed_ms":612,"timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` 失败输出: ```json {"success":false,"data":null,"error":{"code":"CONTENT_VIOLATION","message":"帖子内容被 Twitter 检测为垃圾内容或违规","retryable":false,"retry_after_secs":null,"recovery_actions":["修改帖子内容后重试","检查是否包含违禁词或链接"]},"meta":{"module":"posts","action":"create","elapsed_ms":389,"timestamp":"2026-04-17T10:46:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` data 字段:tweet_id (string), text (string), author_id (string), created_at (string ISO8601), url (string) --- ## 8.5 posts delete — 删除帖子 ``` twitter-cli [全局 flags] posts delete --id ``` 成功输出: ```json {"success":true,"data":{"tweet_id":"1811234567890123456","deleted":true},"error":null,"meta":{"module":"posts","action":"delete","elapsed_ms":298,"timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` 失败输出: ```json {"success":false,"data":null,"error":{"code":"NOT_FOUND","message":"帖子不存在或已被删除","retryable":false,"retry_after_secs":null,"recovery_actions":["确认帖子 ID 是否正确","确认帖子是否属于当前账号"]},"meta":{"module":"posts","action":"delete","elapsed_ms":201,"timestamp":"2026-04-17T10:46:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` data 字段:tweet_id (string), deleted (bool, 固定 true) --- ## 8.6 posts like — 点赞帖子 ``` twitter-cli [全局 flags] posts like --id ``` **幂等性**:重复点赞返回 success:true,不报错。 成功输出: ```json {"success":true,"data":{"tweet_id":"1811234567890123456","liked":true},"error":null,"meta":{"module":"posts","action":"like","elapsed_ms":215,"timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` 失败输出: ```json {"success":false,"data":null,"error":{"code":"NOT_FOUND","message":"帖子不存在或已删除","retryable":false,"retry_after_secs":null,"recovery_actions":["确认帖子 ID 是否正确"]},"meta":{"module":"posts","action":"like","elapsed_ms":187,"timestamp":"2026-04-17T10:46:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` data 字段:tweet_id (string), liked (bool, 固定 true) --- ## 8.7 posts unlike — 取消点赞 ``` twitter-cli [全局 flags] posts unlike --id ``` **幂等性**:重复取消点赞返回 success:true。 成功输出: ```json {"success":true,"data":{"tweet_id":"1811234567890123456","liked":false},"error":null,"meta":{"module":"posts","action":"unlike","elapsed_ms":231,"timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` 失败输出: ```json {"success":false,"data":null,"error":{"code":"NOT_FOUND","message":"帖子不存在或已删除","retryable":false,"retry_after_secs":null,"recovery_actions":["确认帖子 ID 是否正确"]},"meta":{"module":"posts","action":"unlike","elapsed_ms":198,"timestamp":"2026-04-17T10:46:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` data 字段:tweet_id (string), liked (bool, 固定 false) --- ## 8.8 posts retweet — 转发帖子 ``` twitter-cli [全局 flags] posts retweet --id ``` **注意**:重复转发返回 DUPLICATE 错误(退出码 8),与 like 行为不同。 成功输出: ```json {"success":true,"data":{"tweet_id":"1811234567890123456","retweet_id":"1811234567890123999","retweeted":true},"error":null,"meta":{"module":"posts","action":"retweet","elapsed_ms":334,"timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` 失败输出(重复转发): ```json {"success":false,"data":null,"error":{"code":"DUPLICATE","message":"已经转发过该帖子","retryable":false,"retry_after_secs":null,"recovery_actions":["如需取消转发,使用 posts unretweet 命令"]},"meta":{"module":"posts","action":"retweet","elapsed_ms":198,"timestamp":"2026-04-17T10:46:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` data 字段:tweet_id (string), retweet_id (string), retweeted (bool, 固定 true) --- ## 8.9 posts unretweet — 取消转发 ``` twitter-cli [全局 flags] posts unretweet --id ``` **注意**:`--id` 是原帖子 ID,不是转发产生的帖子 ID。 成功输出: ```json {"success":true,"data":{"tweet_id":"1811234567890123456","retweeted":false},"error":null,"meta":{"module":"posts","action":"unretweet","elapsed_ms":287,"timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` 失败输出: ```json {"success":false,"data":null,"error":{"code":"NOT_FOUND","message":"帖子不存在或已删除","retryable":false,"retry_after_secs":null,"recovery_actions":["确认帖子 ID 是否正确"]},"meta":{"module":"posts","action":"unretweet","elapsed_ms":234,"timestamp":"2026-04-17T10:46:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` data 字段:tweet_id (string), retweeted (bool, 固定 false) --- ## 8.10 user get — 通过用户名查询 ``` twitter-cli [全局 flags] user get --screen-name ``` screen-name 不含 @ 前缀(如 `twitter` 而非 `@twitter`)。 成功输出: ```json {"success":true,"data":{"id":"783214","name":"Twitter","screen_name":"twitter","description":"What's happening?!","followers_count":65000000,"following_count":0,"tweet_count":15234,"listed_count":89123,"verified":true,"protected":false,"created_at":"2007-02-20T14:35:54Z","profile_image_url":"https://pbs.twimg.com/profile_images/880136122604507136/xHrnqf1T_normal.jpg","profile_banner_url":"https://pbs.twimg.com/profile_banners/783214/1594913664","location":"127.0.0.1","url":"https://about.twitter.com/"},"error":null,"meta":{"module":"user","action":"get","elapsed_ms":342,"timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` 失败输出: ```json {"success":false,"data":null,"error":{"code":"NOT_FOUND","message":"用户 @nonexistent_xyz_abc 不存在或已停用","retryable":false,"retry_after_secs":null,"recovery_actions":["确认用户名拼写是否正确","检查用户是否已注销或被封禁"]},"meta":{"module":"user","action":"get","elapsed_ms":189,"timestamp":"2026-04-17T10:46:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` data 字段:id (string), name (string), screen_name (string), description (string), followers_count (number), following_count (number), tweet_count (number), listed_count (number), verified (bool), protected (bool), created_at (string ISO8601), profile_image_url (string|null), profile_banner_url (string|null), location (string|null), url (string|null) --- ## 8.11 user get-by-id — 通过 ID 查询 ``` twitter-cli [全局 flags] user get-by-id --rest-id ``` 返回结构与 `user get` 完全相同。用户 ID 永不变,适合长期追踪特定用户。 成功输出: ```json {"success":true,"data":{"id":"783214","name":"Twitter","screen_name":"twitter","description":"What's happening?!","followers_count":65000000,"following_count":0,"tweet_count":15234,"listed_count":89123,"verified":true,"protected":false,"created_at":"2007-02-20T14:35:54Z","profile_image_url":"https://pbs.twimg.com/profile_images/880136122604507136/xHrnqf1T_normal.jpg","profile_banner_url":"https://pbs.twimg.com/profile_banners/783214/1594913664","location":"127.0.0.1","url":"https://about.twitter.com/"},"error":null,"meta":{"module":"user","action":"get-by-id","elapsed_ms":287,"timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` 失败输出: ```json {"success":false,"data":null,"error":{"code":"NOT_FOUND","message":"用户 ID 999999999999999 不存在","retryable":false,"retry_after_secs":null,"recovery_actions":["确认用户 ID 是否正确"]},"meta":{"module":"user","action":"get-by-id","elapsed_ms":201,"timestamp":"2026-04-17T10:46:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` --- ## 8.12 search top — 搜索热门帖子 ``` twitter-cli [全局 flags] search top \ --query \ [--cursor ] ``` 按相关性和热度排序(对应 Twitter 的 "Top" 标签)。 | 参数 | 必填 | 说明 | |------|------|------| | --query | 是 | 搜索关键词,支持 Twitter 搜索语法 | | --cursor | 否 | 分页游标(来自上一页的 next_cursor) | 搜索语法示例: - 基础:`"rust programming"` - 指定用户:`"from:elonmusk"` - 时间范围:`"rust since:2026-01-01 until:2026-04-17"` - 组合:`"#rustlang has:media min_faves:100 lang:en"` 成功输出: ```json {"success":true,"data":{"tweets":[{"tweet_id":"1811234567890123456","text":"Rust is amazing! #rust","author_id":"6253282","author_name":"Rustacean","author_screen_name":"rustacean_dev","created_at":"2026-04-17T09:30:00Z","like_count":1523,"retweet_count":342,"reply_count":87,"view_count":45123,"lang":"en"}],"next_cursor":"DAABCgABGBi-NkIAAA","prev_cursor":null,"result_count":20},"error":null,"meta":{"module":"search","action":"top","elapsed_ms":687,"timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` 失败输出: ```json {"success":false,"data":null,"error":{"code":"RATE_LIMIT","message":"搜索 API 请求过于频繁,Twitter 返回 429","retryable":true,"retry_after_secs":900,"recovery_actions":["等待 retry_after_secs 秒后重试","降低搜索请求频率","避免并发运行多个搜索命令"]},"meta":{"module":"search","action":"top","elapsed_ms":145,"timestamp":"2026-04-17T10:46:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` data 字段:tweets (array), tweets[].tweet_id (string), tweets[].text (string), tweets[].author_id (string), tweets[].author_name (string), tweets[].author_screen_name (string), tweets[].created_at (string ISO8601), tweets[].like_count (number), tweets[].retweet_count (number), tweets[].reply_count (number), tweets[].view_count (number|null), tweets[].lang (string), next_cursor (string|null), prev_cursor (string|null), result_count (number) --- ## 8.13 search latest — 搜索最新帖子 ``` twitter-cli [全局 flags] search latest \ --query \ [--cursor ] ``` 按时间倒序排序(对应 Twitter 的 "Latest" 标签),适合实时监控。 参数、返回结构与 `search top` 完全相同,仅排序逻辑不同(时间倒序 vs 热度排序)。 成功输出: ```json {"success":true,"data":{"tweets":[{"tweet_id":"1811234567890123999","text":"Just published a new Rust blog post!","author_id":"1234567890","author_name":"Dev Guy","author_screen_name":"devguy","created_at":"2026-04-17T10:30:00Z","like_count":12,"retweet_count":3,"reply_count":1,"view_count":234,"lang":"en"}],"next_cursor":"DAABCgABGBi-NkIBBB","prev_cursor":null,"result_count":20},"error":null,"meta":{"module":"search","action":"latest","elapsed_ms":531,"timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` 失败输出(临时限流,返回 404): ```json {"success":false,"data":null,"error":{"code":"NOT_FOUND","message":"搜索 API 返回 404,可能是临时频率限制","retryable":true,"retry_after_secs":30,"recovery_actions":["等待 30 秒后单独重试此命令","避免并发运行多个搜索"]}} ``` **频率限制警告**:搜索 API 对并发敏感,不要同时运行多个 search 命令。 --- ## 8.14 upload image — 上传图片 ``` twitter-cli [全局 flags] upload image \ --file \ [--category tweet-image|dm-image|banner-image] ``` | 参数 | 必填 | 默认 | 说明 | |------|------|------|------| | --file | 是 | — | 本地图片路径(JPEG/PNG/GIF/WebP) | | --category | 否 | tweet-image | 媒体类别 | category 说明: - `tweet-image`:用于 posts create --media-ids - `dm-image`:用于 dm send --media-id - `banner-image`:用于用户 Banner(Python API) 大小限制:JPEG/PNG/WebP 最大 5MB;GIF 最大 15MB。 成功输出: ```json {"success":true,"data":{"media_id":1811234567890123456,"media_id_string":"1811234567890123456","size":245678,"image":{"image_type":"image/jpeg","w":1920,"h":1080},"expires_after_secs":86400},"error":null,"meta":{"module":"upload","action":"image","elapsed_ms":1234,"timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` 失败输出(文件过大): ```json {"success":false,"data":null,"error":{"code":"MEDIA_UPLOAD_FAILED","message":"图片文件超过大小限制(最大 5MB)","retryable":false,"retry_after_secs":null,"recovery_actions":["压缩图片后重试","使用 ImageMagick: convert input.jpg -resize 1920x1080\\> -quality 85 output.jpg"],"details":{"file_size":8234567,"max_size":5242880}},"meta":{"module":"upload","action":"image","elapsed_ms":89,"timestamp":"2026-04-17T10:46:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` data 字段:media_id (number, 不推荐), **media_id_string** (string, 推荐使用), size (number bytes), image.image_type (string MIME), image.w (number), image.h (number), expires_after_secs (number, 媒体 ID 有效期) **重要**:始终使用 `media_id_string` 而非 `media_id`(大整数可能有精度损失)。 --- ## 8.15 settings get — 查询账号设置 ``` twitter-cli [全局 flags] settings get ``` 无需额外参数。合并查询 sensitive 设置和 account 设置,通过两次 API 请求完成。 成功输出: ```json {"success":true,"data":{"sensitive":{"display_sensitive_media":false,"nsfw_user":false,"opt_in_filtering":true,"opt_in_blocking":true},"account":{"screen_name":"myaccount","language":"zh-cn","time_zone":{"name":"Asia/Shanghai","utc_offset":28800,"tzinfo_name":"Asia/Shanghai"},"sleep_time":{"enabled":false,"start_time":null,"end_time":null},"allow_dm_groups":true,"allow_dms_from":"following","protected":false,"geo_enabled":false}},"error":null,"meta":{"module":"settings","action":"get","elapsed_ms":428,"timestamp":"2026-04-17T10:45:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` 失败输出: ```json {"success":false,"data":null,"error":{"code":"AUTH_FAILED","message":"Twitter 返回 401","retryable":false,"retry_after_secs":null,"recovery_actions":["重新从浏览器导出 cookies"]},"meta":{"module":"settings","action":"get","elapsed_ms":132,"timestamp":"2026-04-17T10:46:00Z","cli_version":"1.0.0","schema_version":"1.0"}} ``` data.sensitive 字段:display_sensitive_media (bool), nsfw_user (bool), opt_in_filtering (bool), opt_in_blocking (bool) data.account 字段:screen_name (string), language (string), time_zone.name (string), time_zone.utc_offset (number), time_zone.tzinfo_name (string), sleep_time.enabled (bool), sleep_time.start_time (number|null), sleep_time.end_time (number|null), allow_dm_groups (bool), allow_dms_from (string: "following"|"verified"|"everyone"), protected (bool), geo_enabled (bool) --- # 9. 常见错误及修复 ## AUTH_FAILED(退出码 3) **现象**:Twitter 返回 401,cookies 认证失败。 **修复**: 1. 重新从浏览器导出 cookies(登录 twitter.com → F12 → Network → Cookie 头) 2. 确认 cookies 包含 `ct0=` 和 `auth_token=` 字段 3. 检查文件权限:`chmod 600 cookies.txt` ## RATE_LIMIT(退出码 5) **现象**:Twitter 返回 429。 **修复**: 1. 读取 `error.retry_after_secs`,等待对应秒数后重试 2. 降低请求频率(批量操作间添加延迟) 3. 搜索 API 尤其敏感:不要并发运行多个 search 命令 ```bash # 读取 retry_after 并等待 retry_after=$(echo "$result" | jq '.error.retry_after_secs // 60') sleep "$retry_after" ``` ## NETWORK(退出码 4) **现象**:连接超时、DNS 失败。 **修复**: 1. 检查网络连接 2. 尝试添加 `--proxy http://127.0.0.1:8080` 3. 增加超时:`--request-timeout-secs 60` ## CONTENT_VIOLATION(退出码 10) **现象**:帖子/私信内容被 Twitter 判定为违规。 **修复**: 1. 修改内容,避免包含违禁词、垃圾链接 2. 避免重复发布完全相同的内容(DUPLICATE,退出码 8) ## CONFIG_PARSE_ERROR(退出码 12) **现象**:config.toml 格式错误。 **修复**:检查 TOML 语法,确认必要字段: ```toml [profile.default] cookies_file = "/absolute/path/to/cookies.txt" ``` --- # 10. AI agent 集成示例 ## Python 解析单条命令 ```python import json import subprocess def run_cli(*args): result = subprocess.run( ["twitter-cli"] + list(args), capture_output=True, text=True ) envelope = json.loads(result.stdout.strip()) return envelope, result.returncode envelope, code = run_cli( "--cookies-file", "cookies.txt", "user", "get", "--screen-name", "twitter" ) if envelope["success"]: user = envelope["data"] print(f"ID: {user['id']}, followers: {user['followers_count']}") else: error = envelope["error"] if error["retryable"]: wait = error.get("retry_after_secs") or 30 time.sleep(wait) # 重试... else: raise Exception(f"不可重试错误: {error['code']}: {error['message']}") ``` ## Python 解析批量命令 ```python import json import subprocess proc = subprocess.Popen( ["twitter-cli", "--cookies-file", "cookies.txt", "dm", "send-batch", "--user-ids", "783214,6253282", "--text", "Hello batch!"], stdout=subprocess.PIPE, text=True ) items = [] summary = None for line in proc.stdout: obj = json.loads(line.strip()) t = obj.get("type") if t == "header": print(f"预期处理 {obj['expected_count']} 个用户") elif t == "item": items.append(obj) if obj["success"]: print(f" [{obj['index']}] 成功: {obj['data']['event_id']}") else: print(f" [{obj['index']}] 失败: {obj['error']['code']}") elif t == "summary": summary = obj proc.wait() print(f"完成:{summary['success_count']}/{summary['total']} 成功") # 提取失败的索引用于重试 failed = [item for item in items if not item["success"]] ``` ## Shell 脚本模式 ```bash #!/bin/bash set -euo pipefail COOKIES="$HOME/.x-api/cookies.txt" # 运行命令并检查退出码 run_and_check() { local output output=$(twitter-cli --cookies-file "$COOKIES" "$@" 2>/dev/null) local code=$? case $code in 0) echo "$output" ;; 3) echo "AUTH ERROR: 请更新 cookies" >&2; exit 3 ;; 5) local wait wait=$(echo "$output" | jq '.error.retry_after_secs // 60') echo "RATE LIMIT: 等待 ${wait}s..." >&2 sleep "$wait" run_and_check "$@" # 递归重试(注意:生产环境需限制重试次数) ;; *) echo "ERROR $code: $(echo "$output" | jq -r '.error.message')" >&2; exit "$code" ;; esac } # 使用示例 result=$(run_and_check user get --screen-name twitter) echo "$result" | jq '.data.id' ``` --- # 11. schema_version 版本化规则 | 变更类型 | 版本变化 | 向后兼容 | |----------|----------|----------| | 新增可选字段 | Minor 升级(1.0 → 1.1) | 是 | | 修改字段名或类型 | Major 升级(1.0 → 2.0) | 否 | | 删除已有字段 | Major 升级 | 否 | AI agent 应检查 Major 版本: ```python schema_ver = envelope["meta"]["schema_version"] major = int(schema_ver.split(".")[0]) assert major == 1, f"不支持的 schema_version: {schema_ver}" ``` --- # 12. 命令速查表 | 命令 | 必填参数 | 输出格式 | |------|---------|---------| | `dm send` | --user-id, --text | Envelope | | `dm send-batch` | --user-ids, --text 或 --texts-file | BatchLine | | `dm inbox` | — | Envelope | | `posts create` | --text | Envelope | | `posts delete` | --id | Envelope | | `posts like` | --id | Envelope | | `posts unlike` | --id | Envelope | | `posts retweet` | --id | Envelope | | `posts unretweet` | --id | Envelope | | `user get` | --screen-name | Envelope | | `user get-by-id` | --rest-id | Envelope | | `search top` | --query | Envelope | | `search latest` | --query | Envelope | | `upload image` | --file | Envelope | | `settings get` | — | Envelope |