import re BIZ_EVENT_NAMES = {"contact_click", "form_start", "form_error", "form_submit", "form_submit_result", "product_view"} # 阶段B业务事件(spec §1) EVENT_NAMES = {"page_view", "page_stay", "sdk_loaded", "session_resume"} | BIZ_EVENT_NAMES SCHEMA_VERSION = 2 # 批级信缺省 = v1(旧SDK) MAX_EVENTS_PER_BATCH = 50 MAX_PROP_KEYS = 32 MAX_STR_LEN = 500 MAX_PATH_LEN = 500 MAX_TITLE_LEN = 200 CLOCK_SKEW_LATE_SEC = 120 # 超过即标 late CLOCK_SKEW_REJECT_SEC = 86400 # 超过即拒绝 SENSITIVE_KEY_PATTERNS = ("password", "passwd", "token", "secret", "card", "verif", "otp", "idcard") SANITIZE_RULE_VERSION = "v2" # 三轨制(spec §2.4,slug 安全线=32):①参数名黑名单整键删;②归因白名单提取保留; # ③其余按值判别:纯数字≤10位或 slug≤32 保留,其余删。 URL_PARAM_DROP_PATTERNS = ("email", "phone", "mobile", "tel", "name", "user", "token", "key", "password", "verif", "passwd", "idcard") ATTRIBUTION_PARAM_WHITELIST = {"gclid", "gbraid", "wbraid", "fbclid", "msclkid", "yclid", "gad_source", "gad_campaignid", "dclid"} _SAFE_VALUE = re.compile(r"^(?:\d{1,10}|[A-Za-z0-9][A-Za-z0-9._-]{0,30})$") # slug≤32:32+连续字母数字视为token删 SESSION_IDLE_MS = 30 * 60 * 1000 BOT_UA_KEYWORDS = ("bot", "spider", "crawl", "slurp", "bingpreview", "headless", "lighthouse") EVENT_PROP_WHITELIST = { # spec §9.1:未知字段拒绝。免费文本/任意属性不得入库 "page_view": {"referrer", "referrer_domain", "utm", "nav_type", "prerendering"}, "page_stay": {"active_ms", "path", "active_from", "active_to", "flush_reason", "measurement_version"}, "sdk_loaded": {"sdk_version", "build_id", "schema_version", "config_version"}, "session_resume": {"prev_session_id", "reason"}, # 业务事件属性键白名单(spec §2)——值级枚举/格式校验在 validation._biz_props_ok "contact_click": {"channel"}, "form_start": {"form_id"}, "form_submit": {"form_id", "submission_id"}, "form_error": {"form_id", "submission_id", "error_code", "field_count"}, "form_submit_result": {"form_id", "submission_id", "result_code"}, "product_view": {"product_id", "inferred", "rule_version"}, } MAX_PROP_DEPTH = 2 MAX_PROP_JSON_BYTES = 4096 EXTENDED_ENV_KEYS = { # SDK 扩展环境键白名单(Visitor.env_extra 快照,展示用): "device_pixel_ratio", "viewport", "orientation", "max_touch_points", "device_memory", # 设备形态 "languages", "color_scheme", "standalone", # 偏好/宿主 "network_downlink", "network_rtt", "save_data", "wechat_version", # 网络/宿主 "device_model", "platform_version", "architecture", "bitness", # UA-CH 高熵 } # --------------------------------------------------------------------------- # 来源归因规则库 v2(tracker/services/attribution.py 消费)。改规则必须 bump 版本号 # 并跑 recompute_attribution 重算历史——已落库分类带 rule_version 可追溯。 # v2:新增 paid_social 渠道——fbclid 点击参数与 fb 家族+付费 medium(实测主导模式 # utm_medium=paid&utm_source=fb)不再混入 paid_search。 # --------------------------------------------------------------------------- ATTRIBUTION_RULE_VERSION = "v2" AI_REFERRAL_DOMAINS = { # AI 平台 → 显示名 "chatgpt.com": "ChatGPT", "perplexity.ai": "Perplexity", "copilot.microsoft.com": "Copilot", "gemini.google.com": "Gemini", "deepseek.com": "DeepSeek", "claude.ai": "Claude", "poe.com": "Poe", "you.com": "You", } SEARCH_ENGINE_DOMAINS = { # 引擎后缀/域名 → 显示名("google."/"yandex." 前缀匹配任意国家站 TLD) "google.": "Google", "bing.com": "Bing", "baidu.com": "百度", "yandex.": "Yandex", "duckduckgo.com": "DuckDuckGo", "yahoo.com": "Yahoo", "ecosia.org": "Ecosia", "search.naver.com": "Naver", "search.brave.com": "Brave", } SEARCH_TERM_PARAMS = { # 引擎 → referrer URL 里的搜索词参数名(白名单提取) "Bing": ["q"], "百度": ["wd", "word"], "DuckDuckGo": ["q"], "Yandex": ["text"], "Ecosia": ["q"], "Yahoo": ["p"], "Brave": ["q"], "Naver": ["query"], } # 注:Google 不提供,绝不虚构 PAID_CLICK_PARAMS = {"gclid": "Google Ads", "fbclid": "Meta Ads", "msclkid": "Bing Ads", "yclid": "Yandex Ads"} PAID_MEDIUMS = {"cpc", "ppc", "paidsearch", "paid", "social", "paid_social", "social_ads", "paidsocial"} # utm_medium 付费判定集(含社交系——仅 fb 家族配社交 medium 判付费) SOCIAL_MEDIUMS = {"social", "paid_social", "social_ads", "paidsocial"} # 社交系 medium:非 fb 来源不判付费(防误伤自然社交) SOCIAL_DOMAINS = { "t.co": "Twitter/X", "twitter.com": "Twitter/X", "x.com": "Twitter/X", "facebook.com": "Facebook", "linkedin.com": "LinkedIn", "instagram.com": "Instagram", "vk.com": "VK", "reddit.com": "Reddit", "pinterest.com": "Pinterest", "telegram.me": "Telegram", "t.me": "Telegram", } INAPP_UA_MARKERS = {"MicroMessenger": "微信", "Line/": "Line", "FBAN": "Facebook", "FBAV": "Facebook", "Instagram": "Instagram", "WhatsApp": "WhatsApp"} CHANNELS = ("natural_search", "paid_search", "paid_social", "ai_referral", "social", "in_app", "referral", "direct", "unknown")