快速开始¶
从零到发出第一条推文。
1. 启动 Go server¶
docker run -d --name go-twitter-api \
-p 127.0.0.1:50051:50051 \
-e TWITTER_GRPC_ADDR=0.0.0.0:50051 \
-e TWITTER_GRPC_API_KEY=<至少 32 字符的随机串> \
ghcr.io/robin528919/go-twitter-api:0.2.2
生产部署用 docker-compose-pro.yml(只读根文件系统、丢弃全部 capability、健康检查)。
镜像为私有,拉取前需在服务器执行一次 docker login ghcr.io。
跨主机必须加密
默认只绑 127.0.0.1。跨主机暴露 gRPC 必须同时配置 TLS 与 ≥32 字符 API key——
请求里流动的是明文账号凭据与会话 token。
2. 安装 Python SDK¶
三件产物见 Python SDK 下载。SDK 版本必须与 server 镜像版本一致。
3. 登录并持久化状态¶
import asyncio
from twitter_sdk import x, dump_account_state, load_account_state
async def first_login() -> bytes:
async with x.android.AndroidClient("127.0.0.1:50051", api_key=API_KEY) as client:
state = await client.login(
username="your-account",
password="your-password",
totp="123456", # 无 2FA 时省略
)
# ★ 状态含设备身份,必须持久化并加密存储
return dump_account_state(state)
blob = asyncio.run(first_login())
登录会真实消耗账号的登录频次并可能触发风控。同一账号不要并发登录,也不要在每次操作前 重新登录——保存下来的状态可以长期复用。
4. 用已保存的状态调用业务接口¶
async def post_once(blob: bytes) -> bytes:
state = load_account_state(blob)
async with x.android.AndroidClient("127.0.0.1:50051", api_key=API_KEY) as client:
acct = client.account(state)
tweet = await acct.posts.create("hello from go-twitter-api")
print("tweet_id =", tweet.tweet_id)
await acct.posts.delete(tweet.tweet_id)
# ★ 每次 RPC 后状态可能刷新,保存最新的
return dump_account_state(acct.state)
5. 带图发帖¶
media_id = await acct.media.upload(image_bytes, mime_type="image/jpeg")
tweet = await acct.posts.create("with picture", media_ids=[media_id])
media_id 有有效期,拿到后应尽快用于发帖。
调用方必须自己做的事¶
| 事项 | 为什么不在 SDK 里 |
|---|---|
加密存储 AccountState |
其中含明文 token 与 secret,存储方案取决于你的基础设施 |
| 同账号互斥 | 并发用同一状态调用会导致状态覆盖与风控命中 |
| 重试与幂等 | 非幂等写操作(发帖、回复、引用、上传)重试前必须先确认上次是否已生效 |
| 频率控制 | 平台对高频操作实施风控,合适的节奏取决于账号权重与业务形态 |
各 RPC 的幂等性标注见接口验证状态。
出错了怎么读¶
服务端把上游失败分类成 gRPC 状态码,details 里只有结构化摘要,不含上游响应正文
(避免 token 与 cookie 泄漏到客户端日志)。完整上游原文在 server 端日志里。
| gRPC 状态码 | 常见含义 |
|---|---|
UNAUTHENTICATED |
会话失效,需要重新登录 |
FAILED_PRECONDITION |
缺少必要的设备证明材料或前置状态 |
RESOURCE_EXHAUSTED |
触发平台频控,退避后再试 |
INVALID_ARGUMENT |
入参不合法(本地校验拦下,未发出请求) |
UNIMPLEMENTED |
调用了尚未实现的接口 |