GEO Task Worker v2 免安装版 (含嵌入式 Python + 全部依赖)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

137 lines
5.4 KiB

import json
import struct
class KimiClient:
def __init__(self, token):
from curl_cffi import requests as cffi_req
self.requests = cffi_req
self.url = "https://www.kimi.com/apiv2/kimi.gateway.chat.v1.ChatService/Chat"
self._refresh_token = ""
# 兼容 JSON 格式: {"access_token":"...", "refresh_token":"..."}
raw = token.strip()
if raw.startswith("{"):
try:
import json
obj = json.loads(raw)
self._refresh_token = obj.get("refresh_token") or ""
raw = obj.get("access_token") or ""
except Exception:
pass
self.token = raw if raw.startswith("Bearer") else f"Bearer {raw}"
import jwt as jwt_lib
payload = jwt_lib.decode(raw.replace("Bearer ", ""), options={"verify_signature": False})
self.device_id = payload.get("device_id", "")
self.traffic_id = payload.get("sub", "")
self.session_id = self._get_tobid()
def refresh_access_token(self) -> bool:
if not self._refresh_token:
return False
try:
resp = self.requests.get(
"https://www.kimi.com/api/auth/token/refresh",
headers={
"authorization": f"Bearer {self._refresh_token}",
"origin": "https://www.kimi.com",
"referer": "https://www.kimi.com/",
},
timeout=15, impersonate="chrome146",
)
if resp.status_code == 200:
data = resp.json()
new_token = data.get("access_token") or ""
new_refresh = data.get("refresh_token") or ""
if new_token:
self.token = f"Bearer {new_token}" if not new_token.startswith("Bearer") else new_token
if new_refresh:
self._refresh_token = new_refresh
print(f" [kimi] access_token 已刷新")
return True
except Exception as e:
print(f" [kimi] refresh 异常: {e}")
return False
def _get_tobid(self):
resp = self.requests.post(
"https://gator.volces.com/tobid",
json={"app_id": 20001731, "user_unique_id": self.traffic_id, "web_id": self.device_id},
timeout=10,
)
return resp.json().get("tobid", "")
def _build_headers(self):
return {
"authorization": self.token,
"connect-protocol-version": "1",
"content-type": "application/connect+json",
"origin": "https://www.kimi.com",
"referer": "https://www.kimi.com/",
"x-msh-device-id": self.device_id,
"x-msh-session-id": self.session_id,
"x-msh-platform": "web",
"x-msh-version": "2.0.0",
"x-traffic-id": self.traffic_id,
"x-language": "zh-CN",
"r-timezone": "Asia/Shanghai",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/149.0.0.0 Safari/537.36",
}
def chat(self, question, thinking=True):
scenario = "SCENARIO_K3" if thinking else "SCENARIO_K2D5"
body = {
"scenario": scenario,
"tools": [
{"type": "TOOL_TYPE_SEARCH", "search": {}},
{"type": "TOOL_TYPE_CRON_JOB"},
],
"message": {
"role": "user",
"blocks": [{"message_id": "", "text": {"content": question}}],
"scenario": scenario,
"is_goal": False,
},
"options": {
"thinking": bool(thinking),
"enable_plugin": True,
"reasoning_effort": "REASONING_EFFORT_NONE",
},
"project_id": "",
}
json_bytes = json.dumps(body, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
packet = b"\x00" + struct.pack(">I", len(json_bytes)) + json_bytes
resp = self.requests.post(
self.url, headers=self._build_headers(), data=packet,
timeout=360, impersonate="chrome146",
)
raw_req = json.dumps(body, ensure_ascii=False)
return resp.content, f"\x00{raw_req}"
async def extract_kimi_from_browser():
from playwright.async_api import async_playwright
from .base import CDP_URL
async with async_playwright() as pw:
try:
browser = await pw.chromium.connect_over_cdp(CDP_URL)
for page in browser.contexts[0].pages:
if "kimi.com" in page.url:
token = await page.evaluate("""() => {
const at = localStorage.getItem('access_token');
if (at && at.startsWith('eyJ') && at.length > 400) return at;
const ka = document.cookie.match(/kimi-auth=([^;]+)/);
if (ka && ka[1].startsWith('eyJ') && ka[1].length > 400) return ka[1];
const m = document.cookie.match(/access_token=([^;]+)/);
if (m && m[1].startsWith('eyJ') && m[1].length > 400) return m[1];
return '';
}""")
if token:
print(f" [Kimi] Token 从浏览器提取: {token[:30]}...")
return {"token": token, "id": "-1"}
except Exception:
pass
return None