150 lines
4.5 KiB
Python
Executable file
150 lines
4.5 KiB
Python
Executable file
#!/usr/bin/env python
|
|
|
|
from itertools import batched
|
|
from random import choice
|
|
from re import compile, sub, split, match
|
|
|
|
from evennia.utils import logger, dedent
|
|
|
|
def paragraph(text):
|
|
"""
|
|
Removes initial spaces as well as final newlines.
|
|
|
|
Blank lines, however, are preserved.
|
|
"""
|
|
# Shame we can chain this or _thread_, but:
|
|
return sub(r";;", "\n\n",
|
|
sub(r"\n", " ",
|
|
sub(r"\n\n+", ";;",
|
|
dedent(text)))).strip()
|
|
|
|
def fix_msg(text):
|
|
if text and isinstance(text, str) and len(text) > 0:
|
|
return capitalize_line(text)
|
|
elif text and isinstance(text, tuple):
|
|
return (capitalize_line(text[0]), text[1])
|
|
else:
|
|
return text
|
|
|
|
def capitalize_line(line):
|
|
"""
|
|
Return a capitalized version of 'line'.
|
|
"""
|
|
# logger.info(f"Fixing {line}")
|
|
if not line or line.strip() == "":
|
|
return line
|
|
elif match(r"^\|[bm][a-z].*", line):
|
|
return "The " + line
|
|
elif match(r"^\|[A-z][a-z].*", line):
|
|
return line[0:2] + line[2].upper() + line[3:]
|
|
else:
|
|
return line[0].upper() + line[1:]
|
|
|
|
def squish(text):
|
|
"Remove series of spaces from the text."
|
|
no_start = sub(r"\( +", "(", text)
|
|
no_after = sub(r" +\)", ")", no_start)
|
|
no_inside = sub(r"\" *(.*?) *\"", "\"\\1\"", no_after)
|
|
return sub('[ \t]+', ' ', no_inside).strip()
|
|
|
|
def _routput_choose(text):
|
|
"""
|
|
Pick a choice when text is: one ^ two ^ three
|
|
Done by splitting the text, and calling random.choice().
|
|
"""
|
|
choices = split(r" *\^ *", text)
|
|
return choice(choices)
|
|
|
|
def _routput_empty(entry):
|
|
"""
|
|
Return True if entry is empty, e.g. None or blank.
|
|
False otherwise. Note: Spaces are ignored.
|
|
"""
|
|
if entry:
|
|
if isinstance(entry, str) and entry.strip() == '':
|
|
return True
|
|
if isinstance(entry, (list, tuple)) and len(entry) == 0:
|
|
return True
|
|
return False
|
|
return True
|
|
|
|
def _routput_pair(no_choice, choices = None):
|
|
"""
|
|
Return a list based on the ^-separated options in 'choices'.
|
|
|
|
Give a pair of split items, the 'no_choice' is the first section
|
|
(before the << ... >>>), and the 'choices' is the section between
|
|
the '<< ... >>' delimiter.
|
|
"""
|
|
|
|
# While unlikely, the text before << and the options between the
|
|
# << ...>> text may be blank:
|
|
if _routput_empty(no_choice) and _routput_empty(choices):
|
|
return ''
|
|
|
|
# If we start a string with <<...>>, then thte 'no_choice' option
|
|
# is blank, so we only return a choice:
|
|
if _routput_empty(no_choice):
|
|
return _routput_choose(choices)
|
|
|
|
# With text before, but not inside the <<...>> section, we just
|
|
# return the first part:
|
|
if _routput_empty(choices):
|
|
return no_choice
|
|
|
|
return no_choice + ' ' + _routput_choose(choices)
|
|
|
|
def routput(text, *substitutions):
|
|
"""
|
|
Return string with internal word choices replaced randomly.
|
|
For instance, the string:
|
|
|
|
'This feels <<^ very ^ quite>> <<nice^cozy^comfortable>>.'
|
|
|
|
Could return any of the following strings:
|
|
|
|
'This feels quite nice.'
|
|
'This feels cozy.'
|
|
'This feels very comfortable.'
|
|
"""
|
|
if text:
|
|
# section is a list of some phrase followed by another phrase
|
|
# that contains ^-separate choices:
|
|
sections = split(r" *<< *(.*?) *>> *", text)
|
|
|
|
# Parts are the first section followed by the _rendered_
|
|
# choices, so this^that is either 'this' or 'that':
|
|
parts = [_routput_pair(*pair) for pair in batched(sections, 2)]
|
|
|
|
# The parts may have empty strings, so we filter those out,
|
|
# leaving on the phrases to join:
|
|
phrases = [phrase for phrase in parts if not _routput_empty(phrase)]
|
|
|
|
if substitutions:
|
|
proposal = ' '.join(phrases).format(*substitutions)
|
|
else:
|
|
proposal = ' '.join(phrases)
|
|
|
|
# If a choice is at the end of a sentence, and we could have
|
|
# "no choice", as in:
|
|
# "He searches <<thoroughly ^ quickly ^>>."
|
|
# We don't want a version that looks like:
|
|
# "He searches # ."
|
|
# with a space before the punctuation:
|
|
#
|
|
# Also, we should remove all double spaces that may show:
|
|
return squish(sub(r"\s+([!?.,])", "\\1", proposal))
|
|
|
|
|
|
def choices(text, *substitutions):
|
|
"""
|
|
Returns a section of 'text' based on separating semicolons.
|
|
|
|
Note that text can already be separated as a list or tuple.
|
|
"""
|
|
if isinstance(text, str):
|
|
text = split(r"\s*;;\s*", text)
|
|
|
|
if text:
|
|
return routput(choice(text), *substitutions)
|