''' Rex script for Weechat, by lesmon, under the MIT license. I don't have an e-mail address, but you should find me on Freenode. Why would you need me anyway? this code is messy and I probably wont't be more qualified than you are to fix it. This script is heavily inspired by https://xkcd.com/1288/ It replaces parts of the text you recieve from IRC following given rules. The exact syntax of rules can be found by typing /help rex You can directly add new rules to ~/.weechat/rules.rex (assuming it is the default directory for Weechat on your system). ''' from weechat import * import os, re # Matches substitution rules parser = re.compile(r'\s*(/?)(.+)\1((?<=/)i)?\s+->\s+(.*)$') # List of rules to apply rules, plain = [], [] def make_repl(repl, smart_case): # We need to do this to get proper capture group replacement normal_repl = lambda match: match.re.sub(repl, match.group()) normal_repl.orig = repl if smart_case: def better_repl(match): # The result is evaluated first, to avoid .upper() on the regex found, result = match.group(), normal_repl(match) if found == found.upper(): return result.upper() elif found[0].isupper(): return result.capitalize() return result better_repl.orig = repl return better_repl return normal_repl def make_rule(rule): raw, pattern, i, repl = rule # Way too long, but who cares? return re.compile(('\b' if not raw else '') + pattern, re.U | (re.I if i or not raw else 0)), make_repl(repl, not raw) def repr_rule(rule): raw, pattern, i, repl = rule return raw + pattern + raw + ('i' if i else '') + ' -> ' + repl def say(message, buffer=''): prnt(buffer, message) def load(path): global rules, plain if not os.path.isfile(path): return False new_rules, new_plain = [], [] with open(path, 'r') as stored: for line in stored.readlines(): rule = parser.match(line) if not rule: continue rule = rule.groups() new_plain.append(rule) new_rules.append(make_rule(rule)) rules, plain = new_rules, new_plain return True def rex_cb(data, buffer, args): global rules, plain if ' ' not in args.strip(): command = args.strip().lower() if command == 'list': say('List of rules being applied:') for n, rule in enumerate(plain): say('%8d. ' % (n + 1) + repr_rule(rule)) elif command == 'reload': say('Reloading "%s" ...' % path) say('Done!' if load(path) else '%sFailure. Maybe "%s" doesn\'t exist?' % (prefix('error'), path)) elif command == 'save': say('Saving rules to "%s" ...' % path) with open(path, 'w') as stored: for rule in plain: stored.write('%s\n' % repr_rule(rule)) say('Done!') else: say('%sInvalid command. Please type /help rex for more informations.' % prefix('error')) return WEECHAT_RC_ERROR else: command, arg = args.split(' ', 1) rule = parser.match(arg) if command == 'add': if not rule: say('%sThe rule is malformed or invalid. Please type /help rex for more informations.' % prefix('error')) return WEECHAT_RC_ERROR rule = rule.groups() plain.append(rule) rules.append(make_rule(rule)) say('Successfully added the following rule: ' + repr_rule(rule)) elif command == 'del' and re.match('\d{1,8}', arg): if int(arg) > len(rules): say('%sThe number is out of range. Please specify a number between 1 and %d.' % (prefix('error'), len(rules))) return WEECHAT_RC_ERROR n = int(arg) - 1 rule = plain[n] del plain[n] del rules[n] say('The following rule was deleted: ' + repr_rule(rule)) else: say('%sInvalid command. Please type /help rex for more informations.' % prefix('error')) return WEECHAT_RC_ERROR return WEECHAT_RC_OK def mitm_cb(data, modifier, modifier_data, string): sender, command, dest, args = string.split(' ', 3) message = args[1:] for pattern, repl in rules: message = pattern.sub(repl, message) return ' '.join((sender, command, dest, ':' + message)) #---------------- THE PROGRAM STARTS HERE ---------------- register('Rex', 'lesmon', '1.1', 'MIT', 'Fixes your text with regexes!', '', '') path = info_get('weechat_dir', '') + '/rules.rex' hook_command('rex', 'Allows you to control the rules from Weechat.\n\n' 'The rules are applied in the order they have been added.\n' 'A rule can have two different forms:\n\n' ' foo -> bar\n' ' Replaces foo with bar, using the same case, and only if foo is the beginning of a word.\n' ' Examples: Foo is replaced with Foo, FOOBAR with BARBAR, but barfoo does not change\n\n' ' /foo/ -> bar\n' ' Replaces any occurence of foo with bar, without special treatment.\n' ' You can use /i to ignore case, i.e. /foo/i -> bar will match Foo and replace it with bar.\n\n' 'Note that foo and bar uses the extended regexp syntax provided by Python 2 for substitutions, you can do the following:\n\n' ' (?<=systemd\s+)sucks -> rocks\n' ' An example of lookbehind assertion, to be more politically correct on controversial channels.\n\n' ' wom([ae])n -> m\\1n\n' ' Replace woman by man, and women by men, among others.\n\n' 'Please refer to the Python 2 Official Documentation on the module \'re\' for more informations.\n\n', 'list | reload | add rule | del n | save', 'The possible commands are:\n\n' ' /rex list\n Display the rules being applied\n\n' ' /rex reload\n Reloads the rules located in "%s"\n BE CAREFUL! You will lose manually added rules with /rex add\n\n' ' /rex add rule\n Add a new rule manually\n Example: /rex add foo -> bar\n\n' ' /rex del n\n Delete the rule numbered n in /rex list\n\n' ' /rex save\n Save the current rules to "%s"\n\n' % (path, path), 'list|reload|add|del|save', 'rex_cb', '') hook_modifier('irc_in2_privmsg', 'mitm_cb', '') say('Loading "%s" ...' % path) say('Done!' if load(path) else '%sFailure. Maybe "%s" doesn\'t exist?' % (prefix('error'), path))