#!/usr/bin/python
"""
File Name: chub_to_lorebook.py
Version: 1.1.0
Description: This script parses json chub files and converts to a NovelAI compatible Lorebook
Usage: python chub_to_lorebook.py <chub_json_file.json> [<user_name>]
Dependencies: This script requires Python 3.6 or later.
Latest Changes:
- Added automatic {{char}} substitution
- Added optional {{user}} substitution via a command line argument
"""
import json
import os
import re
import sys
import uuid
if len(sys.argv) < 2:
print("Usage: python chub_to_lorebook.py <chub_json_file.json>")
sys.exit(1)
source_file_path = sys.argv[1]
base_name, _ = os.path.splitext(source_file_path)
output_file_path = f"{base_name}.lorebook"
# Load the source JSON data
with open(source_file_path, 'r') as source_file:
source_data = json.load(source_file).get("data")
# Setting user and char name
user_name = sys.argv[2] if len(sys.argv) > 2 else None
char_name = source_data.get("name")
# base format for the final output
transformed_data = {
"lorebookVersion": 5,
"entries": [],
"settings": {"orderByKeyLocations": False},
"categories": []
}
regex_single_asterisk = re.compile(r"(?<!\*)\*(?!\*)")
regex_user = re.compile(r"\{\{user\}\}")
regex_char = re.compile(r"\{\{char\}\}")
#
# get description
#
description_text = source_data.get("description", "")
# add personality to description if its present and not in the description
personality_text = source_data.get("personality")
if (personality_text and (personality_text not in description_text)):
description_text += "\n" + personality_text
# substitute the user name if it was provided
if (user_name):
description_text = regex_user.sub(user_name, description_text)
# substitute the char name
description_text = regex_char.sub(char_name, description_text)
description_entry = {
"text": description_text,
"displayName": char_name,
"enabled": True,
"keys": [char_name]
}
transformed_data["entries"].append(description_entry)
#
# get credits
#
credits = {
"text": "",
"displayName": char_name + " Credits",
"enabled": False,
}
credits["text"] += "Author: " + source_data.get("creator")
credits["text"] += "\nImage: " + source_data.get("avatar")
credits["text"] += "\nLink: " + "http://www.chub.ai/characters/" + source_data.get("extensions").get("chub").get("full_path")
transformed_data["entries"].append(credits)
#
# create scenario category
#
scenarios_uuid = str(uuid.uuid4())
scenarios_category = {
"name": char_name + " Scenarios",
"id": scenarios_uuid,
"enabled": False,
}
transformed_data["categories"].append(scenarios_category)
#
# Prepare scenario numbering prefix
#
total_scenarios = len(source_data.get("alternate_greetings", [])) + 1
add_zero = total_scenarios > 9
#
# Get main scenario
#
scenario_text = regex_single_asterisk.sub("", source_data.get("first_mes", ""))
scenario_text = regex_char.sub(char_name, scenario_text)
# substitute the user name if it was provided
if (user_name):
scenario_text = regex_user.sub(user_name, scenario_text)
scenario_entry = {
"text": scenario_text,
"displayName": ("0" if add_zero else "") + "1. Scenario",
"enabled": False,
"category": scenarios_uuid,
}
transformed_data["entries"].append(scenario_entry)
#
# get mes_example (behavior) if provided
#
mes_example_text = source_data.get("mes_example", "")
if (mes_example_text):
mes_example_text = regex_char.sub(char_name, mes_example_text)
# substitute the user name if it was provided
if (user_name):
mes_example_text = regex_user.sub(user_name, mes_example_text)
mes_example_text = f"{char_name} behavior example:\n{mes_example_text}"
behavior_entry = {
"text": regex_single_asterisk.sub("", mes_example_text),
"displayName": char_name + " Scenario Behavior",
"enabled": True,
}
transformed_data["entries"].append(behavior_entry)
#
# get alternative scnearios/greetings
#
scenario_num = 2
for entry in source_data.get("alternate_greetings", []):
added_zero = "0" if add_zero and scenario_num < 10 else ""
scenario_text = regex_single_asterisk.sub("", entry)
# substitute the user name if it was provided
if (user_name):
scenario_text = regex_user.sub(user_name, scenario_text)
# substitute the char name
scenario_text = regex_char.sub(char_name, scenario_text)
transformed_entry = {
"text": scenario_text,
"displayName": f"{added_zero}{scenario_num}. Scenario",
"enabled": False,
"category": scenarios_uuid,
}
transformed_data["entries"].append(transformed_entry)
scenario_num += 1
#
# Write the transformed data to the new JSON file, named based on the input file
#
with open(output_file_path, 'w') as target_file:
json.dump(transformed_data, target_file, indent=4)
print(f"Transformation complete. Data written to {output_file_path}")