PrimeBot 启元 Q1 & T1 技能整活工作台
BUILD AUTOMATION · 偷懒神器一号

“一源双包”影分身构建器源码精讲

改一份源码,1 秒钟自动吐出 Q1 和 T1 共 4 个 .rpk 安装包与 4 份上架 JSON

😜 调皮课代表大白话划重点(30 秒读懂核心)
到了活动现场才揭晓分到的是 Q1 还是 T1?要是维护两套代码,改句台词还得两边同步,太容易漏改翻车了!所以这个脚本施展了「影分身之术」:在临时目录里把你的源码复印两份,一份自动把 ROBOT_MODEL 和 pkg.yaml 改成 q1(机型 ID 100),另一份改成 t1(机型 ID 101),一口气全打包好放在 Tools/dist/ 等你翻牌!

🔍 核心机关与修改指南

📋 SKILL_SPECS 清单 双技能同构

同时管理 energy_station(社交能量补给站)和 universal_skill(启元百变互动官)两套王牌技能。

🔄 自动改写机型变量 防手抖设计

精准校验源码里恰好有一处 ROBOT_MODEL = "q1",并在构建 T1 包时自动替换成 "t1",绝不污染原文件。

📝 自动配对上架小抄 publish_*.json

自动生成包含双字段(categoryIds/CodeList、robotModelIds/CodeList)的 JSON 文件,彻底杜绝发布时报 1001 缺字段错误!

💻 完整源码与配置内容(共 97 行 · 支持行号定位与一键复制)

📄 共 97 行
1"""从 Example/ 下的技能源码构建 Q1/T1 双机型 .rpk 包及可审阅的发布元数据。"""
2
3import importlib.util
4import json
5import shutil
6import tempfile
7from pathlib import Path
8
9ROOT = Path(__file__).resolve().parents[1]
10WORKSPACE = ROOT.parent
11DEV_KIT = ROOT / "primehub_dev_kit"
12EXAMPLE_ROOT = WORKSPACE / "Example"
13OUTPUT = ROOT / "dist"
14
15SKILL_SPECS = (
16 {
17 "folder": "energy_station",
18 "pkg_prefix": "cans518_energy_station",
19 "publish_prefix": "publish",
20 "title": "社交能量补给站",
21 "short_desc": "三格好心情,给每个敢动手的人充个电",
22 "long_desc_tpl": (
23 "离线小剧场:开场、勇气/灵感/快乐三格充电、谢幕。"
24 "每格短台词配动作,无需联网或语音输入。"
25 "仅适配%s,使用预设动作,运行时请留出周围1米空间。"
26 "基于官方示例改编;台词和编排可在源码中修改。"
27 ),
28 "icon": "skill-game.svg",
29 },
30 {
31 "folder": "universal_skill",
32 "pkg_prefix": "cans518_universal_skill",
33 "publish_prefix": "publish_universal",
34 "title": "启元百变互动官",
35 "short_desc": "迎宾问候+灵感广播+好运盲盒抽签的通用互动剧场",
36 "long_desc_tpl": (
37 "Q1/T1通用互动技能模板:包含元气迎宾、每日灵感金句(支持联网API与断网自动兜底)、"
38 "随机好运抽签及谢幕。仅适配%s,内置形态检测与TTS防抖轮询,运行时请留出周围1米空间。"
39 ),
40 "icon": "skill-verify.svg",
41 },
42)
43
44
45def build_skill(pack_module, spec_cfg: dict) -> None:
46 source_dir = EXAMPLE_ROOT / spec_cfg["folder"]
47 for model, model_id, model_code in (("q1", 100, "robot_q"), ("t1", 101, "robot_t")):
48 with tempfile.TemporaryDirectory(prefix="qiyuan_build_") as staging:
49 skill_dir = Path(staging) / (spec_cfg["folder"] + "_" + model)
50 shutil.copytree(source_dir, skill_dir, ignore=shutil.ignore_patterns("__pycache__", "*.pyc"))
51 script = skill_dir / "src/main.py"
52 source = script.read_text(encoding="utf-8")
53 assignment = 'ROBOT_MODEL = "q1"'
54 if source.count(assignment) != 1:
55 raise ValueError("期望恰好一个 ROBOT_MODEL 声明: " + str(script))
56 script.write_text(source.replace(assignment, 'ROBOT_MODEL = "%s"' % model), encoding="utf-8")
57 config = skill_dir / "cfg/pkg.yaml"
58 package_name = "%s_%s_20261024" % (spec_cfg["pkg_prefix"], model)
59 lines = []
60 for line in config.read_text(encoding="utf-8").splitlines():
61 if line.startswith("package_name:"):
62 line = "package_name: " + package_name
63 elif line.startswith("robot_model:"):
64 line = "robot_model: " + model
65 lines.append(line)
66 config.write_text("\n".join(lines) + "\n", encoding="utf-8")
67 pack_module.pack(skill_dir, OUTPUT)
68 metadata = {
69 "skillName": spec_cfg["title"] + " " + model.upper(),
70 "version": "0.1.0",
71 "skillDesc": spec_cfg["short_desc"],
72 "long_description": spec_cfg["long_desc_tpl"] % model.upper(),
73 "categoryIds": [104],
74 "categoryCodeList": ["action"],
75 "tagsIds": [127],
76 "tagsCodeList": ["companion"],
77 "robotModelIds": [model_id],
78 "robotModelCodeList": [model_code],
79 "iconUrl": spec_cfg["icon"],
80 }
81 publish_file = "%s_%s.json" % (spec_cfg["publish_prefix"], model)
82 (OUTPUT / publish_file).write_text(
83 json.dumps(metadata, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
84 )
85
86
87def build() -> None:
88 spec = importlib.util.spec_from_file_location("primehub_pack", DEV_KIT / "pack.py")
89 pack_module = importlib.util.module_from_spec(spec)
90 spec.loader.exec_module(pack_module)
91 OUTPUT.mkdir(exist_ok=True)
92 for spec_cfg in SKILL_SPECS:
93 build_skill(pack_module, spec_cfg)
94
95
96if __name__ == "__main__":
97 build()