AMBRACE(拥爱)表情「市场」仓库:用远程索引模式分发表情包。
- 仓库结构:本目录是一份
index.json(远程索引)+packs/下的表情包 zip。 - 消费方:AMBRACE 后端读取本索引,用户可在 App 聊天页「表情面板 → 市场」里下载 / 卸载市场表情包。
- 本仓库只存放素材与索引,代码逻辑在
AICompanionServer后端(backend/app/services/emoji_market.py、backend/app/api/emojis.py)与聊天页前端。
AMBRACE-emoji/
├── index.json # 市场索引(数组)
├── packs/ # 各表情包 zip(manifest.json + 贴图 + icon)
│ └── my_pack.zip
└── scripts/
└── validate_pack.py # 本地校验单包 / 全量校验索引引用的 zip
index.json 是一个 JSON 数组,每个元素描述一个表情包:
[
{
"id": "my_pack",
"name": "我的表情包",
"description": "示例:日常可爱表情",
"version": "1.0.0",
"icon": "packs/my_pack/icon.png",
"file": "packs/my_pack.zip",
"sha256": "07a...(zip 的 SHA-256,64 位小写十六进制)",
"size": 123456,
"emoji_count": 8
}
]| 字段 | 必填 | 说明 |
|---|---|---|
id |
是 | 包唯一标识,仅允许 [A-Za-z0-9_-](与 zip 内 manifest.json 的 id 必须一致) |
name |
是 | 包显示名 |
description |
是 | 一句话描述 |
version |
是 | 语义化版本,如 1.0.0 |
icon |
是 | 相对本仓库根的图标路径(png/webp/jpg/jpeg,≤2MB) |
file |
是 | 相对本仓库根的 zip 路径;后端会把它解析为下载 URL |
sha256 |
是 | zip 文件的 SHA-256(小写十六进制) |
size |
是 | zip 字节数 |
emoji_count |
是 | 表情数(建议与 manifest 的 emojis 长度一致) |
file与icon用相对路径(相对仓库根,如packs/xxx.zip);后端取EMOJI_MARKET_URL所在目录拼接为完整 URL。若填完整https://地址则原样使用(域名需在白名单:raw.githubusercontent.com/github.com/objects.githubusercontent.com/codeload.github.com/githubusercontent.com)。
一个表情包是一个 zip,manifest.json 必须位于 zip 根目录,其余为贴图文件(png / webp / jpg / jpeg)+ 一张 icon:
my_pack.zip
├── manifest.json
├── icon.png
├── 开心.png
├── 难过.png
└── ...
manifest.json 字段:
{
"id": "my_pack",
"name": "我的表情包",
"description": "示例:日常可爱表情",
"version": "1.0.0",
"icon": "icon.png",
"emojis": [
{ "file": "开心.png", "name": "开心", "meaning": "表达开心的情绪" },
{ "file": "难过.png", "name": "难过", "meaning": "表达难过的情绪" }
],
"previews": [
{ "file": "banner.png", "caption": "预览横幅" }
]
}| 字段 | 必填 | 说明 |
|---|---|---|
id |
是 | 必须与索引条目的 id 一致;仅允许 [A-Za-z0-9_-] |
name |
是 | 包显示名 |
description |
是 | 一句话描述 |
version |
是 | 版本号 |
icon |
是 | 图标文件名(在 zip 根目录;png/webp/jpg/jpeg,≤2MB) |
emojis |
是 | 非空数组,每项 {file, name, meaning} 均必填 |
previews |
否 | 可选预览图数组,每项 {file, caption?} |
硬性约束(后端强校验,不满足会被拒装):
file不允许包含路径分隔符或..(只允许简单文件名)。- 贴图/图标扩展名白名单:
.png/.webp/.jpg/.jpeg。 - 单个贴图 ≤ 2MB;包内全部贴图(icon + emojis + previews)总重 ≤ 20MB。
- zip 内不允许路径穿越 / 符号链接 / 绝对路径 / 重名文件(解压后同名);总条目数 ≤ 800。
打包前先跑校验脚本(需要 Python 3.12+,无第三方依赖):
# 校验一个目录(含 manifest.json + 贴图),自动打包并给出 sha256 / size
python scripts/validate_pack.py packs/my_pack
# 校验一个已经打好的 zip
python scripts/validate_pack.py packs/my_pack.zip校验通过时会打印:
校验通过 pack_id=my_pack emoji=2 size=123456 sha256=07a...
把打印的 sha256 与 size 填进 index.json 即可。
- 新建
packs/<id>/目录,放manifest.json+ 贴图 +icon。 - 打包成
packs/<id>.zip(保留manifest.json在 zip 根)。 - 运行
python scripts/validate_pack.py packs/<id>得到sha256/size。 - 在
index.json补一条记录(id/name/description/version/icon/file/sha256/size/emoji_count)。 - 提交并开 PR;CI(
.github/workflows/validate.yml)会自动校验索引引用的 zip 是否存在且校验通过。