← Back to Skills Marketplace
fanyin1

Sillytavern Charactecard

by FanYin1 · GitHub ↗ · v1.0.0 · MIT-0
cross-platform ✓ Security Clean
318
Downloads
1
Stars
0
Active Installs
1
Versions
Install in OpenClaw
/install charactercard
Description
完美兼容 SillyTavern 角色卡的导入、解析、导出功能。支持 V1/V2/V3 规范,支持 PNG 嵌入式 JSON 和纯 JSON 格式。当用户需要导入、查看、编辑或导出 SillyTavern 角色卡时使用此技能。
README (SKILL.md)

🎭 SillyTavern 角色卡处理

完整支持 SillyTavern 角色卡的导入、解析、转换和导出功能。

📋 功能概览

功能 说明
导入 PNG 角色卡 从 PNG 图片中提取嵌入的 JSON 数据
导入 JSON 角色卡 直接解析 JSON 格式的角色卡
格式检测 自动识别 V1/V2/V3 规范
数据验证 验证角色卡数据完整性
格式转换 V1/V2/V3 之间的格式转换
导出 PNG 将角色卡数据嵌入到 PNG 图片中
导出 JSON 导出为标准 JSON 格式

🎯 使用场景

1. 导入角色卡

当用户说:

  • "导入这个角色卡"
  • "读取这个 PNG 文件"
  • "解析这个角色"
  • "加载 SillyTavern 角色"

你应该

  1. 使用 read 工具读取文件
  2. 检测文件格式(PNG 或 JSON)
  3. 提取角色卡数据
  4. 验证数据完整性
  5. 展示角色信息

2. 查看角色信息

当用户说:

  • "这个角色是谁"
  • "显示角色详情"
  • "角色的设定是什么"

你应该: 展示角色的关键信息:

  • 名称 (name)
  • 描述 (description)
  • 人格 (personality)
  • 场景 (scenario)
  • 第一条消息 (first_mes)
  • 示例对话 (mes_example)
  • 标签 (tags)
  • 创建者 (creator)

3. 导出角色卡

当用户说:

  • "导出为 PNG"
  • "保存为 JSON"
  • "生成角色卡文件"

你应该

  1. 确认导出格式(PNG 或 JSON)
  2. 确认规范版本(V2 或 V3)
  3. 生成文件
  4. 返回文件路径

📐 SillyTavern 角色卡规范

V1 格式(已弃用)

扁平结构,所有字段在顶层:

{
  "name": "角色名",
  "description": "角色描述",
  "personality": "人格特征",
  "scenario": "场景设定",
  "first_mes": "第一条消息",
  "mes_example": "对话示例"
}

V2 格式(推荐)

{
  "spec": "chara_card_v2",
  "spec_version": "2.0",
  "data": {
    "name": "角色名",
    "description": "角色描述",
    "personality": "人格特征",
    "scenario": "场景设定",
    "first_mes": "第一条消息",
    "mes_example": "对话示例",
    "creator_notes": "创建者备注",
    "system_prompt": "系统提示词",
    "post_history_instructions": "历史后指令",
    "tags": ["标签1", "标签2"],
    "creator": "创建者",
    "character_version": "1.0",
    "extensions": {}
  }
}

V3 格式(最新)

在 V2 基础上增加:

{
  "spec": "chara_card_v3",
  "spec_version": "3.0",
  "data": {
    ...V2字段,
    "alternate_greetings": ["备选问候1", "备选问候2"],
    "creator_notes_multilingual": {
      "zh-CN": "中文备注",
      "en-US": "English notes"
    },
    "source": "角色来源",
    "group_only_greetings": ["群组专用问候"],
    "creation_date": 1234567890,
    "modification_date": 1234567890,
    "assets": {
      "avatar": "base64_image_data",
      "background": "base64_image_data"
    }
  }
}

🔧 核心功能实现

1. PNG 格式检测

// 检测文件是否为 PNG
function isPNG(buffer) {
  const pngSignature = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
  for (let i = 0; i \x3C 8; i++) {
    if (buffer[i] !== pngSignature[i]) return false;
  }
  return true;
}

2. 从 PNG 提取 JSON

