xianyu-auto-reply/utils/xianyu_utils.py
2025-07-26 14:08:42 +08:00

353 lines
16 KiB
Python
Raw 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 base64
import json
import subprocess
from functools import partial
import time
import hashlib
import struct
import os
from typing import Any, Dict, List
import blackboxprotobuf
subprocess.Popen = partial(subprocess.Popen, encoding="utf-8")
import execjs
def get_js_path():
"""获取JavaScript文件的路径"""
current_dir = os.path.dirname(os.path.abspath(__file__))
root_dir = os.path.dirname(current_dir)
js_path = os.path.join(root_dir, 'static', 'xianyu_js_version_2.js')
return js_path
try:
xianyu_js = execjs.compile(open(get_js_path(), 'r', encoding='utf-8').read())
except Exception as e:
raise RuntimeError(f"无法加载JavaScript文件: {e}")
def trans_cookies(cookies_str: str) -> dict:
"""将cookies字符串转换为字典"""
if not cookies_str:
raise ValueError("cookies不能为空")
cookies = {}
for cookie in cookies_str.split("; "):
if "=" in cookie:
key, value = cookie.split("=", 1)
cookies[key] = value
return cookies
def generate_mid() -> str:
"""生成mid"""
import random
random_part = int(1000 * random.random())
timestamp = int(time.time() * 1000)
return f"{random_part}{timestamp} 0"
def generate_uuid() -> str:
"""生成uuid"""
timestamp = int(time.time() * 1000)
return f"-{timestamp}1"
def generate_device_id(user_id: str) -> str:
"""生成设备ID"""
import random
# 字符集
chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
result = []
for i in range(36):
if i in [8, 13, 18, 23]:
result.append("-")
elif i == 14:
result.append("4")
else:
if i == 19:
# 对于位置19需要特殊处理
rand_val = int(16 * random.random())
result.append(chars[(rand_val & 0x3) | 0x8])
else:
rand_val = int(16 * random.random())
result.append(chars[rand_val])
return ''.join(result) + "-" + user_id
def generate_sign(t: str, token: str, data: str) -> str:
"""生成签名"""
app_key = "34839810"
msg = f"{token}&{t}&{app_key}&{data}"
# 使用MD5生成签名
md5_hash = hashlib.md5()
md5_hash.update(msg.encode('utf-8'))
return md5_hash.hexdigest()
class MessagePackDecoder:
"""MessagePack解码器的纯Python实现"""
def __init__(self, data: bytes):
self.data = data
self.pos = 0
self.length = len(data)
def read_byte(self) -> int:
if self.pos >= self.length:
raise ValueError("Unexpected end of data")
byte = self.data[self.pos]
self.pos += 1
return byte
def read_bytes(self, count: int) -> bytes:
if self.pos + count > self.length:
raise ValueError("Unexpected end of data")
result = self.data[self.pos:self.pos + count]
self.pos += count
return result
def read_uint8(self) -> int:
return self.read_byte()
def read_uint16(self) -> int:
return struct.unpack('>H', self.read_bytes(2))[0]
def read_uint32(self) -> int:
return struct.unpack('>I', self.read_bytes(4))[0]
def read_uint64(self) -> int:
return struct.unpack('>Q', self.read_bytes(8))[0]
def read_int8(self) -> int:
return struct.unpack('>b', self.read_bytes(1))[0]
def read_int16(self) -> int:
return struct.unpack('>h', self.read_bytes(2))[0]
def read_int32(self) -> int:
return struct.unpack('>i', self.read_bytes(4))[0]
def read_int64(self) -> int:
return struct.unpack('>q', self.read_bytes(8))[0]
def read_float32(self) -> float:
return struct.unpack('>f', self.read_bytes(4))[0]
def read_float64(self) -> float:
return struct.unpack('>d', self.read_bytes(8))[0]
def read_string(self, length: int) -> str:
return self.read_bytes(length).decode('utf-8')
def decode_value(self) -> Any:
"""解码单个MessagePack值"""
if self.pos >= self.length:
raise ValueError("Unexpected end of data")
format_byte = self.read_byte()
# Positive fixint (0xxxxxxx)
if format_byte <= 0x7f:
return format_byte
# Fixmap (1000xxxx)
elif 0x80 <= format_byte <= 0x8f:
size = format_byte & 0x0f
return self.decode_map(size)
# Fixarray (1001xxxx)
elif 0x90 <= format_byte <= 0x9f:
size = format_byte & 0x0f
return self.decode_array(size)
# Fixstr (101xxxxx)
elif 0xa0 <= format_byte <= 0xbf:
size = format_byte & 0x1f
return self.read_string(size)
# nil
elif format_byte == 0xc0:
return None
# false
elif format_byte == 0xc2:
return False
# true
elif format_byte == 0xc3:
return True
# bin 8
elif format_byte == 0xc4:
size = self.read_uint8()
return self.read_bytes(size)
# bin 16
elif format_byte == 0xc5:
size = self.read_uint16()
return self.read_bytes(size)
# bin 32
elif format_byte == 0xc6:
size = self.read_uint32()
return self.read_bytes(size)
# float 32
elif format_byte == 0xca:
return self.read_float32()
# float 64
elif format_byte == 0xcb:
return self.read_float64()
# uint 8
elif format_byte == 0xcc:
return self.read_uint8()
# uint 16
elif format_byte == 0xcd:
return self.read_uint16()
# uint 32
elif format_byte == 0xce:
return self.read_uint32()
# uint 64
elif format_byte == 0xcf:
return self.read_uint64()
# int 8
elif format_byte == 0xd0:
return self.read_int8()
# int 16
elif format_byte == 0xd1:
return self.read_int16()
# int 32
elif format_byte == 0xd2:
return self.read_int32()
# int 64
elif format_byte == 0xd3:
return self.read_int64()
# str 8
elif format_byte == 0xd9:
size = self.read_uint8()
return self.read_string(size)
# str 16
elif format_byte == 0xda:
size = self.read_uint16()
return self.read_string(size)
# str 32
elif format_byte == 0xdb:
size = self.read_uint32()
return self.read_string(size)
# array 16
elif format_byte == 0xdc:
size = self.read_uint16()
return self.decode_array(size)
# array 32
elif format_byte == 0xdd:
size = self.read_uint32()
return self.decode_array(size)
# map 16
elif format_byte == 0xde:
size = self.read_uint16()
return self.decode_map(size)
# map 32
elif format_byte == 0xdf:
size = self.read_uint32()
return self.decode_map(size)
# Negative fixint (111xxxxx)
elif format_byte >= 0xe0:
return format_byte - 0x100
raise ValueError(f"Unknown format byte: {format_byte:02x}")
def decode_array(self, size: int) -> List[Any]:
"""解码数组"""
return [self.decode_value() for _ in range(size)]
def decode_map(self, size: int) -> Dict[Any, Any]:
"""解码字典"""
result = {}
for _ in range(size):
key = self.decode_value()
value = self.decode_value()
result[key] = value
return result
def decode(self) -> Any:
"""解码整个MessagePack数据"""
return self.decode_value()
def decrypt(data: str) -> str:
"""解密消息数据"""
import json as json_module # 使用别名避免作用域冲突
try:
# 确保输入数据是字符串类型
if not isinstance(data, str):
data = str(data)
# 清理数据移除可能的非ASCII字符
try:
# 尝试编码为ASCII如果失败则使用UTF-8编码后再解码
data.encode('ascii')
except UnicodeEncodeError:
# 如果包含非ASCII字符先编码为UTF-8字节再解码为ASCII兼容的字符串
data = data.encode('utf-8', errors='ignore').decode('ascii', errors='ignore')
# Base64解码
try:
decoded_data = base64.b64decode(data)
except Exception as decode_error:
# 如果base64解码失败尝试添加填充
missing_padding = len(data) % 4
if missing_padding:
data += '=' * (4 - missing_padding)
decoded_data = base64.b64decode(data)
# 使用MessagePack解码器解码数据
decoder = MessagePackDecoder(decoded_data)
decoded_value = decoder.decode()
# 如果解码后的值是字典转换为JSON字符串
if isinstance(decoded_value, dict):
def json_serializer(obj):
if isinstance(obj, bytes):
return obj.decode('utf-8', errors='ignore')
raise TypeError(f"Type {type(obj)} not serializable")
return json_module.dumps(decoded_value, default=json_serializer, ensure_ascii=False)
# 如果是其他类型,尝试转换为字符串
return str(decoded_value)
except Exception as e:
raise Exception(f"解密失败: {str(e)}")
if __name__ == '__main__':
# t = 1741667630548
# token = 'b7e897bf9767618a32b439c6103fe1cb'
# data = '{"appKey":"444e9908a51d1cb236a27862abc769c9","deviceId":"ED4CBA2C-5DA0-4154-A902-BF5CB52409E2-3888777108"}'
# print(generate_sign(t, token, data))
msg = "ggGLAYEBsjMxNDk2MzcwNjNAZ29vZmlzaAKzNDc5ODMzODkwOTZAZ29vZmlzaAOxMzQxNjU2NTI3NDU0Mi5QTk0EAAXPAAABlbKji20GggFlA4UBoAK6W+aIkeW3suaLjeS4i++8jOW+heS7mOasvl0DoAQaBdoEKnsiY29udGVudFR5cGUiOjI2LCJkeENhcmQiOnsiaXRlbSI6eyJtYWluIjp7ImNsaWNrUGFyYW0iOnsiYXJnMSI6Ik1zZ0NhcmQiLCJhcmdzIjp7InNvdXJjZSI6ImltIiwidGFza19pZCI6IjNleFFKSE9UbVBVMSIsIm1zZ19pZCI6ImNjOGJjMmRmN2M5MzRkZjA4NmUwNTY3Y2I2OWYxNTczIn19LCJleENvbnRlbnQiOnsiYmdDb2xvciI6IiNGRkZGRkYiLCJidXR0b24iOnsiYmdDb2xvciI6IiNGRkU2MEYiLCJib3JkZXJDb2xvciI6IiNGRkU2MEYiLCJjbGlja1BhcmFtIjp7ImFyZzEiOiJNc2dDYXJkQWN0aW9uIiwiYXJncyI6eyJzb3VyY2UiOiJpbSIsInRhc2tfaWQiOiIzZXhRSkhPVG1QVTEiLCJtc2dfaWQiOiJjYzhiYzJkZjdjOTM0ZGYwODZlMDU2N2NiNjlmMTU3MyJ9fSwiZm9udENvbG9yIjoiIzMzMzMzMyIsInRhcmdldFVybCI6ImZsZWFtYXJrZXQ6Ly9hZGp1c3RfcHJpY2U/Zmx1dHRlcj10cnVlJmJpek9yZGVySWQ9MjUwMzY4ODEyNjM1NjYzNjM3MCIsInRleHQiOiLkv67mlLnku7fmoLwifSwiZGVzYyI6Iuivt+WPjOaWueayn+mAmuWPiuaXtuehruiupOS7t+agvCIsImRlc2NDb2xvciI6IiNBM0EzQTMiLCJ0aXRsZSI6IuaIkeW3suaLjeS4i++8jOW+heS7mOasviIsInVwZ3JhZGUiOnsidGFyZ2V0VXJsIjoiaHR0cHM6Ly9oNS5tLmdvb2Zpc2guY29tL2FwcC9pZGxlRmlzaC1GMmUvZm0tZG93bmxhb2QvaG9tZS5odG1sP25vUmVkcmllY3Q9dHJ1ZSZjYW5CYWNrPXRydWUmY2hlY2tWZXJzaW9uPXRydWUiLCJ2ZXJzaW9uIjoiNy43LjkwIn19LCJ0YXJnZXRVcmwiOiJmbGVhbWFya2V0Oi8vb3JkZXJfZGV0YWlsP2lkPTI1MDM2ODgxMjYzNTY2MzYzNzAmcm9sZT1zZWxsZXIifX0sInRlbXBsYXRlIjp7Im5hbWUiOiJpZGxlZmlzaF9tZXNzYWdlX3RyYWRlX2NoYXRfY2FyZCIsInVybCI6Imh0dHBzOi8vZGluYW1pY3guYWxpYmFiYXVzZXJjb250ZW50LmNvbS9wdWIvaWRsZWZpc2hfbWVzc2FnZV90cmFkZV9jaGF0X2NhcmQvMTY2NzIyMjA1Mjc2Ny9pZGxlZmlzaF9tZXNzYWdlX3RyYWRlX2NoYXRfY2FyZC56aXAiLCJ2ZXJzaW9uIjoiMTY2NzIyMjA1Mjc2NyJ9fX0HAQgBCQAK3gAQpmJpelRhZ9oAe3sic291cmNlSWQiOiJDMkM6M2V4UUpIT1RtUFUxIiwidGFza05hbWUiOiLlt7Lmi43kuItf5pyq5LuY5qy+X+WNluWutiIsIm1hdGVyaWFsSWQiOiIzZXhRSkhPVG1QVTEiLCJ0YXNrSWQiOiIzZXhRSkhPVG1QVTEifbFjbG9zZVB1c2hSZWNlaXZlcqVmYWxzZbFjbG9zZVVucmVhZE51bWJlcqVmYWxzZaxkZXRhaWxOb3RpY2W6W+aIkeW3suaLjeS4i++8jOW+heS7mOasvl2nZXh0SnNvbtoBr3sibXNnQXJncyI6eyJ0YXNrX2lkIjoiM2V4UUpIT1RtUFUxIiwic291cmNlIjoiaW0iLCJtc2dfaWQiOiJjYzhiYzJkZjdjOTM0ZGYwODZlMDU2N2NiNjlmMTU3MyJ9LCJxdWlja1JlcGx5IjoiMSIsIm1zZ0FyZzEiOiJNc2dDYXJkIiwidXBkYXRlS2V5IjoiNDc5ODMzODkwOTY6MjUwMzY4ODEyNjM1NjYzNjM3MDoxX25vdF9wYXlfc2VsbGVyIiwibWVzc2FnZUlkIjoiY2M4YmMyZGY3YzkzNGRmMDg2ZTA1NjdjYjY5ZjE1NzMiLCJtdWx0aUNoYW5uZWwiOnsiaHVhd2VpIjoiRVhQUkVTUyIsInhpYW9taSI6IjEwODAwMCIsIm9wcG8iOiJFWFBSRVNTIiwiaG9ub3IiOiJOT1JNQUwiLCJhZ29vIjoicHJvZHVjdCIsInZpdm8iOiJPUkRFUiJ9LCJjb250ZW50VHlwZSI6IjI2IiwiY29ycmVsYXRpb25Hcm91cElkIjoiM2V4UUpIT1RtUFUxX0ZGcjRHT1NuOE9RbyJ9qHJlY2VpdmVyrTIyMDI2NDA5MTgwNzmrcmVkUmVtaW5kZXKy562J5b6F5Lmw5a625LuY5qy+sHJlZFJlbWluZGVyU3R5bGWhMa9yZW1pbmRlckNvbnRlbnS6W+aIkeW3suaLjeS4i++8jOW+heS7mOasvl2ucmVtaW5kZXJOb3RpY2W75Lmw5a625bey5ouN5LiL77yM5b6F5LuY5qy+rXJlbWluZGVyVGl0bGW75Lmw5a625bey5ouN5LiL77yM5b6F5LuY5qy+q3JlbWluZGVyVXJs2gCaZmxlYW1hcmtldDovL21lc3NhZ2VfY2hhdD9pdGVtSWQ9OTAwMDUyNjQ0Mjc3JnBlZXJVc2VySWQ9MzE0OTYzNzA2MyZwZWVyVXNlck5pY2s955S3KioqeSZzaWQ9NDc5ODMzODkwOTYmbWVzc2FnZUlkPWNjOGJjMmRmN2M5MzRkZjA4NmUwNTY3Y2I2OWYxNTczJmFkdj1ub6xzZW5kZXJVc2VySWSqMzE0OTYzNzA2M65zZW5kZXJVc2VyVHlwZaEwq3Nlc3Npb25UeXBloTGqdXBkYXRlSGVhZKR0cnVlDAEDgahuZWVkUHVzaKR0cnVl"
msg = "ggGLAYEBsjMxNDk2MzcwNjNAZ29vZmlzaAKzNDc5ODMzODkwOTZAZ29vZmlzaAOxMzQxNjU2NTI3NDU0Mi5QTk0EAAXPAAABlbKji20GggFlA4UBoAK6W+aIkeW3suaLjeS4i++8jOW+heS7mOasvl0DoAQaBdoEKnsiY29udGVudFR5cGUiOjI2LCJkeENhcmQiOnsiaXRlbSI6eyJtYWluIjp7ImNsaWNrUGFyYW0iOnsiYXJnMSI6Ik1zZ0NhcmQiLCJhcmdzIjp7InNvdXJjZSI6ImltIiwidGFza19pZCI6IjNleFFKSE9UbVBVMSIsIm1zZ19pZCI6ImNjOGJjMmRmN2M5MzRkZjA4NmUwNTY3Y2I2OWYxNTczIn19LCJleENvbnRlbnQiOnsiYmdDb2xvciI6IiNGRkZGRkYiLCJidXR0b24iOnsiYmdDb2xvciI6IiNGRkU2MEYiLCJib3JkZXJDb2xvciI6IiNGRkU2MEYiLCJjbGlja1BhcmFtIjp7ImFyZzEiOiJNc2dDYXJkQWN0aW9uIiwiYXJncyI6eyJzb3VyY2UiOiJpbSIsInRhc2tfaWQiOiIzZXhRSkhPVG1QVTEiLCJtc2dfaWQiOiJjYzhiYzJkZjdjOTM0ZGYwODZlMDU2N2NiNjlmMTU3MyJ9fSwiZm9udENvbG9yIjoiIzMzMzMzMyIsInRhcmdldFVybCI6ImZsZWFtYXJrZXQ6Ly9hZGp1c3RfcHJpY2U/Zmx1dHRlcj10cnVlJmJpek9yZGVySWQ9MjUwMzY4ODEyNjM1NjYzNjM3MCIsInRleHQiOiLkv67mlLnku7fmoLwifSwiZGVzYyI6Iuivt+WPjOaWueayn+mAmuWPiuaXtuehruiupOS7t+agvCIsImRlc2NDb2xvciI6IiNBM0EzQTMiLCJ0aXRsZSI6IuaIkeW3suaLjeS4i++8jOW+heS7mOasviIsInVwZ3JhZGUiOnsidGFyZ2V0VXJsIjoiaHR0cHM6Ly9oNS5tLmdvb2Zpc2guY29tL2FwcC9pZGxlRmlzaC1GMmUvZm0tZG93bmxhb2QvaG9tZS5odG1sP25vUmVkcmllY3Q9dHJ1ZSZjYW5CYWNrPXRydWUmY2hlY2tWZXJzaW9uPXRydWUiLCJ2ZXJzaW9uIjoiNy43LjkwIn19LCJ0YXJnZXRVcmwiOiJmbGVhbWFya2V0Oi8vb3JkZXJfZGV0YWlsP2lkPTI1MDM2ODgxMjYzNTY2MzYzNzAmcm9sZT1zZWxsZXIifX0sInRlbXBsYXRlIjp7Im5hbWUiOiJpZGxlZmlzaF9tZXNzYWdlX3RyYWRlX2NoYXRfY2FyZCIsInVybCI6Imh0dHBzOi8vZGluYW1pY3guYWxpYmFiYXVzZXJjb250ZW50LmNvbS9wdWIvaWRsZWZpc2hfbWVzc2FnZV90cmFkZV9jaGF0X2NhcmQvMTY2NzIyMjA1Mjc2Ny9pZGxlZmlzaF9tZXNzYWdlX3RyYWRlX2NoYXRfY2FyZC56aXAiLCJ2ZXJzaW9uIjoiMTY2NzIyMjA1Mjc2NyJ9fX0HAQgBCQAK3gAQpmJpelRhZ9oAe3sic291cmNlSWQiOiJDMkM6M2V4UUpIT1RtUFUxIiwidGFza05hbWUiOiLlt7Lmi43kuItf5pyq5LuY5qy+X+WNluWutiIsIm1hdGVyaWFsSWQiOiIzZXhRSkhPVG1QVTEiLCJ0YXNrSWQiOiIzZXhRSkhPVG1QVTEifbFjbG9zZVB1c2hSZWNlaXZlcqVmYWxzZbFjbG9zZVVucmVhZE51bWJlcqVmYWxzZaxkZXRhaWxOb3RpY2W6W+aIkeW3suaLjeS4i++8jOW+heS7mOasvl2nZXh0SnNvbtoBr3sibXNnQXJncyI6eyJ0YXNrX2lkIjoiM2V4UUpIT1RtUFUxIiwic291cmNlIjoiaW0iLCJtc2dfaWQiOiJjYzhiYzJkZjdjOTM0ZGYwODZlMDU2N2NiNjlmMTU3MyJ9LCJxdWlja1JlcGx5IjoiMSIsIm1zZ0FyZzEiOiJNc2dDYXJkIiwidXBkYXRlS2V5IjoiNDc5ODMzODkwOTY6MjUwMzY4ODEyNjM1NjYzNjM3MDoxX25vdF9wYXlfc2VsbGVyIiwibWVzc2FnZUlkIjoiY2M4YmMyZGY3YzkzNGRmMDg2ZTA1NjdjYjY5ZjE1NzMiLCJtdWx0aUNoYW5uZWwiOnsiaHVhd2VpIjoiRVhQUkVTUyIsInhpYW9taSI6IjEwODAwMCIsIm9wcG8iOiJFWFBSRVNTIiwiaG9ub3IiOiJOT1JNQUwiLCJhZ29vIjoicHJvZHVjdCIsInZpdm8iOiJPUkRFUiJ9LCJjb250ZW50VHlwZSI6IjI2IiwiY29ycmVsYXRpb25Hcm91cElkIjoiM2V4UUpIT1RtUFUxX0ZGcjRHT1NuOE9RbyJ9qHJlY2VpdmVyrTIyMDI2NDA5MTgwNzmrcmVkUmVtaW5kZXKy562J5b6F5Lmw5a625LuY5qy+sHJlZFJlbWluZGVyU3R5bGWhMa9yZW1pbmRlckNvbnRlbnS6W+aIkeW3suaLjeS4i++8jOW+heS7mOasvl2ucmVtaW5kZXJOb3RpY2W75Lmw5a625bey5ouN5LiL77yM5b6F5LuY5qy+rXJlbWluZGVyVGl0bGW75Lmw5a625bey5ouN5LiL77yM5b6F5LuY5qy+q3JlbWluZGVyVXJs2gCaZmxlYW1hcmtldDovL21lc3NhZ2VfY2hhdD9pdGVtSWQ9OTAwMDUyNjQ0Mjc3JnBlZXJVc2VySWQ9MzE0OTYzNzA2MyZwZWVyVXNlck5pY2s955S3KioqeSZzaWQ9NDc5ODMzODkwOTYmbWVzc2FnZUlkPWNjOGJjMmRmN2M5MzRkZjA4NmUwNTY3Y2I2OWYxNTczJmFkdj1ub6xzZW5kZXJVc2VySWSqMzE0OTYzNzA2M65zZW5kZXJVc2VyVHlwZaEwq3Nlc3Npb25UeXBloTGqdXBkYXRlSGVhZKR0cnVlDAEDgahuZWVkUHVzaKR0cnVl"
res = decrypt(msg)
print(res)