# sed module # <Kays> test # <Kays> s/^t/b/g # <guppy> <Kays> best # --Kays import re import sys @plugin class sed(object): def __init__(self, server): self.server = server self.prnt = server.prnt server.handle("message", self.handle_message) # this regex determines if the message appears # to possible be a sed request self.message_cond = re.compile('^s(.)') # this regex determines if the sed flags are valid self.valid_flags = re.compile('^[ig]+$') self.backlog = { } # this is the threshold for how many messages # that backlog should keep track of for any one # channel. If the user decides to run a sed on # a message that is 10 lines before it, then # it's not a very useful sed to say the least self.backlog_threshold = 10 def handle_message(self, channel, user, message): # if there is no backlog for this channel, # then just store the message and exit if self.backlog.get(channel, None) is None: self.backlog_message(channel, user, message) return match = self.message_cond.findall(message) mlen = len(match) # mlen == 0 (no match case) # backlog the message and exit if mlen == 0: self.backlog_message(channel, user, message) return # otherwise, there's a match. # sed delimiter delimiter = match[0] # split the string into single characters message_chars = list(message) # a sed command has at least 3 parts, # and at most, 4 parts = [] # escape sequences needs to be ignored run_next = False # for each message_chars, scrob all parts for index in range(0, len(message_chars)): # if this char is escaped, ignore if run_next: run_next = False continue # if this is an escape char if message_chars[index] == '\\': run_next = True continue # if this char is a delimiter, save it if message_chars[index] == delimiter: parts.append(index) # verify that we have all relevant parts # if this doesn't validate, then append # the message onto the backlog and exit if len(parts) != 3: # backlog the message self.backlog_message(channel, user, message) # now exit return # at this point, sed is good, so let's grab all 3-4 # parts of sed and make our replacements # PART 1: s/(..)// sed_part1 = message[parts[0]+1:parts[1]] # PART 2: s//(..)/ sed_part2 = message[parts[1]+1:parts[2]] # PART 3: s///(..) sed_part3 = message[parts[2]+1:] # validate that the flags are decent # if not, just add the message and exit if len(sed_part3) > 0 and not self.valid_flags.match(sed_part3): # backlog the message self.backlog_message(channel, user, message) # exit return # get the message to run sed with repl_message = self.find_backlog_message(channel, user, sed_part1, sed_part3) # if repl_message is None, log the message and exit if repl_message is None: self.backlog_message(channel, user, message) return # run sed! # if global replace flag_global = 1 if 'g' in sed_part3: flag_global = 0 # WARNING: re.sub does not have a flags # parameter for Python version < 2.7 # for Python < 2.7, flag 'i' will be ignored if sys.version_info[:2] == (2,7): # if ignore case flag is set flag_ignore = 0 if 'i' in sed_part3: flag_ignore = re.I result = re.sub(sed_part1, sed_part2, repl_message, count=flag_global, flags=flag_ignore) else: result = re.sub(sed_part1, sed_part2, repl_message, count=flag_global) # add the sed result to the backlog self.backlog_message(channel, user, result) # print the message self.server.doMessage(channel, "<%s> %s" % (user, result)) def backlog_message (self, channel, user, message): # if the backlog doesn't contain this # channel, add it if self.backlog.get(channel, None) is None: self.backlog[channel] = [] # if the backlog is over the threshold, drop one if len(self.backlog[channel]) > self.backlog_threshold: self.backlog[channel].pop(0) # record the message self.backlog[channel].append([user, message]) def find_backlog_message (self, channel, user, regex, regex_modifiers): # if backlog channel doesn't exist, return nothing if self.backlog.get(channel, None) is None: return modifiers = 0 # don't match 'i' (ignore-case) for Python versions < 2.7 if sys.version_info[:2] == (2,7) and 'i' in regex_modifiers: modifiers = re.I reg_obj = re.compile(regex, modifiers) for usr, msg in reversed(self.backlog[channel]): if user == usr and len(reg_obj.findall(msg)) > 0: return msg return