PNG 文件由多个 chunk 组成,角色卡数据存储在 tEXtiTXt chunk 中,关键字为 "chara"

function extractCharaFromPNG(buffer) {
  let offset = 8; // 跳过 PNG 签名

  while (offset \x3C buffer.length) {
    // 读取 chunk 长度(4 字节,大端序)
    const length = buffer.readUInt32BE(offset);
    offset += 4;

    // 读取 chunk 类型(4 字节)
    const type = buffer.toString('ascii', offset, offset + 4);
    offset += 4;

    if (type === 'tEXt' || type === 'iTXt') {
      // 读取关键字(以 null 结尾)
      let keywordEnd = offset;
      while (buffer[keywordEnd] !== 0) keywordEnd++;
      const keyword = buffer.toString('utf8', offset, keywordEnd);

      if (keyword === 'chara') {
        // 提取数据(跳过 null 字节)
        const dataStart = keywordEnd + 1;
        const dataEnd = offset + length;
        const data = buffer.toString('utf8', dataStart, dataEnd);

        // 尝试 base64 解码
        try {
          return JSON.parse(Buffer.from(data, 'base64').toString('utf8'));
        } catch {
          return JSON.parse(data);
        }
      }
    }

    // 跳到下一个 chunk(数据 + 4 字节 CRC)
    offset += length + 4;

    // IEND chunk 表示文件结束
    if (type === 'IEND') break;
  }

  return null;
}

3. 格式规范化

function normalizeCharacterCard(data) {
  // 检测版本
  if (data.spec === 'chara_card_v2' || data.spec === 'chara_card_v3') {
    return data; // 已经是 V2/V3 格式
  }

  // V1 转 V2
  return {
    spec: 'chara_card_v2',
    spec_version: '2.0',
    data: {
      name: data.name || '',
      description: data.description || '',
      personality: data.personality || '',
      scenario: data.scenario || '',
      first_mes: data.first_mes || '',
      mes_example: data.mes_example || '',
      creator_notes: data.creator_notes || '',
      system_prompt: data.system_prompt || '',
      post_history_instructions: data.post_history_instructions || '',
      tags: data.tags || [],
      creator: data.creator || '',
      character_version: data.character_version || '1.0',
      extensions: data.extensions || {}
    }
  };
}

4. 数据验证

function validateCharacterCard(data) {
  const errors = [];

  // 检查必需字段
  if (!data.name || data.name.trim() === '') {
    errors.push('缺少角色名称 (name)');
  }

  if (!data.description || data.description.trim() === '') {
    errors.push('缺少角色描述 (description)');
  }

  // 检查字段类型
  if (data.tags && !Array.isArray(data.tags)) {
    errors.push('标签 (tags) 必须是数组');
  }

  if (data.extensions && typeof data.extensions !== 'object') {
    errors.push('扩展 (extensions) 必须是对象');
  }

  return {
    valid: errors.length === 0,
    errors
  };
}

5. 导出为 PNG

function embedCharaInPNG(imageBuffer, characterData) {
  // 将角色数据转换为 base64
  const jsonStr = JSON.stringify(characterData);
  const base64Data = Buffer.from(jsonStr, 'utf8').toString('base64');

  // 创建 tEXt chunk
  const keyword = 'chara';
  const keywordBuffer = Buffer.from(keyword + '\0', 'utf8');
  const dataBuffer = Buffer.from(base64Data, 'utf8');

  const chunkLength = keywordBuffer.length + dataBuffer.length;
  const chunk = Buffer.alloc(chunkLength + 12);

  // 写入 chunk 长度
  chunk.writeUInt32BE(chunkLength, 0);

  // 写入 chunk 类型
  chunk.write('tEXt', 4, 'ascii');

  // 写入关键字和数据
  keywordBuffer.copy(chunk, 8);
  dataBuffer.copy(chunk, 8 + keywordBuffer.length);

  // 计算 CRC
  const crc = calculateCRC(chunk.slice(4, 8 + chunkLength));
  chunk.writeUInt32BE(crc, 8 + chunkLength);

  // 在 IEND 之前插入 chunk
  const iendIndex = imageBuffer.indexOf(Buffer.from('IEND', 'ascii')) - 4;
  return Buffer.concat([
    imageBuffer.slice(0, iendIndex),
    chunk,
    imageBuffer.slice(iendIndex)
  ]);
}

