import json import re from datetime import datetime, timezone, timedelta from typing import Any TZ_CN = timezone(timedelta(hours=8)) def _now_collect_time() -> str: return datetime.now(TZ_CN).strftime("%Y-%m-%d %H:%M:%S") def format_collected_at(value: Any = None) -> str: if value is None or (isinstance(value, str) and not value.strip()): return _now_collect_time() if isinstance(value, datetime): dt = value if dt.tzinfo is not None: dt = dt.astimezone(TZ_CN) return dt.strftime("%Y-%m-%d %H:%M:%S") s = str(value).strip() if re.fullmatch(r"\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}", s): return s if re.fullmatch(r"\d{4}-\d{2}-\d{2}", s): return f"{s} 00:00:00" try: dt = datetime.fromisoformat(s.replace("Z", "+00:00")) if dt.tzinfo is not None: dt = dt.astimezone(TZ_CN) return dt.strftime("%Y-%m-%d %H:%M:%S") except Exception: pass m = re.match(r"^(\d{4})[/-](\d{1,2})[/-](\d{1,2})[ T](\d{1,2}):(\d{1,2})(?::(\d{1,2}))?", s) if m: y, mo, d, h, mi, sec = m.groups() return f"{int(y):04d}-{int(mo):02d}-{int(d):02d} {int(h):02d}:{int(mi):02d}:{int(sec or 0):02d}" print(f" [WARN] collectedAt 无法解析 {s!r}, 使用当前时间") return _now_collect_time() def build_submit_payload(task: dict, parsed: dict, location: str = "") -> dict: result = parsed.get("result") or {} sources = parsed.get("sources") or [] answer = (result.get("answer") or "").strip() think = (result.get("thinking_process") or "").strip() or None related = result.get("related_questions") or [] if isinstance(related, list): extended = "\n".join(str(x) for x in related if x) or None else: extended = str(related) if related else None search_links = [] for i, sr in enumerate(sources, 1): url = sr.get("source_url") or "" if not url: continue search_links.append({ "url": url, "index": i, "title": sr.get("source_title") or "", "source": sr.get("source_name") or "", "snippet": sr.get("snippet") or sr.get("body") or None, "site_name": sr.get("source_name") or "", "site_icon": sr.get("source_icon") or "", "published_time": sr.get("source_publish_date") or "", }) adopted = [{"url": u, "title": ""} for u in (result.get("cited_links") or []) if u] shared = [] share_link = result.get("share_link") or "" if share_link: shared.append({"url": share_link, "title": ""}) meta_infos = [] for img in (result.get("image_links") or []): if img: meta_infos.append({ "source_type": "image", "url": img, "title": "", "summary": "", "website_name": "", "icon": "", "value": json.dumps({"images": [{"full_size_url": [img]}], "videos": []}, ensure_ascii=False), }) for vid in (result.get("video_links") or []): if vid: meta_infos.append({ "source_type": "video", "url": vid, "title": "", "summary": "", "website_name": "", "icon": "", "value": json.dumps({"images": [], "videos": [{"url": vid}]}, ensure_ascii=False), }) deep = int(task.get("deepThinking") if task.get("deepThinking") is not None else parsed.get("deep_thinking") or 0) web = int(task.get("webSearch") if task.get("webSearch") is not None else (1 if parsed.get("is_online") else 0)) task_id = str(task.get("id") or "").strip() dispatch_id = str(task.get("dispatchId") or "").strip() collected_at = format_collected_at( parsed.get("collect_time") or (parsed.get("result") or {}).get("collect_time") or task.get("collectedAt") ) return { "id": task_id, "dispatchId": dispatch_id, "collectedAt": collected_at, "answer": answer, "thinkText": think, "deepThinking": 1 if deep else 0, "webSearch": 1 if web else 0, "model": task.get("model") or "", "location": location or "", "extendedQuestion": extended, "searchLinks": search_links, "adoptedLinks": adopted, "sharedLinks": shared, "metaInfos": meta_infos, "productLinkList": [], }