Files
2026-08-27 18:32:03 +08:00

139 lines
6.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import json
import random
import uuid
from .static import Request_pb2 as RequestProto
from .dy_util import (
generate_webid,
generate_req_sign,
generate_millisecond,
normalize_client_cert,
DEFAULT_USER_AGENT,
)
def _ua_headers(auth) -> tuple[str, str]:
ua = getattr(auth, "user_agent", None) or DEFAULT_USER_AGENT
browser_version = ua.split("Mozilla/", 1)[-1] if "Mozilla/" in ua else ua
return ua, browser_version
class ProtoBuilder:
@staticmethod
def build_normal_request(auth, cmd):
ua, browser_version = _ua_headers(auth)
request = RequestProto.Request()
request.cmd = cmd
request.sequence_id = random.randint(10000, 11000)
request.sdk_version = "1.1.3"
request.token = auth.ticket if auth.ticket else ""
request.refer = 3
request.inbox_type = 0
request.build_number = "5fa6ff1:Detached: 5fa6ff1111fd53aafc4c753505d3c93daad74d27"
did = str(getattr(auth, "device_id", "") or "0")
request.device_id = did
request.device_platform = 'douyin_pc'
request.headers['session_aid'] = '6383'
request.headers['session_did'] = did
request.headers['app_name'] = 'douyin_pc'
request.headers['priority_region'] = 'cn'
request.headers['user_agent'] = ua
request.headers['cookie_enabled'] = 'true'
request.headers['browser_language'] = 'zh-CN'
request.headers['browser_platform'] = 'Win32'
request.headers['browser_name'] = 'Mozilla'
request.headers['browser_version'] = browser_version
request.headers['browser_online'] = 'true'
request.headers['screen_width'] = '1707'
request.headers['screen_height'] = '960'
request.headers['referer'] = ''
request.headers['timezone_name'] = 'Etc/GMT-8'
request.headers['deviceId'] = did
request.headers['webid'] = generate_webid(auth)
request.headers['fp'] = auth.cookie.get('s_v_web_id', '') if auth.cookie else ''
request.headers['is-retry'] = '0'
request.auth_type = 4
request.biz = 'douyin_web'
request.access = 'web_sdk'
request.ts_sign = auth.ts_sign if auth.ts_sign else ""
request.sdk_cert = normalize_client_cert(auth.client_cert or "")
return request
@staticmethod
def build_read_request(auth, cmd):
"""读接口(收件箱/会话消息)的 Request 信封。
Request.token 必须是 x_tt_token cookie。build_normal_request 填的是
auth.ticketbd-ticket-guard 票据):长度合法,服务端照样回
status_code=0 "OK",但把调用方当成匿名用户,body 恒为空——和「收件箱
里没有消息」完全无法区分。实测同一请求只换 token:
auth.ticket -> 73 字节 0 条;x_tt_token -> 113KB 47 条。
发送接口另有签名,沿用 build_normal_request,不在此处改动。
"""
request = ProtoBuilder.build_normal_request(auth, cmd)
cookies = getattr(auth, "cookie", None) or {}
token = str(cookies.get("x_tt_token") or "").strip()
if token:
request.token = token
return request
@staticmethod
def build_create_conversation_request(auth, toId, myId):
request = ProtoBuilder.build_normal_request(auth, 609)
request.body.create_conversation_v2_body.conversation_type = 1
request.body.create_conversation_v2_body.participants.extend([int(toId), int(myId)])
reuqest_sign = generate_req_sign({
"sign_data": f"avatar_url=&idempotent_id=&name=&participants={toId},{myId}",
"certType": "cookie",
"scene": "web_protect"
}, auth.private_key)
request.reuqest_sign = reuqest_sign
return request
@staticmethod
def build_get_conversation_list_info_request(auth, toId, myId, conversation_short_id):
request = ProtoBuilder.build_normal_request(auth, 610)
request.body.get_conversation_info_list_v2_body.data.conversation_id = f"0:1:{myId}:{toId}"
request.body.get_conversation_info_list_v2_body.data.conversation_short_id = int(conversation_short_id)
request.body.get_conversation_info_list_v2_body.data.conversation_type = 1
return request
@staticmethod
def build_send_message_request(
auth,
conversation_id,
conversation_short_id,
ticket,
msg_content,
message_type: int = 7,
):
client_message_id = str(uuid.uuid4())
request = ProtoBuilder.build_normal_request(auth, 100)
request.body.send_message_body.conversation_id = conversation_id
request.body.send_message_body.conversation_type = 1
request.body.send_message_body.conversation_short_id = int(conversation_short_id)
request.body.send_message_body.content = json.dumps(msg_content, ensure_ascii=False,
separators=(',', ':'))
request.body.send_message_body.ext.append(
RequestProto.ExtValue(key='s:client_message_id', value=client_message_id)
)
request.body.send_message_body.ext.append(
RequestProto.ExtValue(key='s:stime', value=str(generate_millisecond()))
)
request.body.send_message_body.ext.append(
RequestProto.ExtValue(key='s:mentioned_users', value='')
)
request.body.send_message_body.message_type = int(message_type)
request.body.send_message_body.ticket = ticket
request.body.send_message_body.client_message_id = client_message_id
# 签名数据计算
sign_data_str = f'content={json.dumps(msg_content, separators=(",", ":"), ensure_ascii=False)}' + f'&conversation_id={conversation_id}&conversation_short_id={conversation_short_id}'
req_sign = generate_req_sign({
"sign_data": sign_data_str,
"certType": "cookie",
"scene": "web_protect"
}, auth.private_key)
request.reuqest_sign = req_sign
return request