📝 使用示例

示例 1:导入 PNG 角色卡

用户:导入这个角色卡 /path/to/character.png

你的回复:
好的,让我读取这个角色卡文件。

[使用 read 工具读取文件]
[检测为 PNG 格式]
[提取 chara 数据]
[解析 JSON]
[验证数据]

✅ 成功导入角色卡!

📋 角色信息:
• 名称:艾莉娅
• 规范:V2 (chara_card_v2)
• 创建者:某某
• 标签:精灵、魔法、友善

📖 描述:
艾莉娅是一位来自精灵王国的年轻魔法师...

🎭 人格:
善良、好奇、热爱学习...

🌍 场景:
在一个充满魔法的世界中...

💬 第一条消息:
"你好!我是艾莉娅,很高兴认识你!"

示例 2:导出为 PNG

用户:把这个角色导出为 PNG 格式

你的回复:
好的,我将角色卡数据嵌入到 PNG 图片中。

[读取角色头像或创建默认图片]
[将角色数据转换为 JSON]
[嵌入到 PNG tEXt chunk]
[保存文件]

✅ 导出成功!

文件路径:~/.openclaw/workspace/艾莉娅_v2.png
文件大小:245 KB
格式:PNG with embedded chara_card_v2

你可以将这个文件导入到 SillyTavern 或其他兼容的应用中使用。

示例 3:格式转换

用户:把这个 V1 格式的角色卡转换为 V3

你的回复:
好的,我将 V1 格式转换为 V3 格式。

[读取 V1 数据]
[规范化为 V2 结构]
[添加 V3 特有字段]
[验证数据]

✅ 转换完成!

变更:
• spec: V1 → chara_card_v3
• spec_version: → 3.0
• 新增字段:
  - alternate_greetings (空数组,可手动添加)
  - creator_notes_multilingual (空对象,可添加多语言备注)
  - creation_date (当前时间戳)
  - modification_date (当前时间戳)

是否需要导出为文件?

⚠️ 重要注意事项

1. 文件路径

  • 必须使用绝对路径/home/user/.openclaw/workspace/character.png
  • 禁止相对路径./character.png

2. 字符编码

  • PNG chunk 数据使用 UTF-8 编码
  • JSON 数据使用 base64 编码后存储在 PNG 中
  • 确保正确处理多字节字符(中文、日文等)

3. 数据大小限制

  • 角色卡 JSON:建议 \x3C 10 MB
  • 描述字段:建议 \x3C 500 KB
  • 头像图片:建议 \x3C 15 MB(base64 编码)

4. 兼容性

  • V1 → V2/V3:完全兼容,自动转换
  • V2 → V3:兼容,添加新字段
  • V3 → V2:兼容,移除 V3 特有字段

5. 世界书(World Book)

如果角色卡包含 character_book 字段,需要保留:

{
  "data": {
    ...其他字段,
    "character_book": {
      "name": "世界书名称",
      "entries": [
        {
          "keys": ["关键词1", "关键词2"],
          "content": "触发内容",
          "enabled": true,
          "insertion_order": 100
        }
      ]
    }
  }
}

🚫 错误处理

常见错误

错误 原因 解决方案
"不是有效的 PNG 文件" 文件签名不匹配 检查文件是否损坏
"未找到 chara 数据" PNG 中没有嵌入角色卡 确认是否为角色卡 PNG
"JSON 解析失败" 数据格式错误 检查 JSON 语法
"缺少必需字段" 角色卡数据不完整 补充必需字段
"base64 解码失败" 编码格式错误 检查数据编码

错误示例

错误:说"无法读取角色卡"而不尝试 ❌ 错误:只显示原始 JSON 而不解析 ❌ 错误:忽略数据验证错误

正确:尝试读取并提供详细的错误信息 ✅ 正确:解析并格式化显示角色信息 ✅ 正确:验证数据并提示用户修复问题

🎯 快速参考

