如何用 TwexAPI 发送 XChat DM
发送私信是写入动作,所以最重要的不是用哪个 HTTP 客户端,而是发送前后的流程:这条消息是谁审批的、由哪个账号发送、目标是否当前可达、发送后返回的 message ID 存在哪里。
TwexAPI 的 Send DM (XChat v3) 端点通过 XChat encrypted chat API 发送私信。recipient 可以是一对一用户的 user ID 或 @handle,也可以是已有群组会话的 g... group ID。接口还支持通过 media_urls 附加一张图片,或通过 video_url 附加一个视频。
适合用于已审批的客服跟进、创作者运营、合作沟通或 CRM 任务。不要围绕它做盲发式批量私信循环。
端点和请求字段
当你的应用已经有一条审批通过的消息准备发送时,使用 POST /v3/twitter/send-dm。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
recipient | string | 是 | X user ID、@handle,或以 g... 开头的已有 group ID。 |
text | string | 是 | 私信正文。发送前应先校验。 |
cookie | string | 是 | X 认证 cookie 或 auth_token。只应保存在服务端。 |
media_urls | string array 或 null | 否 | 一张图片的公开 URL。最多 1 个,不能和 video_url 同时使用。 |
video_url | string 或 null | 否 | 一个视频的公开 http(s) URL。不能和 media_urls 同时使用。 |
pin | string 或 null | 否 | Identity PIN。API 默认值是 1234;如果你配置了自己的 PIN,应从安全环境变量读取。 |
proxy | string 或 null | 否 | 可选的发送会话 HTTP proxy。 |
文档标注该端点为 $0.0025 每次调用。它只是调用成本,不代表可以跳过审核或可达性判断。
基础请求
curl --request POST \
--url https://api.twexapi.io/v3/twitter/send-dm \
--header "Authorization: Bearer $TWEXAPI_BEARER_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"recipient": "@example_user",
"text": "你好,感谢你反馈导出问题。我们已经发布修复,可以帮你确认最新任务是否正常。",
"cookie": "auth_token=...; ct0=...; twid=...",
"pin": "1234"
}'成功响应会在 data 中返回 message object。
1{
2 "code": 200,
3 "msg": "success",
4 "data": {
5 "id": "1234567890123456789",
6 "text": "你好,感谢你反馈导出问题。",
7 "time": "2024-01-01T12:00:00Z",
8 "attachment": null,
9 "attachments": null,
10 "sequence_id": "2076661179334979585",
11 "sender_id": "1234567890123456789",
12 "recipient_id": "9876543210987654321"
13 }
14}至少保存 data.id、sequence_id、sender_id、recipient_id、time 和原始响应。这些字段能把 CRM 动作、客服工单和后续会话记录对齐。
带审批状态的 Python 客户端
下面的脚本从环境变量读取凭据,先校验请求,并且只有草稿状态为 approved 时才会发送。
1import os
2from datetime import datetime, timezone
3from typing import Any
4
5import requests
6
7API_URL = "https://api.twexapi.io/v3/twitter/send-dm"
8BEARER_TOKEN = os.environ["TWEXAPI_BEARER_TOKEN"]
9X_COOKIE = os.environ["TWEXAPI_X_COOKIE"]
10XCHAT_PIN = os.environ.get("TWEXAPI_XCHAT_PIN", "1234")
11PROXY = os.environ.get("TWEXAPI_PROXY")
12
13def validate_dm_payload(
14 recipient: str,
15 text: str,
16 *,
17 media_urls: list[str] | None = None,
18 video_url: str | None = None,
19) -> None:
20 if not recipient or not recipient.strip():
21 raise ValueError("recipient is required")
22 if not text or not text.strip():
23 raise ValueError("text is required")
24 if media_urls and video_url:
25 raise ValueError("media_urls and video_url are mutually exclusive")
26 if media_urls and len(media_urls) > 1:
27 raise ValueError("media_urls supports at most one image")
28
29def send_xchat_dm(
30 recipient: str,
31 text: str,
32 *,
33 media_urls: list[str] | None = None,
34 video_url: str | None = None,
35) -> dict[str, Any]:
36 validate_dm_payload(
37 recipient,
38 text,
39 media_urls=media_urls,
40 video_url=video_url,
41 )
42
43 payload: dict[str, Any] = {
44 "recipient": recipient.strip(),
45 "text": text.strip(),
46 "cookie": X_COOKIE,
47 "pin": XCHAT_PIN,
48 }
49
50 if media_urls:
51 payload["media_urls"] = media_urls
52 if video_url:
53 payload["video_url"] = video_url
54 if PROXY:
55 payload["proxy"] = PROXY
56
57 response = requests.post(
58 API_URL,
59 headers={
60 "Authorization": f"Bearer {BEARER_TOKEN}",
61 "Content-Type": "application/json",
62 "Accept": "application/json",
63 },
64 json=payload,
65 timeout=60,
66 )
67 response.raise_for_status()
68 data = response.json()
69
70 if data.get("code") != 200:
71 raise RuntimeError(data.get("msg", "TwexAPI returned an error"))
72
73 return data
74
75def normalize_send_result(
76 draft: dict[str, Any],
77 response: dict[str, Any],
78) -> dict[str, Any]:
79 message = response.get("data") or {}
80 return {
81 "draft_id": draft["draft_id"],
82 "recipient": draft["recipient"],
83 "message_id": message.get("id"),
84 "sequence_id": message.get("sequence_id"),
85 "sender_id": message.get("sender_id"),
86 "recipient_id": message.get("recipient_id"),
87 "sent_at": message.get("time") or datetime.now(timezone.utc).isoformat(),
88 "api_code": response.get("code"),
89 "api_msg": response.get("msg"),
90 "raw_response": response,
91 }
92
93if __name__ == "__main__":
94 draft = {
95 "draft_id": "support-followup-1042",
96 "recipient": "@example_user",
97 "text": "你好,感谢你反馈导出问题。我们已经发布修复,可以帮你确认最新任务是否正常。",
98 "status": "approved",
99 }
100
101 if draft["status"] != "approved":
102 raise ValueError("DM draft must be approved before sending")
103
104 result = send_xchat_dm(draft["recipient"], draft["text"])
105 print(normalize_send_result(draft, result))在服务端或可信工作站运行:
TWEXAPI_BEARER_TOKEN="your_twexapi_bearer_token" \
TWEXAPI_X_COOKIE="auth_token=...; ct0=...; twid=..." \
TWEXAPI_XCHAT_PIN="1234" \
python send_xchat_dm.py不要在浏览器或前端应用里调用这个接口。请求里包含账号凭据,而且会真的发送消息。
发送图片或视频
XChat v3 同一条 DM 支持一张图片或一个视频,两类媒体字段互斥。
image_result = send_xchat_dm(
"@example_user",
"这是刚才提到的截图。",
media_urls=["https://example.com/screenshot.jpg"],
)video_result = send_xchat_dm(
"@example_user",
"这里是一段简短演示。",
video_url="https://example.com/walkthrough.mp4",
)媒体 URL 需要是公开且稳定的。如果你的存储使用会过期的签名 URL,建议在发送前即时生成,并且日志里只记录脱敏版本。
放在可达性检查之后
更稳的流程是:先检查可达性,再审批消息,发送后回写结果。
- 预检:需要可达性快照时,先对用户名或 user ID 调用
POST /v2/dm/status。 - 草稿:保存
recipient、消息正文、附件选择、发送账号、来源流程和负责人。 - 审批:在调用写入端点前,要求人工或规则状态明确通过。
- 发送:对已审批草稿调用一次
POST /v3/twitter/send-dm。 - 记录:保存
message_id、sequence_id、sender、recipient、发送时间和原始响应。 - 跟进:把保存的 ID 关联到工单、CRM 联系人或 campaign 记录。
这样每条消息都有可解释的发送原因。失败时也更容易判断问题来自目标、正文、媒体、cookie 还是平台状态。
保存 DM 状态
生产环境里,建议把消息记录和凭据存储分开。
| 字段 | 作用 |
|---|---|
dm_draft_id | 把已发送消息关联回已审批草稿。 |
recipient_input | 保留原始 @handle、user ID 或 group ID。 |
sender_account_id | 标识哪个 X 账号发送了消息。 |
message_text | 保存已审批的正文。 |
attachment_kind | 区分无媒体、图片或视频。 |
message_id | 返回的 XChat message ID。 |
sequence_id | 后续会话分页时可能用到。 |
sent_at | 支持 CRM、客服和审计时间线。 |
status | 跟踪 approved、sent、failed、needs_review。 |
raw_response | 保存原始 API 证据。 |
不要在消息表里保存完整 cookie。保存密钥引用或账号凭据 ID 即可。
常见错误
- 未确认目标是否可达就直接发送。
- 把
@handle当成永久身份;可用时应保存返回的recipient_id。 - 同时传入
media_urls和video_url。 - 在
media_urls里传入超过一张图片。 - 在日志里打印账号 cookie、auth token 或完整签名媒体 URL。
- 未确认消息是否已发出,就对未知失败自动重试。
遇到未知 timeout 时,应先检查会话状态再决定是否重试。第二次发送可能造成重复消息。
总结
POST /v3/twitter/send-dm 是 TwexAPI 用于发送 XChat v3 私信的端点,支持一对一目标和已有群组。生产实现的关键很清楚:必要时先检查可达性,审批草稿,只发送一次,保存返回的 message object,并把不确定失败交给复核。
这样 DM 自动化能保留效率,但不会变成不可控的私信发送器。