Files
tilde-projects/Code/irc/madlibbot/madlib.py
T

171 lines
5.5 KiB
Python
Raw Normal View History

2018-06-13 14:34:23 +00:00
#!/usr/bin/python
2018-09-19 17:04:38 +00:00
import collections
2018-06-13 14:34:23 +00:00
import glob
2018-09-19 17:04:38 +00:00
import inflect
2018-06-13 14:34:23 +00:00
import random
import re
2018-09-19 17:04:38 +00:00
p = inflect.engine()
2018-06-13 14:34:23 +00:00
file_pattern = "/home/*/madlibs/*.madlib"
2018-10-05 16:02:38 -04:00
file_regex = re.compile(r"^/home/(.*?)/.*/([^/]*)\.madlib$")
word_regex = re.compile(r"{{(.*?)(#.*?)?(\|.*)?}}")
word_replace_regex = re.compile(r"^(.*)#(\d*)$")
2018-06-13 14:34:23 +00:00
# Take a file path and return a tuple with the title and original path
def munge_story(file_name):
match = re.match(file_regex, file_name)
count = count_words_file(file_name)
2018-10-05 16:02:38 -04:00
return (
"'" + match.group(2).replace("_", " ") + "' by ~" + match.group(1),
match.group(0),
count,
)
2018-06-13 14:34:23 +00:00
# Retrive a list of all madlib story files
def find_stories(limit=999, shuffle=False):
files = glob.glob(file_pattern)
if shuffle == True:
files = random.sample(files, max(1, min(limit, len(files))))
else:
files = files[:limit]
return map(munge_story, files)
2018-10-05 16:02:38 -04:00
2018-06-13 14:34:23 +00:00
# Count the number of replacable words in the story
def count_words(story):
2018-09-19 17:04:38 +00:00
# words that have a '#' part and match should only be counted once
count = 0
repeats = []
for match in re.finditer(word_regex, story):
2018-10-05 16:02:38 -04:00
if match.group(2) is None: # the '#' part
2018-09-19 17:04:38 +00:00
count += 1
elif match.group(1) + match.group(2) not in repeats:
count += 1
repeats.append(match.group(1) + match.group(2))
return count
2018-06-13 14:34:23 +00:00
2018-10-05 16:02:38 -04:00
2018-06-13 14:34:23 +00:00
# Count the number of replacable words in the story when given a file path
def count_words_file(storyPath):
with open(storyPath, "r") as storyFile:
story = storyFile.read()
return count_words(story)
2018-10-05 16:02:38 -04:00
2018-06-13 14:34:23 +00:00
# Get the next replaceable word in the story. Includes full token and just the word
# If you specify rand as True, it will get a random word instead of the next one
def find_next_word(story, rand=False):
matches = list(word_regex.finditer(story))
if len(matches) == 0:
return None
match = matches[0]
if rand is True:
match = random.choice(matches)
return (match.group(0), match.group(1))
2018-10-05 16:02:38 -04:00
2018-09-19 17:04:38 +00:00
# Using a query phrase, replace a word and return the entire story body
2018-06-13 14:34:23 +00:00
def replace_word(story, query, word):
2018-09-19 17:04:38 +00:00
rquery = word_regex.search(query)
# '{{foo#bar|baz|bang}}' => ('foo', '#bar', '|baz|bang')
2018-10-05 16:02:38 -04:00
pipes = [
p.lower()
for p in (rquery.group(3) if rquery.group(3) is not None else "")
.strip("|")
.split("|")
]
2018-09-19 17:04:38 +00:00
munged_word = process_pipes(word, pipes)
story = story.replace(query, munged_word, 1)
2018-10-05 16:02:38 -04:00
if rquery.group(2) is not None: # if there is a '#' part we replace all instances
print("Looking for {{" + rquery.group(1) + rquery.group(2) + ".*?}}")
others = re.findall(r"{{" + rquery.group(1) + rquery.group(2) + ".*?}}", story)
2018-09-19 17:04:38 +00:00
if len(others) > 0:
2018-10-05 16:02:38 -04:00
story = replace_word(
story, others[0], word
) # iteratively replace the next instance
2018-09-19 17:04:38 +00:00
return story
# Modify user input based on certain modifying pipes
def process_pipes(word, pipes):
for pipe in pipes:
try:
word = {
2018-10-05 16:02:38 -04:00
"upper": lambda word: word.upper(),
"lower": lambda word: word.lower(),
"title": lambda word: word.title(),
"numeric": lambda word: p.number_to_words(word),
"ordinal": lambda word: p.ordinal(word),
collections.defaultdict: lambda word: word,
}[pipe](word)
2018-09-19 17:04:38 +00:00
except Exception:
2018-10-05 16:02:38 -04:00
pass # just keep going if an error occurs processing a pipe
2018-09-19 17:04:38 +00:00
return word
2018-06-13 14:34:23 +00:00
2018-10-05 16:02:38 -04:00
2018-06-13 14:34:23 +00:00
# A helper function that will split the story up into chat-printable lengths
def yield_lines(line, max_size):
words = []
words_length = 0
# Add words to the words line untill we run out of space
for word in line.split():
# If the next word itself is longer thatn max_size, we'll have to chop it up
while len(word) > max_size:
splitsize = max_size - words_length - 1
words.append(word[:splitsize] + "-")
2018-10-05 16:02:38 -04:00
yield " ".join(words)
2018-06-13 14:34:23 +00:00
words = []
words_length = 0
word = word[splitsize:]
if words_length + len(word) + 1 > max_size:
2018-10-05 16:02:38 -04:00
yield " ".join(words)
2018-06-13 14:34:23 +00:00
words = []
words_length = 0
words.append(word)
2018-10-05 16:02:38 -04:00
words_length += len(word) + 1 # For the space
yield " ".join(words) # For any words left over
2018-06-13 14:34:23 +00:00
# Open a story file and ask for the parts
def make_story(storyFile):
with open(storyFile, "r") as storyFile:
story = storyFile.read()
match = word_regex.search(story)
while match is not None:
2018-10-05 16:02:38 -04:00
word = raw_input("Give me {}: ".format(match.group(1)))
print("Replacing '{}' with '{}'".format(match.group(0), word))
print(story)
2018-06-13 14:34:23 +00:00
story = re.sub(match.group(0), word, story, 1)
match = word_regex.search(story)
2018-10-05 16:02:38 -04:00
print(story)
2018-06-13 14:34:23 +00:00
def start():
stories = find_stories(20, True)
if len(stories) == 0:
2018-10-05 16:02:38 -04:00
print("Sorry, no stories found. :/")
2018-06-13 14:34:23 +00:00
return
input = -1
while input < 0 or input >= len(stories):
for idx, story in enumerate(stories):
2018-10-05 16:02:38 -04:00
print("[{}] {}".format(idx, story[0]))
2018-06-13 14:34:23 +00:00
input = raw_input("Which story would you like?: ")
try:
# Try to convert the string input into a number
input = int(input)
except:
# If they put something stupid in, treat it as a -1 and ask for
# input again
input = -1
# Call make_story with the file path of the selected story
make_story(stories[input][1])
2018-10-05 16:02:38 -04:00
2018-06-13 14:34:23 +00:00
if __name__ == "__main__":
start()