#!/usr/bin/env python3 """ Fulvia Spelling correction module This module will fix spelling errors if someone corrects them using the sed notation (s///) commonly found in vi/vim. """ import re from module import hook from tools import FulviaMemory def setup(bot): bot.memory['find_lines'] = FulviaMemory() @hook(True) def collectlines(bot, trigger): """Create a temporary log of what people say""" # Don't log things in PM if trigger.is_privmsg: return # Add a log for the channel and nick, if there isn't already one if trigger.channel not in bot.memory['find_lines']: bot.memory['find_lines'][trigger.channel] = FulviaMemory() if trigger.nick not in bot.memory['find_lines'][trigger.channel]: bot.memory['find_lines'][trigger.channel][trigger.nick] = list() # Create a temporary list of the user's lines in a channel templist = bot.memory['find_lines'][trigger.channel][trigger.nick] line = trigger.group() if line.startswith("s/"): # Don't remember substitutions return elif line.startswith("\x01ACTION"): # For /me messages line = line[:-1] templist.append(line) else: templist.append(line) del templist[:-10] # Keep the log to 10 lines per person bot.memory['find_lines'][trigger.channel][trigger.nick] = templist #Match nick, s/find/replace/flags. Flags and nick are optional, nick can be #followed by comma or colon, anything after the first space after the third #slash is ignored, you can escape slashes with backslashes, and if you want to #search for an actual backslash followed by an actual slash, you're shit out of #luck because this is the fucking regex of death as it is. # @rule(r"""(?: # (\S+) # Catch a nick in group 1 # [:,]\s+)? # Followed by colon/comma and whitespace, if given # s/ # The literal s/ # ( # Group 2 is the thing to find # (?:\\/ | [^/])+ # One or more non-slashes or escaped slashes # )/( # Group 3 is what to replace with # (?:\\/ | [^/])* # One or more non-slashes or escaped slashes # ) # (?:/(\S+))? # Optional slash, followed by group 4 (flags) # """) @hook(True) def findandreplace(bot, trigger): # Don't bother in PM if trigger.is_privmsg: return rule = re.compile(r"(\S+)?(?:[:,]\s|^)s\/((?:\\\/|[^/])+)\/((?:\\\/|[^/])*"\ + r")(?:\/(\S+))?") group = rule.search(trigger.group(0)) if not group: return g = (trigger.group(0),) + group.groups() trigger.set_group(g, bot.config) # Correcting other person vs self. rnick = (trigger.group(1) or trigger.nick) search_dict = bot.memory['find_lines'] # only do something if there is conversation to work with if trigger.channel not in search_dict: return if rnick not in search_dict[trigger.channel]: return #TODO rest[0] is find, rest[1] is replace. These should be made variables of #their own at some point. rest = [trigger.group(2), trigger.group(3)] rest[0] = rest[0].replace(r'\/', '/') rest[1] = rest[1].replace(r'\/', '/') me = False # /me command flags = (trigger.group(4) or '') print(flags) # If g flag is given, replace all. Otherwise, replace once. if 'g' in flags: count = 0 else: count = 1 # repl is a lambda function which performs the substitution. i flag turns # off case sensitivity. re.U turns on unicode replacement. if 'i' in flags: regex = re.compile(re.escape(rest[0]), re.U | re.I) repl = lambda s: re.sub(regex, rest[1], s, count == 1) else: repl = lambda s: re.sub(rest[0], rest[1], s, count) # Look back through the user's lines in the channel until you find a line # where the replacement works new_phrase = None for line in reversed(search_dict[trigger.channel][rnick]): if line.startswith("\x01ACTION"): me = True # /me command line = line[8:] else: me = False new_phrase = repl(line) if new_phrase != line: # we are done break if not new_phrase or new_phrase == line: return # Didn't find anything # Save the new "edited" message. action = (me and '\x01ACTION ') or '' # If /me message, prepend \x01ACTION templist = search_dict[trigger.channel][rnick] templist.append(action + new_phrase) search_dict[trigger.channel][rnick] = templist bot.memory['find_lines'] = search_dict # output if not me: new_phrase = f"\x02meant\x0f to say: {new_phrase}" if trigger.group(1): phrase = f"{trigger.nick} thinks {rnick} {new_phrase}" else: phrase = f"{trigger.nick} {new_phrase}" bot.msg(phrase)