任务 步骤
导入 PNG read → 检测格式 → 提取 chara → 解析 JSON → 验证
导入 JSON read → 解析 JSON → 规范化 → 验证
查看信息 提取 data 字段 → 格式化显示
导出 PNG 准备图片 → 嵌入 JSON → 保存文件
导出 JSON 规范化数据 → 序列化 → 保存文件
格式转换 检测版本 → 规范化 → 添加/移除字段 → 验证

📚 相关资源


提示:这个技能提供了完整的 SillyTavern 角色卡处理能力,你可以自信地处理用户的所有角色卡相关请求!

Usage Guidance
This skill appears coherent and focused on SillyTavern character-card handling and does not ask for credentials or network access. Before installing, review the full utils.js file (the listing provided here is truncated) to confirm there are no hidden network calls, child_process usage, or arbitrary file writes in the remaining code. As a general precaution, only use the skill with character files you trust or test with sample inputs, since parsing malformed images/JSON could trigger runtime errors. If you plan to run this in a sensitive environment, run it in a sandbox or inspect the entire codebase first.
Capability Analysis
Type: OpenClaw Skill Name: charactercard Version: 1.0.0 The skill bundle provides legitimate functionality for importing, parsing, and exporting SillyTavern character cards in PNG and JSON formats. The code in utils.js implements standard PNG chunk manipulation and JSON normalization without any signs of malicious intent, data exfiltration, or obfuscation. The SKILL.md instructions are purely functional and align with the stated purpose of character card management.
Capability Assessment
Purpose & Capability
Name/description describe SillyTavern character-card import/export and the included utils.js implements PNG/JSON detection, extraction, normalization, validation, and embedding—functionality coherent with the stated purpose. Required binaries/env/config are none, which is appropriate.
Instruction Scope
SKILL.md instructs the agent to read user-provided files, detect format, extract/validate data, display info, and produce files—all within the expected scope. It does not instruct reading unrelated system files or sending data to external endpoints.
Install Mechanism
No install spec; the skill is instruction+utility code only. The bundled utils.js uses standard Node built-ins (fs, path, crypto). There are no downloads, package installs, or external sources in the provided files.
Credentials
The skill declares no environment variables, credentials, or config paths. The code shown does not reference process.env or external tokens and only operates on buffers/files supplied by the user.
Persistence & Privilege
always is false and the skill does not request permanent presence or modify other skills/system-wide settings. Autonomous invocation is allowed (platform default) but not combined with elevated privileges.
How to Use
  1. Make sure OpenClaw is installed (local or Docker)
  2. Run the install command in chat: /install charactercard
  3. After installation, invoke the skill by name or use /charactercard
  4. Provide required inputs per the skill's parameter spec and get structured output
Version History
v1.0.0
tavern-card v1.0.0 initial release - Full support for importing, parsing, converting, and exporting SillyTavern character cards. - Handles all V1/V2/V3 card specs, compatible with both PNG (embedded JSON) and pure JSON formats. - Automatic format detection, validation, and conversion between versions. - Supports extracting/embedding character data in PNG files via tEXt/iTXt chunks. - Documentation includes practical usage instructions and error handling guidance.
Metadata
Slug charactercard
Version 1.0.0
License MIT-0
All-time Installs 0
Active Installs 0
Total Versions 1
Frequently Asked Questions

What is Sillytavern Charactecard?

完美兼容 SillyTavern 角色卡的导入、解析、导出功能。支持 V1/V2/V3 规范,支持 PNG 嵌入式 JSON 和纯 JSON 格式。当用户需要导入、查看、编辑或导出 SillyTavern 角色卡时使用此技能。 It is an AI Agent Skill for Claude Code / OpenClaw, with 318 downloads so far.

How do I install Sillytavern Charactecard?

Run "/install charactercard" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.

Is Sillytavern Charactecard free?

Yes, Sillytavern Charactecard is completely free, licensed under MIT-0. You can download, install and use it at no cost.

Which platforms does Sillytavern Charactecard support?

Sillytavern Charactecard is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).

Who created Sillytavern Charactecard?

It is built and maintained by FanYin1 (@fanyin1); the current version is v1.0.0.

💬 Comments