175 lines
5.0 KiB
Python
175 lines
5.0 KiB
Python
from pathlib import Path
|
|
import json
|
|
from typing import Dict, Optional, List
|
|
import os
|
|
import logging
|
|
import toml
|
|
import random
|
|
|
|
|
|
logger = logging.Logger(__name__)
|
|
PREFIX = "MOMMY"
|
|
|
|
|
|
RESPONSES_FILE = Path(__file__).parent / "responses.json"
|
|
ADDITIONAL_ENV_VARS = {
|
|
"pronoun": "PRONOUNS",
|
|
"role": "ROLES",
|
|
"emote": "EMOTES",
|
|
"mood": "MOODS",
|
|
}
|
|
|
|
def _get_xdg_config_dir() -> Path:
|
|
res = os.environ.get("XDG_CONFIG_HOME")
|
|
if res is not None:
|
|
return Path(res)
|
|
|
|
xdg_user_dirs_file = Path(os.environ.get("XDG_CONFIG_HOME") or Path(Path.home(), ".config", "user-dirs.dirs"))
|
|
xdg_user_dirs_default_file = Path("/etc/xdg/user-dirs.defaults")
|
|
|
|
def get_dir_from_xdg_file(xdg_file_path: Path, key_a: str) -> Optional[str]:
|
|
if not xdg_file_path.exists():
|
|
logger.info("config file not found in %s", str(xdg_file_path))
|
|
return
|
|
|
|
with xdg_file_path.open("r") as f:
|
|
for line in f:
|
|
if line.startswith("#"):
|
|
continue
|
|
|
|
parts = line.split("=")
|
|
if len(parts) > 2:
|
|
continue
|
|
|
|
key_b = parts[0].lower().strip()
|
|
value = parts[1].strip().split("#")[0]
|
|
|
|
if key_a.lower() == key_b:
|
|
return value
|
|
|
|
logger.info("key %s not found in %s", key_a, str(xdg_file_path))
|
|
|
|
res = get_dir_from_xdg_file(xdg_user_dirs_file, "XDG_CONFIG_HOME")
|
|
if res is not None:
|
|
return Path(res)
|
|
|
|
res = get_dir_from_xdg_file(xdg_user_dirs_default_file, "CONFIG")
|
|
if res is not None:
|
|
return Path(Path.home(), res)
|
|
|
|
|
|
res = get_dir_from_xdg_file(xdg_user_dirs_default_file, "XDG_CONFIG_HOME")
|
|
if res is not None:
|
|
return Path(Path.home(), res)
|
|
|
|
default = Path(Path.home(), ".config")
|
|
logging.info("falling back to %s", default)
|
|
return default
|
|
|
|
|
|
CONFIG_DIRECTORY = _get_xdg_config_dir() / "mommy"
|
|
CONFIG_FILES = [
|
|
CONFIG_DIRECTORY / "python-mommy.toml",
|
|
CONFIG_DIRECTORY / "mommy.toml",
|
|
]
|
|
COMPILED_CONFIG_FILE = CONFIG_DIRECTORY / "responses.json"
|
|
|
|
def _load_config_file(config_file: Path) -> Optional[Dict[str, List[str]]]:
|
|
global CONFIG
|
|
if not config_file.exists():
|
|
return None
|
|
|
|
with config_file.open("r") as f:
|
|
data = toml.load(f)
|
|
|
|
result = {}
|
|
for key, value in data.items():
|
|
if isinstance(value, str):
|
|
result[key] = [value]
|
|
else:
|
|
result[key] = value
|
|
|
|
return result
|
|
|
|
|
|
ADDITIONAL_PROGRAM_PREFIXES = [
|
|
"cargo", # only as fallback if user already configured cargo
|
|
]
|
|
|
|
def _get_env_var_names(name: str):
|
|
BASE = PREFIX + "_" + name.upper()
|
|
yield "PYTHON_" + BASE
|
|
yield BASE
|
|
for a in ADDITIONAL_PROGRAM_PREFIXES:
|
|
yield a + "_" + BASE
|
|
|
|
def _get_env_value(name: str) -> Optional[str]:
|
|
if name in ADDITIONAL_ENV_VARS:
|
|
for key in _get_env_var_names(ADDITIONAL_ENV_VARS[name]):
|
|
val = os.environ.get(key)
|
|
if val is not None:
|
|
return val
|
|
|
|
for key in _get_env_var_names(name):
|
|
val = os.environ.get(key)
|
|
if val is not None:
|
|
return val
|
|
|
|
|
|
|
|
def compile_config():
|
|
global RESPONSES_FILE
|
|
|
|
data = json.loads(RESPONSES_FILE.read_text())
|
|
config_definition: Dict[str, dict] = data["vars"]
|
|
mood_definitions: Dict[str, dict] = data["moods"]
|
|
|
|
# environment variables for compatibility with cargo mommy
|
|
# fill ADDITIONAL_ENV_VARS with the "env_key" values
|
|
for key, conf in config_definition.items():
|
|
if "env_key" in conf:
|
|
ADDITIONAL_ENV_VARS[key] = conf["env_key"]
|
|
|
|
# set config to the default values
|
|
config: Dict[str, List[str]] = {}
|
|
for key, conf in config_definition.items():
|
|
config[key] = conf["defaults"]
|
|
|
|
# load config file
|
|
config_file_data: Optional[Dict[str, List[str]]]
|
|
for c in CONFIG_FILES:
|
|
config_file_data = _load_config_file(c)
|
|
if config_file_data is not None:
|
|
break
|
|
|
|
if config_file_data is not None:
|
|
config.update(config_file_data)
|
|
|
|
|
|
# fill config with env
|
|
for key, conf in config_definition.items():
|
|
val = _get_env_value(key)
|
|
if val is not None:
|
|
config[key] = val.split("/")
|
|
|
|
# validate moods
|
|
for mood in config["mood"]:
|
|
if mood not in mood_definitions:
|
|
supported_moods_str = ", ".join(mood_definitions.keys())
|
|
print(f"{random.choice(config['role'])} doesn't know how to feel {mood}... {random.choice(config['pronoun'])} moods are {supported_moods_str}")
|
|
exit(1)
|
|
|
|
# compile
|
|
compiled = {}
|
|
compiled_moods = compiled["moods"] = {}
|
|
compiled_vars = compiled["vars"] = {}
|
|
|
|
for mood in config["mood"]:
|
|
compiled_moods[mood] = mood_definitions[mood]
|
|
del config["mood"]
|
|
compiled_vars.update(config)
|
|
|
|
print("writing compiled config to " + str(COMPILED_CONFIG_FILE))
|
|
with COMPILED_CONFIG_FILE.open("w") as f:
|
|
json.dump(compiled, f, indent=4)
|