TwexAPI で XChat DM メディアを取得する方法
DM record は text だけでは足りないことがあります。Support case では screenshot が証拠になり、creator review では video clip が届き、compliance export では file がどの conversation から来たかを説明する必要があります。
TwexAPI の Get DM Media (XChat v3) endpoint は、1 つの XChat attachment を playable media URL に解決します。入力は Get DM History (XChat v3) が返す attachments から取得します。message の conversation_id、attachment の media_hash_key、任意ですが推奨される key_version を使います。
この media endpoint は conversation を検索しません。先に conversation history を取得し、attachment metadata を保存し、必要な attachment に対して POST /v3/twitter/dm-media を呼びます。
Endpoint と Request Fields
1 つの XChat media attachment を取得するには POST /v3/twitter/dm-media を使います。
| Field | Type | Required | Notes |
|---|---|---|---|
conversation_id | string | Yes | /v3/twitter/dm-history から得た conversation ID。 |
media_hash_key | string | Yes | DM history attachment object の media_hash_key。 |
cookie | string | Yes | X authentication cookie または auth_token。server-side に保管します。 |
key_version | string or null | No | DM history から得た message key version。ある場合は渡すことを推奨。 |
type_name | string or null | No | IMAGE、VIDEO、GIF などの optional hint。 |
filename | string or null | No | Attachment object の optional filename hint。 |
pin | string or null | No | Identity PIN。API default は 1234。 |
proxy | string or null | No | Optional HTTP proxy。 |
Basic Request
1curl --request POST \
2 --url https://api.twexapi.io/v3/twitter/dm-media \
3 --header "Authorization: Bearer $TWEXAPI_BEARER_TOKEN" \
4 --header "Content-Type: application/json" \
5 --data '{
6 "conversation_id": "1928096185664843776:1989842918455291904",
7 "media_hash_key": "blRIlZCVIO",
8 "key_version": "3",
9 "type_name": "VIDEO",
10 "filename": "clip.mp4",
11 "cookie": "auth_token=...; ct0=...; twid=...",
12 "pin": "1234"
13 }'response は playable URL と file metadata を返します。
1{
2 "code": 200,
3 "msg": "success",
4 "data": {
5 "url": "https://api.twexapi.io/media/xchat/...",
6 "path": "/media/xchat/...",
7 "content_type": "video/mp4",
8 "filename": "clip.mp4",
9 "byte_length": 421299,
10 "cached": true
11 }
12}返却 metadata は元の message と attachment record に紐づけて保存します。Long-term retention が必要な product では、通常の privacy と retention rule に従い、返却された url から自社 storage に保存します。
Attachment Input From DM History
/v3/twitter/dm-history は message 上に attachments を返すことがあります。これらの fields が /v3/twitter/dm-media の入力になります。
1{
2 "conversation_id": "1928096185664843776:1989842918455291904",
3 "messages": [
4 {
5 "id": "1234567890123456789",
6 "text": "Here is the video.",
7 "sequence_id": "2076661179334979585",
8 "attachments": [
9 {
10 "attachment_id": "cda5ccdd-af53-48a3-bdd1-5039a73aea25",
11 "conversation_id": "1928096185664843776:1989842918455291904",
12 "filename": "clip.mp4",
13 "filesize_bytes": 421299,
14 "key_version": "3",
15 "media_hash_key": "blRIlZCVIO",
16 "type": "video",
17 "type_name": "VIDEO"
18 }
19 ]
20 }
21 ]
22}Attachment object に conversation_id がない場合は、page-level の conversation_id を使います。
Python Media Resolver
この script は保存済み DM history attachment object を受け取り、playable URL を解決し、message と一緒に保存できる normalized row を返します。
1import os
2from datetime import datetime, timezone
3from typing import Any
4
5import requests
6
7API_URL = "https://api.twexapi.io/v3/twitter/dm-media"
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 build_media_payload(
14 conversation_id: str,
15 attachment: dict[str, Any],
16) -> dict[str, Any]:
17 media_hash_key = attachment.get("media_hash_key")
18 if not media_hash_key:
19 raise ValueError("attachment.media_hash_key is required")
20
21 payload: dict[str, Any] = {
22 "conversation_id": attachment.get("conversation_id") or conversation_id,
23 "media_hash_key": media_hash_key,
24 "cookie": X_COOKIE,
25 "pin": XCHAT_PIN,
26 }
27
28 for field in ("key_version", "type_name", "filename"):
29 if attachment.get(field):
30 payload[field] = attachment[field]
31
32 if PROXY:
33 payload["proxy"] = PROXY
34
35 return payload
36
37def fetch_dm_media(
38 conversation_id: str,
39 attachment: dict[str, Any],
40) -> dict[str, Any]:
41 response = requests.post(
42 API_URL,
43 headers={
44 "Authorization": f"Bearer {BEARER_TOKEN}",
45 "Content-Type": "application/json",
46 "Accept": "application/json",
47 },
48 json=build_media_payload(conversation_id, attachment),
49 timeout=60,
50 )
51 response.raise_for_status()
52 data = response.json()
53
54 if data.get("code") != 200:
55 raise RuntimeError(data.get("msg", "TwexAPI returned an error"))
56
57 return data["data"]
58
59def normalize_media_result(
60 *,
61 conversation_id: str,
62 message_id: str,
63 attachment: dict[str, Any],
64 media: dict[str, Any],
65) -> dict[str, Any]:
66 return {
67 "conversation_id": conversation_id,
68 "message_id": message_id,
69 "attachment_id": attachment.get("attachment_id"),
70 "media_hash_key": attachment.get("media_hash_key"),
71 "key_version": attachment.get("key_version"),
72 "type_name": attachment.get("type_name"),
73 "source_filename": attachment.get("filename"),
74 "playable_url": media.get("url"),
75 "api_path": media.get("path"),
76 "content_type": media.get("content_type"),
77 "resolved_filename": media.get("filename"),
78 "byte_length": media.get("byte_length"),
79 "cached": media.get("cached"),
80 "resolved_at": datetime.now(timezone.utc).isoformat(),
81 "raw_attachment": attachment,
82 "raw_media": media,
83 }
84
85if __name__ == "__main__":
86 conversation_id = "1928096185664843776:1989842918455291904"
87 message_id = "1234567890123456789"
88 attachment = {
89 "attachment_id": "cda5ccdd-af53-48a3-bdd1-5039a73aea25",
90 "filename": "clip.mp4",
91 "key_version": "3",
92 "media_hash_key": "blRIlZCVIO",
93 "type_name": "VIDEO",
94 }
95
96 media = fetch_dm_media(conversation_id, attachment)
97 print(
98 normalize_media_result(
99 conversation_id=conversation_id,
100 message_id=message_id,
101 attachment=attachment,
102 media=media,
103 )
104 )Trusted server environment に TWEXAPI_BEARER_TOKEN、TWEXAPI_X_COOKIE、TWEXAPI_XCHAT_PIN を設定してから実行します。
python resolve_xchat_dm_media.pyBrowser からこの endpoint を呼ばないでください。Request には X cookie が必要で、その credential は server-side に置くべきです。
Media Export Workflow
- Read history:
POST /v3/twitter/dm-historyを呼び、messages とattachmentsを保存します。 - Queue attachments:
media_hash_keyを持つ attachment ごとに media job を作ります。 - Resolve media:
POST /v3/twitter/dm-mediaにconversation_id、media_hash_key、利用可能なkey_versionを渡します。 - Store metadata:
url、path、content_type、filename、byte_length、cached、raw response を保存します。 - Archive carefully: File を download する場合は、自社 bucket に access control と retention rule を設定して保存します。
この流れなら、すべての file を conversation、message、attachment、API response まで追跡できます。
Fields To Store
| Field | Why it matters |
|---|---|
conversation_id | Media を元の XChat conversation に結びつけます。 |
message_id | File を含んでいた message に結びつけます。 |
attachment_id | ある場合は attachment-level deduplication に使います。 |
media_hash_key | Media 解決に必要な input。 |
key_version | DM history 由来の推奨 input。 |
type_name | Image、video、GIF の routing に使います。 |
url | Media endpoint が返す playable URL。 |
path | URL と一緒に返る API media path。 |
content_type | Rendering と storage decision に使う MIME type。 |
filename | Suggested filename。 |
byte_length | File size in bytes。 |
cached | Media がすでに available だったかを示します。 |
| Raw attachment and response JSON | Schema が変わったときに reprocess できます。 |
Full account cookie を media record に保存しないでください。Credential reference だけを保存します。
Common Mistakes
/v3/twitter/dm-historyの attachment metadata を保存せずに/v3/twitter/dm-mediaを呼ぶ。- Message
idやsequence_idをmedia_hash_keyとして渡す。 - Attachment に
key_versionがあるのに request に含めない。 - Cookie、playable URL、downloaded file を audit できない場所に log する。
- Retention が必要な場面で media URL だけを archive として扱う。
Wrap-up
dm-history で attachment を見つけた後に、POST /v3/twitter/dm-media を呼びます。conversation_id、media_hash_key、利用可能な key_version を渡し、返却された playable URL と media metadata を元の message record に保存します。