Generate /players/{account_id}[/{match_id}] locally after POST_GAME via OpenDota; publish to OSS only when public_share is enabled.
Co-authored-by: Cursor <cursoragent@cursor.com>
37 KiB
37 KiB
协作说明(面向 AI 与贡献者)
本文件描述 上分帝(Climperor) 的职责边界、代码布局与修改注意点。
项目是什么
Windows 上的 Dota 2 天梯选将识别工具:用 GSI 感知选将阶段,截屏后对顶栏 10 格做 OpenCV 模板匹配,输出双方阵容与选将时间线。
- 中文名:上分帝
- 英文名 / 仓库目录:Climperor /
climperor - 模板来源:仅 Steam CDN 官方头像(
pc/templates/cdn/) - 已放弃 real 实拍模板库补充;不要再引入
templates/real/或入库流水线
上分帝 Web(web/)是独立子项目:英雄克制/搭档、排行、主播、走势、机制、物品、版本等内容站点,与局内选将识别解耦。文档与注释统称「上分帝 Web」,勿再称「关系预览」;历史脚本名(如 serve_relations.py、dist/relations/)暂保留。
Monorepo 布局
climperor/
├── pc/ 局内选将识别(GSI + 截屏 + OpenCV + overlay)
├── web/ 上分帝 Web(前端 frontend/ + 数据流水线 + 部署运维)
└── shared/ 共享包(英雄表/关系/标签/HTTP/路径常量),pc 与 web 都依赖它
跨目录 import 约定:pc/、web/、shared/ 下的每个脚本顶部都有相同两行引导(parents[1] 恒为仓库根),之后即可 from shared import ... / from shared.paths import ...;运行方式不变,一律在仓库根执行 python web/xxx.py、python pc/xxx.py、python shared/xxx.py。shared 不反向依赖 pc/ 或 web/;shared/grid.py 的 cv2/numpy 为懒加载,Web 侧与 CI 无需 opencv。路径常量单一来源是 shared/paths.py;pc/common.py re-export PC 所需常量,PC 侧 from common import ... 调用不变。
目录与模块
pc/(局内选将识别)
| 路径 | 职责 |
|---|---|
pc/common.py |
配置 IO、槽位几何、裁切、NCC 匹配、天梯遮罩、CDN 模板加载;re-export shared.paths 常量 |
pc/recognize.py |
单帧识别;recognize_image() 供会话复用 |
pc/draft_session.py |
整局选将跟踪、改判、皮肤规避策略 |
pc/gsi_watch.py / pc/gsi_setup.py |
GSI 监听与 cfg 安装;POST_GAME 触发 player_pages 赛后建页 |
pc/player_pages.py |
赛后轮询 OpenDota → pc/player_pages/{account_id}/(profile + match JSON);public_share 时 POST /api/players/publish;不进 recommend |
pc/fetch_cdn_templates.py |
拉取 CDN 头像 + 生成 shared/data/heroes.json(含基础属性/血蓝;保留已有 aliases) |
pc/recommend.py |
定位局分路过滤;克/搭/补全网格标记;调用 draft_archetypes 做推进/全球流/缺口画像与短文案 |
pc/item_suggest.py |
本人锁定后:hero_items 核心装 + 敌方 tags/画像定性应对装(不读 fears/STRATZ 统计) |
pc/draft_archetypes.py |
规则阵容画像(推进/全球流/敌我缺口)+ analysis / reasons 文案(不接 AI) |
pc/autocalibrate.py / pc/calibrate.py |
ROI 自动 / 手动标定 |
pc/capture.py |
屏幕捕获 |
pc/overlay.py |
网格「克/搭/补」方标 + 阵容分析横条 + 锁定后装备图标条(点击穿透;不再在顶栏头像下画定位) |
pc/evaluate.py |
按 pc/samples/labels.json 批量评测 |
pc/roles.py / pc/modes.py |
位置字、模式字(禁用网格在 shared/grid.py) |
pc/config.json |
相对坐标、阈值、GSI、recommend 参数(relations_path 指向 shared/data/relations.json) |
pc/templates/cdn/ |
全英雄 CDN 模板(需提交或由脚本生成) |
pc/templates/roles/ / pc/templates/modes/ |
位置 / 模式辅助模板 |
pc/assets/role_icons/ |
Valve 选人筛选角色图标(filter_*,品红 chroma 去黑边) |
pc/requirements.txt |
PC 侧依赖(opencv/numpy/mss/openpyxl) |
shared/(共享包)
| 路径 | 职责 |
|---|---|
shared/paths.py |
全部路径常量单一来源(ROOT/SHARED_DATA/HEROES_JSON/TEMPLATES_CDN/PC_PLAYER_PAGES/DATA/WEB_FRONTEND/各 Web 资产目录);纯常量、零第三方依赖 |
shared/grid.py |
英雄表 hero_table()、选人网格布局与禁用读取(cv2/numpy 懒加载,Web/CI 侧只取表不触发) |
shared/relations.py |
定性克制/搭档边读写与名称解析;默认路径 shared/data/relations.json |
shared/hero_tags.py |
中文定位 tags(核心/辅助/…/幻象) |
shared/http_utils.py |
所有 fetch 脚本共用的 HTTP 请求、图标下载、Valve datafeed 加载(统一 UA / 超时;429/5xx 指数退避重试) |
shared/import_relations_xlsx.py |
从选将笔记 xlsx 种子导入 shared/data/relations.json |
shared/fetch_stratz.py |
拉取 STRATZ 对位优势 / 搭档协同 → shared/data/stratz_matchups.json / shared/data/synergies.json(需 token;--mode matchups|synergies;供 audit_relations.py) |
shared/audit_relations.py |
用 STRATZ 对位/搭档缓存审计 relations.json,列出缺失英雄与候选边(不改写 relations;Web 对位页只读 stratz_matchup_tops.json) |
shared/data/heroes.json |
英雄表(id/key/attr/roles/aliases/abbr/tags + 基础属性/初始血蓝等;由 pc/fetch_cdn_templates.py 生成) |
shared/data/relations.json |
定性克制边 + 搭档边(可提交;手改 JSON,Web 站点只读) |
web/(上分帝 Web)
| 路径 | 职责 |
|---|---|
web/fetch_patches.py |
拉取近一年(默认 365 天,--days/--since)版本列表 + 逐版本 patchnotes 详情 → web/data/patches.json;构建 id→名称/图标的 lookup 并下载引用到的物品/技能图标(--no-icons 跳过;--force 重抓全量;--check 只比对列表与本地 details,stdout JSON) |
web/requirements.txt |
Web 刷新依赖(oss2);Gitea Actions 仅装它 |
web/fetch_stratz_meta.py |
拉取 STRATZ 各勋章段位 winWeek(近 N 周 pick/win + 同段位最近 1 周分路,positionIds)+ 对位 Top → web/data/stratz_hero_meta.json / web/data/stratz_matchup_tops.json(需 token;仅上分帝 Web;勿进 recommend / relations)。对位为全局聚合(无段位/分路/周过滤);weekly 默认全量刷新,--resume-matchups 仅中断续跑;失败保留旧值并标 stale |
web/serve_relations.py |
上分帝 Web 本地开发服务(web/frontend/;改 web/data/*.json 后刷新;History 深度路径 SPA fallback 回 index.html;/api/live-status 调用 fetch_streamer_live.probe_streamers 做真实探测,内存缓存 60s,失败标 stale/不显示直播角标;GET /api/players/{account_id}[/{match_id}] 读 pc/player_pages/;POST /api/players/publish 本地 no-op;/streamer-video/ 提供主播高光 mp4 与同名 JPG 封面,支持 HTTP Range) |
web/export_relations_site.py |
导出上分帝 Web 为纯静态站点 → web/dist/relations/(data.json 快照 + 前端 + 图片;SITE_VERSION / SITE_ORIGIN 与 web/frontend/config.js 同步;--ability-video-base / --static-asset-base / --site-origin 写 config.js;设 --static-asset-base 时不拷贝图标进 dist;调用 seo_prerender.py 写英雄/机制预渲染 HTML + sitemap.xml / llms.txt;拷贝 _redirects / robots.txt;--with-videos 可选本地拷贝技能/主播视频,生产部署勿用) |
web/seo_prerender.py |
导出期 SEO/GEO:注入 title/description/canonical/OG/JSON-LD 与 #seo-prerender 正文;生成全英雄 /heroes/{key}、机制 /mechanics/{effect}、顶层页、sitemap.xml、llms.txt |
web/deploy_relations.py |
一键部署上分帝 Web 静态站点到 Cloudflare Pages(导出 + 资产预检 + wrangler 直传 + 绑域名;默认 OSS base 指向 climperor 桶的视频与静态图;凭据经 keyzoo 注入或 env) |
web/refresh_web.py |
上分帝 Web 数据分层刷新编排(daily/weekly/patch/all);忽略纯时间戳与 is_live 的业务摘要有变更才 OSS upload + deploy_relations.py(亦监视 frontend / relations.json / heroes.json / 网格顺序);写 web/.refresh/summary.json(步骤/耗时/stale/部署;--dry-run 只打印不写盘、不 restore/save 缓存);同 runner 文件锁串行发布,patch 遇全量刷新则延后并标 skipped;--skip-deploy / --skip-oss / --force-deploy。daily:stats/排行/比赛/pro/主播/主播开播探测 + patch check;weekly:STRATZ meta + 物品 + counter;patch:仅 check,has_new 时详情 + abilities/商店/meta/fears |
web/refresh_cache.py |
Gitea Actions 刷新状态缓存桥:文件锁内从 runner ~/.cache/climperor-web-refresh/ 恢复/保存,web/.refresh-cache/ 供 Actions cache 冷启动备份;覆盖所有定时生成数据与 patches.json,主播只合并抓取字段,保留 git 中手工名单 |
web/notify_site_traffic.py |
上分帝 Web 日报 → 飞书 webhook(访问估数 + Gitea web-daily/weekly/patch 运行结论/REFRESH_SUMMARY + Pages 当日生产部署 + 生产数据新鲜度与 live API 探针;--dry-run 只打印卡片) |
.gitea/workflows/site-traffic-notify.yml |
每日 09:00 CST 跑 web/notify_site_traffic.py;Secrets:CLOUDFLARE_EMAIL / CLOUDFLARE_API_KEY / FEISHU_WEBHOOK_URL;内置 GITEA_TOKEN 用于查询同仓 Actions |
.gitea/workflows/web-daily.yml |
每日 06:00 CST:web/refresh_web.py --tier daily(self-hosted;需 CF + OSS Secrets;STRATZ 可选) |
.gitea/workflows/web-weekly.yml |
周二 07:00 CST:web/refresh_web.py --tier weekly(需 STRATZ_API_TOKEN + CF + OSS) |
.gitea/workflows/web-patch.yml |
每 6 小时:web/refresh_web.py --tier patch(fetch_patches.py --check,有新版本才拉详情与连带;需 CF + OSS) |
web/_cf_status.py |
只读查询 Cloudflare Pages 项目 / 部署 / 自定义域名状态(凭据经 keyzoo 注入) |
web/_oss_ability_videos.py |
阿里云 OSS 桶 climperor 建桶 / CORS / 同步 web/assets/ability_videos/ → ability-video/(凭据经 keyzoo digitevents/voson-RAM 注入);_oss_static_assets.py 同步图标等静态图;_oss_fix_public.py / _oss_launch_upload.py 为配套辅助 |
web/fetch_hero_portraits.py |
拉取官网横版头像 → web/assets/hero_portraits/(上分帝 Web) |
web/fetch_hero_items.py |
拉取 OpenDota 热门装备 → web/data/hero_items.json + web/assets/item_icons/ |
web/fetch_hero_stats.py |
拉取 OpenDota 各段位场次/胜场 → web/data/hero_stats.json(仅上分帝 Web;勿写入 relations/heroes,勿进 recommend) |
web/fetch_hero_matches.py |
拉取同英雄近期比赛 + 终局出装/加点 → web/data/hero_matches.json(--source league|public|both;合并后保留最近 N 场胜局,默认 10;天梯需传奇及以上;公开列表过滤 bot/Turbo、仅 ranked lobby;--public-region china 优先国服;--workers 详情并发 + 凑满即停;可选 OPENDOTA_API_KEY;增量补缺失/不足 N 场;--enrich-item-times 补购买时间;仅上分帝 Web;勿进 recommend) |
web/fetch_pro_matches.py |
按明星名单拉近期联赛/锦标赛对局 → web/data/pro_matches.json(默认读 web/data/pro_player_watchlist.json;--include-pubs 另拉天梯 lobby 7;每种 lobby 各保留 --limit 场;--refresh-limit N 按 fetched_at 只刷新最陈旧 N 人并保留其余;连续 429 熔断后仍写盘;可选 OPENDOTA_API_KEY;--players 覆盖整文件且不做轮换;--all-pros 从 /proPlayers 盲抽;含终局出装/加点;进 refresh_web daily 且带 --include-pubs --refresh-limit 15;仅上分帝 Web「比赛」;勿进 recommend) |
web/fetch_leaderboards.py |
拉取 Valve Immortal 四区榜 Top100 → web/data/leaderboards.json(仅上分帝 Web「排行」;无 MMR/account_id;勿进 recommend) |
web/fetch_streamers.py |
从抖音 / 斗鱼主页补全 web/data/streamers.json 的昵称/签名/关注粉丝获赞(播放)/头像(手工名单 + 直播间/主页 URL;抖音支持 v.douyin.com 短链;斗鱼优先 v.douyu.com/author/<hash>(兼容 author-video)的 $DATA,仅房间号时从直播间 HTML 解析 up_id 再拉作者页,失败才回退 betard;失败保留旧值;不探测开播(由 fetch_streamer_live.py 负责);仅上分帝 Web「主播」;勿进 recommend;进 refresh_web daily) |
web/fetch_streamer_live.py |
探测主播真实在播状态回写 web/data/streamers.json 的 is_live/live_probed_at:抖音解析直播间 SSR 页 roomStore.roomInfo.room.status(2 在播 / 4 下播;预热 cookie + ~1s 间隔;web_rid 校验),B 站走 Room/get_info(live_status==1 在播,轮播算下播),斗鱼走 betard/{room_id}(show_status==1 在播,videoLoop==1 轮播算下播);探测失败清为 is_live:false 并去掉 live_probed_at(与 /api/live-status 一致,不沿用旧直播中)、始终 exit 0;--ids a,b 限范围、--dry-run 只打印;仅 Web;进 refresh_web daily(角标以访问触发的 live API 为准,daily 仅作 data.json 兜底) |
web/frontend/functions/api/live-status.js |
Pages Function GET /api/live-status:访问触发的在播探测(逻辑同 fetch_streamer_live.py,含抖音 / B 站 / 斗鱼),读 data.json 的 streamers.streamers;Cache API 固定键 + isolate 内 in-flight 合并(5 分钟新鲜窗口,无 KV);抖音从数据中心 IP 失败属预期 → 失败主播一律 is_live:false + stale:true(不沿用旧的直播中);全失败回 stale-override 空角标表或 error,永不 500;导出时拷贝 functions/;部署须 cwd=dist 跑 wrangler(Functions 相对 cwd 解析) |
web/frontend/functions/api/mobile-demand.js |
Pages Function GET|POST /api/mobile-demand:移动端「催更」需求计数(Cache API 存 count,无 KV;边缘竞态/驱逐可能少计或重置);本机 serve_relations.py 写 web/.refresh/mobile_demand.json;前端 mobile-gate.js 用 UA 识别手机/平板并拦截,localStorage 同设备只 POST 一次 |
web/frontend/functions/api/players/publish.js |
Pages Function POST /api/players/publish:校验场内 account_id 后拉 OpenDota、写 OSS players/{id}/profile.json 与 matches/{match_id}.json(Secrets:OSS_*、可选 PLAYER_PAGES_PUBLISH_SECRET);不进主 data.json / recommend |
web/frontend/mobile-gate.js |
移动端门禁(<head> 早载):html.mobile-client + 催更按钮;设 window.__CLIMPEROR_MOBILE__ 供 app.js 跳过桌面 boot |
web/fetch_item_shop.py |
官网商店 11 列目录(dota2.com.cn/itemscategory)+ 合成图 → web/data/item_shop.json + 图标 |
web/fetch_items_meta.py |
Valve/OpenDota 装备描述 → 机制标签 → web/data/items_meta.json(%token% 用 special_values 填数;查询类 tags 共用 mechanic_tags.py) |
web/mechanic_tags.py |
上分帝 Web「机制」页共用标签(弱/强驱散、缠绕/缴械/沉默/锁闭/眩晕/妖术/破坏、睡眠/恐惧/嘲讽/致盲/束缚、隐身/虚无/吹风)+ 中文 labels;语义为「施加该效果」 |
web/loc_format.py |
Valve 文案共用:去 HTML、填充 %token% / {s:token}(键 casefold;魔晶/神杖 %bonus_<sv>% 走 values_shard/values_scepter) |
web/fetch_hero_abilities.py |
Valve herodata 技能/魔晶/神杖/天赋 + 驱散汇总 → web/data/hero_abilities.json;每条 ability 另有「施加」类 tags(与 dispellable 区分;--tags-only 可只重算);has_scepter/has_shard 只信 Valve 显式 flag,Valve 移除升级后残留的 scepter_loc/shard_loc 文案会被清空;逐级相同的 cast_points/channel_times 合并为单值(施法前摇/吟唱时间);可选 --icons / --icons-only 缓存 Steam CDN 技能图标 → web/assets/ability_icons/(先天用共用 innate.png,不拉 CDN) |
web/fetch_ability_videos.py |
官网技能演示 webm/mp4(Steam CDN;限速)→ web/assets/ability_videos/;A 杖/魔晶赋予技能(granted_by_scepter/granted_by_shard)CDN 名用 <hero>_aghanims_scepter/<hero>_aghanims_shard,本地文件名仍用 ability key |
web/fetch_item_counter_stats.py |
OpenDota Explorer 近场次聚合:对阵英雄时敌方终局装备购买率/胜率,与同窗口该装备全局队伍基线作差 → web/data/item_counter_stats.json(可再生成缓存;观测证据,不作因果结论;weekly 软失败) |
web/item_fears.py |
规则推导「英雄怕的装备」→ web/data/hero_item_fears.json;可选读取 item_counter_stats.json,对全部英雄的已有机制候选小幅调序;统计发现的新组合须经机制复核后写入 overrides |
web/data/patches.json |
近一年版本列表 + 逐版本详情(patches/lookup/details;由 fetch_patches.py 生成,Web 版本页只读) |
web/data/patch_summaries.json |
手写「AI 解读」(仅保留最新版条目;Cursor 审阅后写入;进 INPUT_WATCH;不进定时生成 / recommend) |
web/data/hero_grid_order.json |
Web 站点四列网格顺序 |
web/data/hero_items.json |
核心成品装备缓存(Web + PC 锁定后核心装推荐只读;由 fetch_hero_items.py 生成) |
web/data/hero_stats.json |
英雄各段位 pick/win + totals + window_*(OpenDota 近约 7 天;Web「走势」Tab:胜率/上场率/场次;冠绝与超凡样本合并展示;由 fetch_hero_stats.py 生成;不参与局内推荐) |
web/data/hero_matches.json |
同英雄近期比赛列表 + 终局出装/加点 + 可选购买时间(OpenDota;--source league|public|both;Web「近期比赛」Tab;由 fetch_hero_matches.py 生成;不参与局内推荐) |
web/data/pro_player_watchlist.json |
明星选手 OpenDota account_id 手工名单(对照 Dotabuff / Liquipedia;含现役战队席位与昔日国服明星;外号:查理斯→Chalice,Somnus 即 Maybe;CN/EU/SA 等);fetch_pro_matches.py 默认只拉此名单;队名可过期,以 id 为准;不进 recommend |
web/data/pro_matches.json |
明星选手近期联赛/锦标赛(可选天梯)对局(by_pro/by_hero + 终局出装/加点 + lobby_type/origin;由 fetch_pro_matches.py 生成;Web 顶层「比赛」侧栏筛「全部 / 职业 / 国服」+ 选手,与英雄详情「近期比赛」合并展示;进 daily;不参与局内推荐) |
web/data/leaderboards.json |
Valve Immortal 四区 Top100(china/europe/americas/se_asia;仅排名+昵称等;由 fetch_leaderboards.py 生成;Web「排行」选手榜只读;不参与局内推荐) |
web/data/streamers.json |
主播目录(手工 live_url/profile URL + 常用英雄 + 可选精选视频 video/video_title + 可选 video_poster/video_fit/video_crop/video_aspect;抖音 profile 补全;平台含抖音 / B 站 / 斗鱼;platform_meta;Web 卡片:头像行 = 头像 | 昵称+账号/获赞粉丝 |「关注」,签名(signature)独立全宽行(最长 3 行);在播时粉环 +「直播」角标叠在环底(抖音式 bottom:-6px,无间距);列表排序:先 is_live 再粉丝数降序;视口分档滚播(远处不拉、近处 metadata、中部 canplay 且单路 preload=auto)+ 同名 JPG 封面占位;有 live_url 时点头像进直播间;「直播」角标只信本轮成功探测(线上 /api/live-status 边缘缓存 5 分钟;本地 serve_relations 同逻辑缓存 60s;stale/失败不显示角标;data.json daily 仅作首屏兜底直至接口返回;live_url 仅作点击入口);由 fetch_streamers.py 补全;Web「主播」只读;不参与局内推荐) |
web/data/stratz_hero_meta.json |
STRATZ 各勋章段位近 N 周 pick/win(weeks/latest)+ 同段位最近 1 周分路(winWeek+positionIds)+ meta_board(由 fetch_stratz_meta.py 生成;Web 英雄详情「走势」优先 + 顶层「走势」/trends 近 N 周榜;不参与局内推荐) |
web/data/stratz_matchup_tops.json |
STRATZ 对位/协同 Top(counters/countered/synergies;由 fetch_stratz_meta.py 生成;全局聚合 + scope/stale/fetched_at;Web「对位」Tab;与定性 relations.json 分开展示;不参与局内推荐) |
web/data/item_shop.json |
商店 11 列目录(官网 basic/upgrade;由 fetch_item_shop.py 生成;物品页只读) |
web/data/items_meta.json |
成品装备描述与机制标签(由 fetch_items_meta.py 生成) |
web/data/item_tag_overrides.json |
装备标签手工加减(合并进 items_meta) |
web/data/ability_tag_overrides.json |
技能「施加」类 tags 手工加减(合并进 hero_abilities;仅 Web 机制页) |
web/data/hero_fear_overrides.json |
英雄→害怕装备手工加减(合并进 item_fears) |
web/data/hero_abilities.json |
英雄技能与机制汇总(由 fetch_hero_abilities.py 生成;含 ability tags) |
web/data/item_counter_stats.json |
OpenDota 对阵装备观测证据缓存(对阵购买率/条件胜率减同装备全局基线;可再生成;不进 recommend / relations) |
web/data/hero_item_fears.json |
英雄怕的装备(规则推导;Web「怕」行) |
web/frontend/ |
上分帝 Web 前端静态资源(index.html / config.js / app.js / style.css / router.js / mobile-gate.js / _redirects / robots.txt / functions/);config.js 含 SITE_VERSION、SITE_ORIGIN、ABILITY_VIDEO_BASE、STATIC_ASSET_BASE、PLAYERS_ASSET_BASE;英雄页底部(无详情时)显示 v{SITE_VERSION} 与数据更新时间;技能演示按官网 16:9(有空间加宽至约 720px,contain 不裁左右);移动端由 mobile-gate.js 拦截(搜索/AI 爬虫 UA 跳过) |
web/frontend/router.js |
History 路径路由:parseHash / serializeHash(操作 pathname+search)/ installRouter / syncStateToUrl;状态↔URL 双向同步(顶层 /heroes|rankings|streamers|matches|players|trends|mechanics|items|patches / 英雄 + 子标签 skills|core|fears|trends|matchups|matches|streamers|patches / Immortal /rankings[/region] / 明星比赛 /matches[/account_id][?origin=pro|china][&page=N] / PC 赛后玩家页 /players/{account_id}[/{match_id}](本机 API 或 OSS players/;默认私有) / 主播 /streamers / 走势 /trends[/bracket][?sort=pr] / 机制 /mechanics[/{effect}](默认 basic_dispel) / 物品 / 版本 / 标签筛选 / 搜索;旧 stats / /rankings/meta 与 hash #/... 兼容) |
web/assets/hero_portraits/ |
官网横版头像(上分帝 Web;默认 wide 面部构图,非匹配模板) |
web/assets/attr_icons/ |
官网主属性图标(力量/敏捷/智力/全才,上分帝 Web 用) |
web/assets/role_icons/ |
Valve 选人定位筛选图标(透明 PNG;英雄页定位栏;本地 /role-icon/,线上 OSS role-icon/) |
web/assets/rank_icons/ |
天梯勋章图标(OpenDota rank_icon_1..8 先锋→冠绝;走势段位选择器) |
web/assets/item_icons/ |
Steam CDN 装备图标(上分帝 Web常用装备) |
web/assets/item_cat_icons/ |
官网商店分类图标(itemcat_*.png,物品页列头) |
web/assets/ability_icons/ |
Steam CDN 技能图标(上分帝 Web;按需缓存,目录 gitignore);例外提交共用徽章 innate.png / talent_tree.png(先天/天赋树;CDN 无独立图标时前端与本地 serve 回退用) |
web/assets/ui_icons/ |
dota2.com.cn 通用 UI 图标(cooldown.png 等;icon_damage.png 等战斗属性图标;平台 logo) |
web/assets/streamer_avatars/ |
主播头像缓存(由 fetch_streamers.py 写入;上分帝 Web「主播」) |
web/assets/streamer_videos/ |
主播精选高光 mp4 + 同名 JPG 封面(手工放入;上分帝 Web「主播」卡片;勿提交,线上走 OSS streamer-video/) |
web/assets/ability_videos/ |
官网技能演示视频(webm/mp4;勿提交,由脚本拉取;线上由阿里云 OSS climperor 桶 ability-video/ 前缀托管) |
运行时产物(勿提交,见 .gitignore):
pc/samples/raw/<matchid>/— GSI 会话截图与gsi.jsonl;手动capture.py可写在raw/根下pc/player_pages/— 赛后玩家主页/比赛 JSON(OpenDota;默认私有;同意公开后另存 OSSplayers/)pc/preview/— 标定 / sheet 预览web/dist/— 静态站点导出(export_relations_site.py)pc/results/— 每局 JSONpc/failures/—--truth调试用错识裁切shared/data/synergies.json/stratz_matchups.json/relations_audit.json等 — 可再生成缓存与报告web/data/hero_stats.json/hero_matches.json/stratz_*.json等 — 可再生成缓存web/assets/ability_videos/— 官网技能演示(体积大,可再拉取)web/assets/streamer_videos/— 主播精选高光(体积大,手工放入后 OSS 同步)
数据流
Dota 2 GSI → pc/gsi_watch.py (:3223)
→ 全量 payload → pc/samples/raw/<matchid>/gsi.jsonl(可关)
→ DraftSession 轮询截屏 → pc/samples/raw/<matchid>/
→ recognize_image (CDN 模板 + 可选天梯遮罩)
→ roles(分路字)/ shared.grid(禁用 + 单元格)/ modes
→ recommend 克/搭/补(relations + draft_archetypes 规则画像,本人槽位只信 GSI)
→ overlay 网格克/搭/补 + 阵容分析条;本人锁定后改推装备图标条(可选)
→ pc/results/draft_*.json + 终端时间线
→ POST_GAME:player_pages 轮询 OpenDota → pc/player_pages/(可选 publish → OSS players/)
技术约束(修改前必读)
- 合规:仅 GSI + 屏幕截图;禁止读进程内存、注入、绕过 VAC。
- GSI 范围:普通玩家视角拿不到双方 pick;GSI 只作阶段触发、
team_slot、本人hero、本机accountid/steamid。本人顶栏槽位只信 GSI,不用截屏名字亮度猜测。 - 克制 / 搭档数据:机制克制/搭档只用定性边存
shared/data/relations.json(克制有向 + 搭档无向 + 理由),不要用胜率/场次表达机制克制,也不要写入shared/data/heroes.json。Web 英雄页三视图:克制 / 被克制 / 搭档。OpenDota 段位胜率/场次单独存web/data/hero_stats.json(近约 7 天窗;上场率 = 出场/(Σ出场/10);冠绝样本过小时与超凡合并),仅上分帝 Web 英雄详情「走势」无 STRATZ 时兜底。STRATZ 周胜率/分路/meta_board存web/data/stratz_hero_meta.json(三卡与分路均取 当前选中勋章 · 最近 1 周winWeek;近 8 周列表从新到旧;分路在 8 周列表上方),对位 Top 存web/data/stratz_matchup_tops.json(英雄详情「走势」与顶层「走势」榜/trends优先用 STRATZ;「对位」Tab 只读),禁止合并进 relations/heroes,禁止pc/recommend.py读取。同英雄近期比赛出装/加点单独存web/data/hero_matches.json,仅 Web「近期比赛」Tab,禁止进 recommend。Valve Immortal 四区榜单独存web/data/leaderboards.json,仅 Web「排行」页选手榜,禁止进 recommend。主播目录单独存web/data/streamers.json(手工名单 + 抖音 profile 补全;平台含抖音 / B 站 / 斗鱼),仅 Web 顶层「主播」与英雄详情「主播」Tab,禁止进 recommend;由refresh_webdaily 软失败刷新粉丝等字段(不阻断整档)。 - 装备机制 / 怕的装备:标签与技能汇总来自 Valve/OpenDota 自动抽取 +
item_tag_overrides.json;item_fears.py规则映射到英雄弱点。fetch_item_counter_stats.py的对阵购买率/胜率差仅作为观测证据:须减去同装备全局基线,对全部英雄的已有机制候选只允许小幅调序;统计发现的新组合即使同时满足games≥100、购买率提升≥3pp、条件胜率差≥1.5pp、两项双比例检验z≥1.96,仍须确认机制成立后手工写入 overrides,禁止直接把高相关当因果克制。大段技能文案只进web/data/hero_abilities.json/items_meta.json,不要塞进heroes.json。Web「怕」行只读hero_item_fears.json;禁止把 fears / counter_stats 统计胜率写入pc/recommend.py或pc/item_suggest.py评分。PC 局内出装仅允许:pc/item_suggest.py在本人锁定后只读hero_items.json(核心相对热度)+ 定性敌方 tags/画像规则表 +items_meta名称 + 本地item_icons;仍禁止 STRATZ / hero_stats / matches。核心装「使用率」为hero_items列表内相对热度归一化,非绝对出场率、无段位维度。技能/物品「施加」类 tags(机制查询页)与技能dispellable(效果能否被驱散)严格区分;机制 tags 禁止写入 relations。 - 机制查询页:顶层
/mechanics[/{effect}];侧栏弱/强驱散 + 核心控制;结果为施加该效果的技能与物品。数据边界:仅 Web。 - 模板策略:只维护 CDN 层。皮肤问题用会话策略(选人可改判、决策
allow_revise=False、best 帧偏 HERO_SELECTION),不要为皮肤加模板库,也不要复活 real 双层库。 - 顶栏时机:皮肤在全员选完后才上顶栏;本机进决策时别人可能还在选——须保持
strategy_tail视觉,禁止「一进 STRATEGY 就永久停读」。 - 坐标:一律相对坐标(相对屏幕高 / 相对中心),勿写死像素分辨率。
- 宁可不认,不可乱认:
min_score+min_margin双门控;不确定就null。 - 平台:面向 Windows;截屏依赖无边框/窗口模式。
- 上分帝 Web 定时刷新:Gitea Actions(self-hosted)跑
web/refresh_web.py;易变 STRATZ/stats/主播粉丝等 不回写 git,由web/refresh_cache.py跨 checkout 保存增量状态。生成 JSON 必须原子替换;HTTP 200 空数据不得覆盖旧缓存;只有业务字段变化才部署,资产变化必须先成功同步 OSS。每轮REFRESH_SUMMARY作为 Actions artifact,飞书以 workflow + summary + 生产 freshness 三联校验。数据-only 刷新不 bumpSITE_VERSION。技能视频与手工relations.json不进定时。禁止把 STRATZ / hero_stats / matches / leaderboards / streamers 写入 recommend。 - PC 赛后玩家页:默认私有,写
pc/player_pages/;仅player_pages.public_share=true时由边缘 ingest 写 OSSplayers/(PC 不内置 OSS 密钥)。Web/players/{account_id}[/{match_id}]本机优先/api/players/...,公网读 OSS;禁止塞进主data.json,禁止进 recommend。需玩家开启「公开比赛数据」。
开发命令
pip install -r pc/requirements.txt
pip install -r web/requirements.txt # 可选:本机 refresh_web / OSS(CI 会装)
python pc/fetch_cdn_templates.py
python shared/import_relations_xlsx.py # 可选:从选将笔记 xlsx 导入关系
python web/fetch_hero_portraits.py # 官网横版头像(上分帝 Web)
python web/fetch_hero_items.py # OpenDota 热门装备缓存(上分帝 Web)
python web/fetch_hero_stats.py # OpenDota 各段位胜率/场次(上分帝 Web 走势兜底)
python web/fetch_stratz_meta.py # STRATZ 周胜率/分路/对位 Top(走势/对位/Meta;需 token)
python web/fetch_hero_matches.py # 同英雄近期比赛出装/加点(上分帝 Web;可 --heroes antimage;--workers 6;--enrich-item-times)
python web/fetch_pro_matches.py # 明星选手近期联赛对局(默认 watchlist;--include-pubs 含天梯;--refresh-limit 15 轮换;可 --players Ame,898754153)
python web/fetch_leaderboards.py # Valve Immortal 四区 Top100(排行页)
python web/fetch_streamers.py # 抖音主播主页补全(主播页;手工名单;亦由 daily 定时软失败刷新)
python web/fetch_streamer_live.py # 探测真实在播状态回写 is_live(主播页角标;daily;--dry-run 只打印)
python web/fetch_item_shop.py # 商店分类目录(物品页)
python web/fetch_items_meta.py # 装备描述 + 机制标签
python web/fetch_hero_abilities.py # 英雄技能 / 驱散汇总
python web/fetch_hero_abilities.py --tags-only # 只重算技能「施加」类 tags
python web/fetch_hero_abilities.py --icons-only # 缓存技能图标到 web/assets/ability_icons/
python web/fetch_ability_videos.py # 官网技能演示(限速;默认 webm)
python web/fetch_item_counter_stats.py # OpenDota 对阵装备相对全局基线(可再生成缓存)
python web/item_fears.py # 英雄怕的装备
python web/fetch_patches.py # 近一年版本日志 + 详情(版本页)
python web/fetch_patches.py --check # 只比对列表与本地 details,stdout JSON(has_new)
python web/refresh_web.py --tier patch --skip-deploy --skip-oss # 本机试跑某档(daily/weekly/patch/all)
python web/serve_relations.py # 上分帝 Web 本地开发服务(英雄 / 排行 / 主播 / 走势 / 机制 / 物品 / 版本)
python web/export_relations_site.py # 导出静态站点到 web/dist/relations/(可部署 Pages)
python web/export_relations_site.py --ability-video-base https://climperor.oss-cn-shanghai.aliyuncs.com --static-asset-base https://climperor.oss-cn-shanghai.aliyuncs.com
python web/deploy_relations.py # 部署 web/dist/relations 到 Cloudflare Pages(凭据经 keyzoo 注入或 env;默认 dota2.refining.dev;视频与图标走 OSS)
python web/notify_site_traffic.py --dry-run # 上分帝 Web 日活摘要(飞书;需 CF + webhook env / keyzoo)
python web/_oss_static_assets.py upload # 图标/portrait 变更后同步 OSS(keyzoo voson-RAM)
python web/_cf_status.py # 只读查 CF Pages 部署 / 自定义域名状态
# Gitea Actions(self-hosted):web-daily / web-weekly / web-patch;Secrets 见 README「定时刷新」与各 workflow 头注释
python pc/gsi_setup.py --check
python pc/gsi_watch.py --once
python pc/recognize.py pc/samples/raw/<帧>.png --sheet
python pc/evaluate.py
修改原则
- 只改任务所需逻辑,避免无关重构与大范围格式化。
- 代码标识符、日志、错误信息、注释用英文;用户可见摘要可用中文。
- 改匹配阈值或裁切时,用
pc/evaluate.py/ 标注帧验证,并更新CHANGELOG.md与必要时的ARCHITECTURE.md。 - 改上分帝 Web 视觉(色板、字号、间距、圆角、组件态)时先对齐
DESIGN.md令牌,再改web/frontend/style.css;勿引入未入规范的硬编码尺度。 - 改 GSI cfg 时同步核对
pc/gsi_setup.py与 Dotagamestate_integration目录。 - 改 Web 路由形态(URL 段 / query 参数 / 默认值)时同步
web/frontend/router.js(parseHash/serializeHash)与app.js的applyPatch校验;新增可路由状态维度时在两处都加,并在syncStateToUrl调用点(含搜索 debounce)接好。History 深度链接依赖web/frontend/_redirects(Cloudflare)与web/serve_relations.py的 SPA fallback(含players);预渲染路径变更时同步web/seo_prerender.py与导出文件列表(含router.js/_redirects/robots.txt)。 - 改
shared/data/heroes.json结构时同步shared/grid.py(依赖attr/name_loc);aliases为中文口语/俗称(勿与name_loc重复),重跑pc/fetch_cdn_templates.py会按key合并保留;tags为中文定位(核心/辅助/…/幻象,由roles+幻想系推导),上分帝 Web 筛选 + 局内draft_archetypes/item_suggest缺口与应对装共用;基础属性/血蓝等数值字段供上分帝 Web 详情条,勿塞机制文案。 - 发版上分帝 Web 时同步 bump
web/export_relations_site.py的SITE_VERSION、web/frontend/config.js的同名变量,以及index.html里style.css/mobile-gate.js/config.js/router.js/app.js的?v=缓存戳;并写CHANGELOG.md。SITE_VERSION语义:末位 = 增量 UI/修复;中段 = 壳层 / 路由 / 可索引或其它阶段性能力成型(如0.5.x→0.6.0);数据-only 刷新不 bump。 - 不要重新引入 real 模板双层库、
cdn_penalty、build_library.py。
文档分工
| 文件 | 内容 |
|---|---|
README.md |
安装与操作手册 |
ARCHITECTURE.md |
背景、选型、关键实测与决策记录 |
DESIGN.md |
上分帝 Web 视觉设计规范(令牌 + 使用规则;design.md 格式) |
AGENTS.md |
本文件:给协作者 / AI 的约束 |
CHANGELOG.md |
面向用户的版本变更 |
与 dota2-hex
本仓库独立演进。并入或旁路对接时,仍须遵守 dota2-hex/AGENTS.md 的合规边界。