implemented compilation of config file to remove complexity from actual library
This commit is contained in:
parent
be024389ba
commit
cc5b7ae55e
@ -15,7 +15,6 @@ license-files = ["LICENSE"]
|
|||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
python-mommy-dev = "python_mommy_venv.__main__:development"
|
python-mommy-dev = "python_mommy_venv.__main__:development"
|
||||||
python-mommy-generate-config = "python_mommy_venv.__main__:write_current_config"
|
|
||||||
mommify-venv = "python_mommy_venv.__main__:mommify_venv"
|
mommify-venv = "python_mommy_venv.__main__:mommify_venv"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
|
@ -2,24 +2,33 @@ import random
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import os
|
import json
|
||||||
import re
|
|
||||||
import signal
|
|
||||||
|
|
||||||
from .config import get_mood, get_template_values
|
from .responses import COMPILED_CONFIG_FILE
|
||||||
from .static import RESPONSES, Situation, colors
|
from .static import colors
|
||||||
|
|
||||||
|
|
||||||
def get_response(situation: Situation, colorize: Optional[bool] = None):
|
def get_response_from_situation(situation: str, colorize: Optional[bool] = None):
|
||||||
if colorize is None:
|
if colorize is None:
|
||||||
colorize = sys.stdout.isatty()
|
colorize = sys.stdout.isatty()
|
||||||
|
|
||||||
# get message
|
# get message
|
||||||
mood = get_mood()
|
config = json.loads(COMPILED_CONFIG_FILE.read_text())
|
||||||
template = random.choice(RESPONSES[mood][situation])
|
existing_moods = list(config["moods"].keys())
|
||||||
message = template.format(**get_template_values(mood))
|
template_options = config["moods"][random.choice(existing_moods)][situation]
|
||||||
|
template: str = random.choice(template_options)
|
||||||
|
|
||||||
|
template_values = {}
|
||||||
|
for key, values in config["vars"].items():
|
||||||
|
template_values[key] = random.choice(values)
|
||||||
|
|
||||||
|
message = template.format(**template_values)
|
||||||
|
|
||||||
# return message
|
# return message
|
||||||
if not colorize:
|
if not colorize:
|
||||||
return message
|
return message
|
||||||
return colors.BOLD + message + colors.ENDC
|
return colors.BOLD + message + colors.ENDC
|
||||||
|
|
||||||
|
|
||||||
|
def get_response(code: int, colorize: Optional[bool] = None) -> str:
|
||||||
|
return get_response_from_situation("positive" if code == 0 else "negative")
|
||||||
|
@ -5,37 +5,21 @@ import stat
|
|||||||
import toml
|
import toml
|
||||||
|
|
||||||
from . import get_response
|
from . import get_response
|
||||||
from .static import Situation
|
from .responses import compile_config
|
||||||
from .config import CONFIG_FILES, CONFIG_DIRECTORY, generate_current_configuration
|
|
||||||
|
|
||||||
|
|
||||||
def development():
|
def development():
|
||||||
s = "positive"
|
s = "positive"
|
||||||
if len(sys.argv) > 1:
|
if len(sys.argv) > 1:
|
||||||
s = sys.argv[1]
|
s = sys.argv[1]
|
||||||
|
|
||||||
print(get_response(Situation(s)))
|
compile_config()
|
||||||
|
|
||||||
|
|
||||||
def write_current_config():
|
|
||||||
f = "python-mommy.toml"
|
|
||||||
if len(sys.argv) > 1:
|
|
||||||
f = sys.argv[1]
|
|
||||||
|
|
||||||
config_file = CONFIG_DIRECTORY / f
|
|
||||||
print(f"writing to: {config_file}")
|
|
||||||
|
|
||||||
data = toml.dumps(generate_current_configuration())
|
|
||||||
print(data)
|
|
||||||
with config_file.open("w") as f:
|
|
||||||
f.write(data)
|
|
||||||
|
|
||||||
|
|
||||||
WRAPPER_TEMPLATE = """#!{inner_bin}
|
WRAPPER_TEMPLATE = """#!{inner_bin}
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import sys, subprocess
|
import sys, subprocess
|
||||||
from python_mommy_venv import get_response, Situation
|
from python_mommy_venv import get_response
|
||||||
|
|
||||||
|
|
||||||
INTERPRETER = "{inner_bin}"
|
INTERPRETER = "{inner_bin}"
|
||||||
@ -43,7 +27,7 @@ result = subprocess.run([INTERPRETER] + sys.argv[1:])
|
|||||||
code = result.returncode
|
code = result.returncode
|
||||||
|
|
||||||
print()
|
print()
|
||||||
print(get_response(Situation.POSITIVE if code == 0 else Situation.NEGATIVE))
|
print(get_response(code))
|
||||||
exit(code=code)
|
exit(code=code)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -97,7 +81,10 @@ def mommify_pip(path: Path):
|
|||||||
with path.open("w") as f:
|
with path.open("w") as f:
|
||||||
f.write(text)
|
f.write(text)
|
||||||
|
|
||||||
|
|
||||||
def mommify_venv():
|
def mommify_venv():
|
||||||
|
compile_config()
|
||||||
|
|
||||||
v = ".venv"
|
v = ".venv"
|
||||||
if len(sys.argv) > 1:
|
if len(sys.argv) > 1:
|
||||||
v = sys.argv[1]
|
v = sys.argv[1]
|
||||||
|
@ -1,193 +0,0 @@
|
|||||||
from typing import Optional, List, Dict, Union
|
|
||||||
import os
|
|
||||||
from os.path import expandvars
|
|
||||||
from sys import platform
|
|
||||||
import logging
|
|
||||||
from pathlib import Path
|
|
||||||
import toml
|
|
||||||
import random
|
|
||||||
|
|
||||||
from .static import RESPONSES
|
|
||||||
|
|
||||||
logger = logging.Logger("mommy_config")
|
|
||||||
|
|
||||||
PREFIXES = [
|
|
||||||
"PYTHON", # first one is always the prefix of the current program
|
|
||||||
"CARGO",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# env key is just a backup key for compatibility with cargo mommy
|
|
||||||
CONFIG = {
|
|
||||||
"mood": {
|
|
||||||
"defaults": ["chill"]
|
|
||||||
},
|
|
||||||
"emote": {
|
|
||||||
"defaults": ["❤️", "💖", "💗", "💓", "💞"]
|
|
||||||
},
|
|
||||||
"pronoun": {
|
|
||||||
"defaults": ["her"]
|
|
||||||
},
|
|
||||||
"role": {
|
|
||||||
"defaults": ["mommy"]
|
|
||||||
},
|
|
||||||
"affectionate_term": {
|
|
||||||
"defaults": ["girl"],
|
|
||||||
"env_key": "LITTLE"
|
|
||||||
},
|
|
||||||
"denigrating_term": {
|
|
||||||
"spiciness": "yikes",
|
|
||||||
"defaults": ["slut", "toy", "pet", "pervert", "whore"],
|
|
||||||
"env_key": "FUCKING"
|
|
||||||
},
|
|
||||||
"part": {
|
|
||||||
"spiciness": "yikes",
|
|
||||||
"defaults": ["milk"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MOOD_PRIORITIES: Dict[str, int] = {}
|
|
||||||
for i, mood in enumerate(RESPONSES):
|
|
||||||
MOOD_PRIORITIES[mood] = i
|
|
||||||
|
|
||||||
PREFIXES = [
|
|
||||||
"PYTHON", # first one is always the prefix of the current program
|
|
||||||
"CARGO",
|
|
||||||
]
|
|
||||||
|
|
||||||
for key, value in CONFIG.items():
|
|
||||||
env_keys = [
|
|
||||||
PREFIXES[0] + "_MOMMY_" + key.upper(),
|
|
||||||
"MOMMY_" + key.upper(),
|
|
||||||
*(p + "_MOMMY_" + key.upper() for p in PREFIXES)
|
|
||||||
]
|
|
||||||
|
|
||||||
if value.get("env_key") is not None:
|
|
||||||
env_keys.append(value.get("env_key"))
|
|
||||||
|
|
||||||
for env_key in env_keys:
|
|
||||||
res = os.environ.get(env_key)
|
|
||||||
if res is not None:
|
|
||||||
value["default"] = res.split("/")
|
|
||||||
|
|
||||||
|
|
||||||
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",
|
|
||||||
]
|
|
||||||
|
|
||||||
def load_config_file(config_file: Path) -> bool:
|
|
||||||
global CONFIG
|
|
||||||
if not config_file.exists():
|
|
||||||
return False
|
|
||||||
|
|
||||||
with config_file.open("r") as f:
|
|
||||||
data = toml.load(f)
|
|
||||||
|
|
||||||
for key, value in data.items():
|
|
||||||
if isinstance(value, str):
|
|
||||||
CONFIG[key]["defaults"] = [value]
|
|
||||||
else:
|
|
||||||
CONFIG[key]["defaults"] = value
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
for c in CONFIG_FILES:
|
|
||||||
if load_config_file(c):
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
# validate config file
|
|
||||||
if True:
|
|
||||||
unfiltered_moods = CONFIG["mood"]["defaults"]
|
|
||||||
CONFIG["mood"]["defaults"] = filtered_moods = []
|
|
||||||
for mood in unfiltered_moods:
|
|
||||||
if mood in RESPONSES:
|
|
||||||
filtered_moods.append(mood)
|
|
||||||
else:
|
|
||||||
logger.warning("mood %s isn't supported", mood)
|
|
||||||
|
|
||||||
|
|
||||||
def get_mood() -> str:
|
|
||||||
return random.choice(CONFIG["mood"]["defaults"])
|
|
||||||
|
|
||||||
def get_template_values(mood: str) -> Dict[str, str]:
|
|
||||||
mood_spice_level = MOOD_PRIORITIES[mood]
|
|
||||||
result = {}
|
|
||||||
|
|
||||||
for key, value in CONFIG.items():
|
|
||||||
spice = value.get("spiciness")
|
|
||||||
allow_key = spice is None
|
|
||||||
if not allow_key:
|
|
||||||
key_spice_level = MOOD_PRIORITIES[spice]
|
|
||||||
allow_key = mood_spice_level >= key_spice_level
|
|
||||||
|
|
||||||
if not allow_key:
|
|
||||||
continue
|
|
||||||
|
|
||||||
result[key] = random.choice(value["defaults"])
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def generate_current_configuration() -> Dict[str, Union[str, List[str]]]:
|
|
||||||
global CONFIG
|
|
||||||
generated = {}
|
|
||||||
|
|
||||||
for key, definition in CONFIG.items():
|
|
||||||
value = definition["defaults"]
|
|
||||||
if len(value) == 1:
|
|
||||||
value = value[0]
|
|
||||||
|
|
||||||
generated[key] = value
|
|
||||||
|
|
||||||
return generated
|
|
174
python_mommy_venv/responses.py
Normal file
174
python_mommy_venv/responses.py
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
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)
|
@ -1,136 +1,4 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
class Situation(Enum):
|
|
||||||
POSITIVE = "positive"
|
|
||||||
NEGATIVE = "negative"
|
|
||||||
OVERFLOW = "overflow "
|
|
||||||
|
|
||||||
|
|
||||||
RESPONSES = {
|
|
||||||
"chill": {
|
|
||||||
Situation.POSITIVE: [
|
|
||||||
"*pets your head*",
|
|
||||||
"*gives you scritches*",
|
|
||||||
"you're such a smart cookie~",
|
|
||||||
"that's a good {affectionate_term}~",
|
|
||||||
"{role} thinks {pronoun} little {affectionate_term} earned a big hug~",
|
|
||||||
"good {affectionate_term}~\n{role}'s so proud of you~",
|
|
||||||
"aww, what a good {affectionate_term}~\n{role} knew you could do it~",
|
|
||||||
"you did it~!",
|
|
||||||
"{role} loves you~",
|
|
||||||
"*gives you a sticker*",
|
|
||||||
"*boops your nose*",
|
|
||||||
"*wraps you in a big hug*",
|
|
||||||
"well done~!\n{role} is so happy for you~",
|
|
||||||
"what a good {affectionate_term} you are~",
|
|
||||||
"that's {role}'s clever little {affectionate_term}~",
|
|
||||||
"you're doing so well~!",
|
|
||||||
"you're making {role} so happy~",
|
|
||||||
"{role} loves {pronoun} cute little {affectionate_term}~"
|
|
||||||
],
|
|
||||||
Situation.NEGATIVE: [
|
|
||||||
"{role} believes in you~",
|
|
||||||
"don't forget to hydrate~",
|
|
||||||
"aww, you'll get it next time~",
|
|
||||||
"do you need {role}'s help~?",
|
|
||||||
"everything's gonna be ok~",
|
|
||||||
"{role} still loves you no matter what~",
|
|
||||||
"oh no did {role}'s little {affectionate_term} make a big mess~?",
|
|
||||||
"{role} knows {pronoun} little {affectionate_term} can do better~",
|
|
||||||
"{role} still loves you~",
|
|
||||||
"{role} thinks {pronoun} little {affectionate_term} is getting close~",
|
|
||||||
"it's ok, {role}'s here for you~",
|
|
||||||
"oh, darling, you're almost there~",
|
|
||||||
"does {role}'s little {affectionate_term} need a bit of a break~?",
|
|
||||||
"oops~! {role} loves you anyways~",
|
|
||||||
"try again for {role}, {affectionate_term}~",
|
|
||||||
"don't worry, {role} knows you can do it~"
|
|
||||||
],
|
|
||||||
Situation.OVERFLOW: [
|
|
||||||
"{role} has executed too many times and needs to take a nap~"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ominous": {
|
|
||||||
Situation.POSITIVE: [
|
|
||||||
"What you have set in motion today will be remembered for aeons to come!",
|
|
||||||
"{role} will see to it that {pronoun} little {affectionate_term}'s name is feared~",
|
|
||||||
"{role} is proud of the evil seed {pronoun} {affectionate_term} has planted into this accursed world"
|
|
||||||
],
|
|
||||||
Situation.NEGATIVE: [
|
|
||||||
"Ah, failure? {role} will make sure the stars are right next time",
|
|
||||||
"Does {role}'s little {affectionate_term} need more time for worship~?",
|
|
||||||
"May the mark of the beast stain your flesh forever, {role} will haunt your soul forevermore"
|
|
||||||
],
|
|
||||||
Situation.OVERFLOW: [
|
|
||||||
"THOU HAST DRUNK TOO DEEPLY OF THE FONT"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"thirsty": {
|
|
||||||
"spiciness": "thirsty",
|
|
||||||
Situation.POSITIVE: [
|
|
||||||
"*tugs your leash*\nthat's a VERY good {affectionate_term}~",
|
|
||||||
"*runs {pronoun} fingers through your hair* good {affectionate_term}~ keep going~",
|
|
||||||
"*smooches your forehead*\ngood job~",
|
|
||||||
"*nibbles on your ear*\nthat's right~\nkeep going~",
|
|
||||||
"*pats your butt*\nthat's a good {affectionate_term}~",
|
|
||||||
"*drags {pronoun} nail along your cheek*\nsuch a good {affectionate_term}~",
|
|
||||||
"*bites {pronoun} lip*\nmhmm~",
|
|
||||||
"give {role} a kiss~",
|
|
||||||
"*heavy breathing against your neck*"
|
|
||||||
],
|
|
||||||
Situation.NEGATIVE: [
|
|
||||||
"you're so cute when you're flustered~",
|
|
||||||
"do you think you're going to get a reward from {role} like that~?",
|
|
||||||
"*grabs your hair and pulls your head back*\nyou can do better than that for {role} can't you~?",
|
|
||||||
"if you don't learn how to code better, {role} is going to put you in time-out~",
|
|
||||||
"does {role} need to give {pronoun} little {affectionate_term} some special lessons~?",
|
|
||||||
"you need to work harder to please {role}~",
|
|
||||||
"gosh you must be flustered~",
|
|
||||||
"are you just keysmashing now~?\ncute~",
|
|
||||||
"is {role}'s little {affectionate_term} having trouble reaching the keyboard~?"
|
|
||||||
],
|
|
||||||
Situation.OVERFLOW: [
|
|
||||||
"you've been a bad little {affectionate_term} and worn out {role}~"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"yikes": {
|
|
||||||
"spiciness": "yikes",
|
|
||||||
Situation.POSITIVE: [
|
|
||||||
"keep it up and {role} might let you cum you little {denigrating_term}~",
|
|
||||||
"good {denigrating_term}~\nyou've earned five minutes with the buzzy wand~",
|
|
||||||
"mmm~ come taste {role}'s {part}~",
|
|
||||||
"*slides {pronoun} finger in your mouth*\nthat's a good little {denigrating_term}~",
|
|
||||||
"you're so good with your fingers~\n{role} knows where {pronoun} {denigrating_term} should put them next~",
|
|
||||||
"{role} is getting hot~",
|
|
||||||
"that's a good {denigrating_term}~",
|
|
||||||
"yes~\nyes~~\nyes~~~",
|
|
||||||
"{role}'s going to keep {pronoun} good little {denigrating_term}~",
|
|
||||||
"open wide {denigrating_term}.\nyou've earned {role}'s {part}~",
|
|
||||||
"do you want {role}'s {part}?\nkeep this up and you'll earn it~",
|
|
||||||
"oooh~ what a good {denigrating_term} you are~"
|
|
||||||
],
|
|
||||||
Situation.NEGATIVE: [
|
|
||||||
"you filthy {denigrating_term}~\nyou made a mess, now clean it up~\nwith your tongue~",
|
|
||||||
"*picks you up by the throat*\npathetic~",
|
|
||||||
"*drags {pronoun} claws down your back*\ndo it again~",
|
|
||||||
"*brandishes {pronoun} paddle*\ndon't make me use this~",
|
|
||||||
"{denigrating_term}.\n{denigrating_term}~\n{denigrating_term}~~",
|
|
||||||
"get on your knees and beg {role} for forgiveness you {denigrating_term}~",
|
|
||||||
"{role} doesn't think {pronoun} little {denigrating_term} should have permission to wear clothes anymore~",
|
|
||||||
"never forget you belong to {role}~",
|
|
||||||
"does {role} need to put you in the {denigrating_term} wiggler~?",
|
|
||||||
"{role} is starting to wonder if you should just give up and become {pronoun} breeding stock~",
|
|
||||||
"on your knees {denigrating_term}~",
|
|
||||||
"oh dear. {role} is not pleased",
|
|
||||||
"one spank per error sounds appropriate, don't you think {denigrating_term}?",
|
|
||||||
"no more {part} for you {denigrating_term}"
|
|
||||||
],
|
|
||||||
Situation.OVERFLOW: [
|
|
||||||
"brats like you don't get to talk to {role}"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class colors:
|
class colors:
|
||||||
HEADER = '\033[95m'
|
HEADER = '\033[95m'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user