def opall(data, buffer, args): channel = weechat.buffer_get_string(buffer, 'localvar_channel') server = weechat.buffer_get_string(buffer, 'localvar_server') if not weechat.info_get('irc_is_channel', channel): weechat.prnt(buffer, '%sopall: Not an IRC channel' % weechat.prefix('error')) return weechat.WEECHAT_RC_OK toOp = withoutOp(server, channel) if len(toOp) == 0: return weechat.WEECHAT_RC_OK # how many people can we op at once modes = int(weechat.info_get('irc_server_isupport_value', '%s,MODES' % server)) or 0 if modes == 0: weechat.prnt(buffer, '%sopall: failed to determine MODES' % weechat.prefix('error')) return weechat.WEECHAT_RC_ERROR frm = 0 to = modes while len(toOp) > frm: weechat.command(buffer, '/OP %s' % ' '.join(toOp[frm:to])) frm = to to += modes return weechat.WEECHAT_RC_OK
def responsive_cb(data, signal, signal_data): term_height = int(weechat.info_get("term_height", "")) term_width = int(weechat.info_get("term_width", "")) try: apply_layout = None for layout, width, height in LAYOUT_LIST: if term_height <= int(height) or term_width <= int(width): apply_layout = layout break if apply_layout is None: # Always apply the last layout if term width/height is larger than configured layouts apply_layout = LAYOUT_LIST[-1][0] if layout_exist(apply_layout) and not layout_current(apply_layout): _print("Applying layout %s" % apply_layout) weechat.command("", "/layout apply %s" % apply_layout) toggle_nick_list(apply_layout) weechat.bar_item_update("rlayout") except ValueError: _print("Height or width is not in number form, ignoring.") return weechat.WEECHAT_RC_OK
def print_as_list(target, matches, data, limit, total): """Prints the output as a comma-separated list of nicks.""" col = w.color(w.info_get("irc_nick_color_name", data["setter"])) pf = fmt_prefix(data).replace("_target_", "") s = "{}\tThe following {} {}" if data["mode"] == "special": w.prnt(target, s.format(pf, "nick matches" if total == 1 else "nicks match", fmt_banmask(data["mask"]))) else: w.prnt(target, (s + ", {} by {}{}{}").format( pf, "nick matches" if total == 1 else "nicks match", fmt_banmask(data["mask"]), fmt_mode_char(data["mode"]), col, data["setter"], w.color("reset") )) nicks = [] remainder = len(matches) - limit i = 0 for name in matches: nicks.append("{}{}{}".format(w.color(w.info_get("irc_nick_color_name", name)), name, w.color("reset"))) i += 1 if i >= limit: break if w.config_string(w.config_get("weechat.look.prefix_same_nick")): pf = (w.color(w.config_get_plugin("prefix_color")) + w.config_string(w.config_get("weechat.look.prefix_same_nick")) + w.color("reset")) printstr = "{}\t{}".format(pf, ", ".join(nicks)) if remainder > 0: printstr += ", and {} more..".format(remainder) w.prnt(target, printstr)
def wu_cond(data, command, return_code, out, err): if return_code == weechat.WEECHAT_HOOK_PROCESS_ERROR: weechat.prnt("", "Error with command '%s'" % command) return weechat.WEECHAT_RC_OK if return_code > 0: weechat.prnt("", "return_code = %d" % return_code) if err != "": weechat.prnt("", "stderr: %s" % err) if out != "": j = ast.literal_eval(out) try: jcheck = j['response']['error']['type'] if j['response']['error']['type'] == "invalidquery": reaction = "Error. Try again." rtnbuf = kserver + "," + kchannel buffer = weechat.info_get("irc_buffer", rtnbuf) weechat.command(buffer, "/msg " + kchannel + " " + reaction) return weechat.WEECHAT_RC_OK if j['response']['error']['type'] == "keynotfound": weechat.prnt("", "Invalid API key.") return weechat.WEECHAT_RC_OK except KeyError: pass co = 'current_observation' reaction = '[' + jname + '] ' + j[co]['weather'] + '. Temp is ' if units == "metric": windspeed = j[co]['wind_kph'] temp = j[co]['temp_c'] like = j[co]['feelslike_c'] if str(temp) == str(like): reaction += str(temp) + "*C" else: reaction += str(temp) + "*C but feels like " + str(like) + "*C" if windspeed > 0: reaction += '. ' reaction += str(j[co]['wind_dir']) + ' wind: ' + str(windspeed) + ' kph' else: windspeed = j[co]['wind_mph'] temp = j[co]['temp_f'] like = j[co]['feelslike_f'] if str(temp) == str(like): reaction += str(temp) + "*F" else: reaction += str(temp) + "*F but feels like " + str(like) + "*F" if windspeed > 0: reaction += '. ' reaction += str(j[co]['wind_dir']) + ' wind: ' + str(windspeed) + ' mph' humid = j[co]['relative_humidity'] if int(humid[:-1]) > 50: reaction += '. Humidity: ' + j[co]['relative_humidity'] reaction += '.' rtnbuf = kserver + "," + kchannel buffer = weechat.info_get("irc_buffer", rtnbuf) weechat.command(buffer, "/msg " + kchannel + " " + reaction) return weechat.WEECHAT_RC_OK
def join_cb(data, signal, signal_data): nick = weechat.info_get("irc_nick_from_host", signal_data) server = signal.split(",")[0] channel = signal_data.split(":")[-1] buffer = weechat.info_get("irc_buffer", "%s,%s" % (server, channel)) if buffer: weechat.prnt(buffer, "Eheh, %s has joined this channel!" % nick) return weechat.WEECHAT_RC_OK
def on_join_scan_cb(data, signal, signal_data): network = signal.split(',')[0] if network in OPTIONS['hooks.excluded_servers'].split(','): return weechat.WEECHAT_RC_OK joined_nick = weechat.info_get("irc_nick_from_host", signal_data) join_match_data = re.match(':[^!]+!([^@]+@(\S+)) JOIN :?([#&]\S*)', signal_data) parsed_ident_host = join_match_data.group(1).lower() parsed_host = join_match_data.group(2).lower() if OPTIONS["compare_idents"] == "on": hostkey = parsed_ident_host else: hostkey = parsed_host chan_name = join_match_data.group(3) network_chan_name = "%s.%s" % (network, chan_name) chan_buffer = weechat.info_get("irc_buffer", "%s,%s" % (network, chan_name)) if not chan_buffer: print "No IRC channel buffer found for %s" % network_chan_name return weechat.WEECHAT_RC_OK if OPTIONS["display_join_messages"] == "on": message = "%s%s%s%s%s" % ( format_from_config(joined_nick, "colors.join_messages.nick"), format_from_config("!", "colors.join_messages.message"), format_from_config(parsed_ident_host, "colors.join_messages.identhost"), format_from_config(" JOINed ", "colors.join_messages.message"), format_from_config(network_chan_name, "colors.join_messages.channel"), ) #Make sure message format is also applied if no formatting is given for nick message = format_from_config(message, "colors.join_messages.message") weechat.prnt(cs_get_buffer(), message) clones = get_clones_for_buffer("%s,%s" % (network, chan_name), hostkey) if clones: key = get_validated_key_from_config("clone_onjoin_alert_key") filtered_clones = filter(lambda clone: clone['nick'] != joined_nick, clones[hostkey]) match_strings = map(lambda m: format_from_config(m[key], "colors.onjoin_alert.matches"), filtered_clones) join_string = format_from_config(' and ',"colors.onjoin_alert.message") masks = join_string.join(match_strings) message = "%s %s %s %s %s" % ( format_from_config(joined_nick, "colors.onjoin_alert.nick"), format_from_config("is already on", "colors.onjoin_alert.message"), format_from_config(network_chan_name, "colors.onjoin_alert.channel"), format_from_config("as", "colors.onjoin_alert.message"), masks ) message = format_from_config(message, 'colors.onjoin_alert.message') if OPTIONS["display_onjoin_alert_clone_buffer"] == "on": weechat.prnt(cs_get_buffer(),message) if OPTIONS["display_onjoin_alert_target_buffer"] == "on": weechat.prnt(chan_buffer, message) if OPTIONS["display_onjoin_alert_current_buffer"] == "on": weechat.prnt(weechat.current_buffer(),message) return weechat.WEECHAT_RC_OK
def url_recv_cb(data, buffer, time, tags, displayed, highlight, prefix, message): if w.config_get_plugin('debug') == 'on': w.prnt("","%s: Got url %s" % (SCRIPT_NAME, message)) #do not trigger on filtered lines and notices if displayed == '0' or prefix == w.config_string(w.config_get('weechat.look.prefix_network')): #leave alone if w.config_get_plugin('debug') == 'on': w.prnt("","%s: not displayed, or sent from network. Ignoring" % SCRIPT_NAME) return w.WEECHAT_RC_OK buf_name = w.buffer_get_string(buffer,"name") if w.config_get_plugin('debug') == 'on': w.prnt("","%s: from buffer %s" % (SCRIPT_NAME, buf_name)) #skip ignored buffers ignore_buffers = w.config_get_plugin('ignore_buffers').split(',') if buf_name in ignore_buffers: if w.config_get_plugin('debug') == 'on': w.prnt("","%s: %s is on ignore_buffer list" % (SCRIPT_NAME, buf_name)) return w.WEECHAT_RC_OK FNULL=open(os.devnull,'w') #can have multiple imgs per msg? for url in urlRe.findall(message): found = False if w.config_get_plugin('debug') == 'on': w.prnt("","%s: testing url %s" % (SCRIPT_NAME,url)) #is the url for an image? if imgRe.match(url): found = True if w.config_get_plugin('debug') == 'on': w.prnt("","%s: found image!" % SCRIPT_NAME) elif imgServiceRe.match(url): found = True if w.config_get_plugin('debug') == 'on': w.prnt("","%s: found image service!" % SCRIPT_NAME) if "imgur" in url: url = re.sub(r'.*imgur\.com/(.*)', r'http://i\.imgur\.com/\1\.jpg', url) #elif "cl.ly" in url: #keyword blacklisting? #domains are tied to the service. guess the services could be blacklisted if found: #blacklist subprocess.call(['%s/get_view_img.sh' % w.info_get("weechat_dir",""), '%s/img_cache' % w.info_get("weechat_dir",""), url], stdout=FNULL,stderr=subprocess.STDOUT) return w.WEECHAT_RC_OK
def handle_query(data, signal, signal_data): global buffer_data # data: empty string # signal: <server>,irc_in_PRIVMSG # signal_data: whole message unparsed # parse the message parsed = weechat.info_get_hashtable("irc_message_parse", {"message": signal_data}) # where should we answer? server = signal.split(",")[0] channel = parsed["channel"] current_nick = weechat.info_get("irc_nick", server) user = parsed["nick"] message = parsed["text"] if channel == current_nick: # we got a message through a private query, refuse it buffer_out = weechat.info_get("irc_buffer", server + "," + user) # close private buffers, but not server buffers # localvar_type can assume five values: private, channel, server, weechat and "" if weechat.buffer_get_string(buffer_out,"localvar_type") == "private": weechat.command(buffer_out, "How about speaking to me in public?") weechat.buffer_close(buffer_out) else: # query came from public channel if message.startswith(current_nick + ":"): # it's a command to our bot query = message.split(":")[1].strip() # remove the part before the colon, and lead/trail whitespaces s = query.split(" ", 1) command = s[0].lower() # command is case-insensitive args = s[1] if len(s) == 2 else "" target = "{0},{1}".format(server, channel) # this is the key to the dict containing the lists if command == "coin": out_msg = _coin() elif command == "help": out_msg = _help(user) elif command == "dice": out_msg = _dice(args) elif command == "server": out_msg = _server() elif command == "about": out_msg = _about() elif command == "rps": out_msg = _rps(user, args) elif command == "list": out_msg = _list(target,user,args) elif command == "coffee": out_msg = _coffee() elif command == "eightball": out_msg = _8ball() else: out_msg = "Unrecognized command. Type '{0}: help' to get a list of commands".format(current_nick) buffer_out = weechat.info_get("irc_buffer", server + "," + channel) if weechat.buffer_get_string(buffer_out,"localvar_type") == "channel": weechat.command(buffer_out, out_msg) return weechat.WEECHAT_RC_OK # must always return this or WEECHAT_RC_ERROR
def renderConversations(unused, command, return_code, out, err): global conversation_map global conv if return_code == weechat.WEECHAT_HOOK_PROCESS_ERROR: weechat.prnt("", "Error with command '%s'" % command) return weechat.WEECHAT_RC_OK if return_code > 0: weechat.prnt("", "return_code = %d" % return_code) if out != '': conv += out if return_code == weechat.WEECHAT_HOOK_PROCESS_RUNNING: weechat.prnt('', 'getting more data') return weechat.WEECHAT_RC_OK if err != "": weechat.prnt("", "stderr: %s" % err) return weechat.WEECHAT_RC_OK try: conversations = reversed(cPickle.loads(conv)) except EOFError: weechat.prnt('', 'wtrecv returned garbage') return weechat.WEECHAT_RC_OK for conversation in conversations: if not conversation.conv_id in conversation_map: conversation_map[conversation.conv_id] = conversation msgs = conversation.messages else: old = conversation_map[conversation.conv_id] conversation_map[conversation.conv_id] = conversation msgs = old.new_messages(conversation) for msg in msgs: if not conversation.number in number_map and msg['from'] != 'Me:': number_map[conversation.number] = msg['from'] for msg in msgs: if conversation.number in number_map: buf = weechat.buffer_search('python', number_map[conversation.number][:-1]) if not buf: buf = weechat.buffer_new(number_map[conversation.number][:-1], "textOut", "", "buffer_close_cb", "") else: buf = weechat.buffer_search('python', 'Me') if not buf: buf = weechat.buffer_new('Me', "textOut", "", "buffer_close_cb", "") if weechat.config_get_plugin('encrypt_sms') == 'True': msg['text'] = decrypt(msg['text'], buf) nick = msg['from'][:-1].strip() tags = 'notify_private,nick_' + msg['from'][:-1].strip() tags += ',log1,prefix_nick_' + weechat.info_get('irc_nick_color_name', nick) nick = msg['from'][:-1].strip() weechat.prnt_date_tags(buf, 0, tags, '\x03' + weechat.info_get('irc_nick_color', nick) + nick + '\t' + msg['text']) conv = '' callGV() return weechat.WEECHAT_RC_OK
def is_own(buffer, prefix): sender = prefix if not weechat.info_get("irc_is_nick", sender) or weechat.info_get("irc_is_nick", sender): sender = sender[1:] if not weechat.info_get("irc_is_nick", sender): return False nick = get_nick(buffer) if nick == sender: return True else: return False
def __init__(self, server, channel='', encoding='utf-8', **kwargs): self.server = server self.channel = channel self.encoding = encoding self.nickname = weechat.info_get("irc_nick", self.server) if channel: buffer_str = '{},{}'.format(server, channel) else: buffer_str = server self.buffer = weechat.info_get('irc_buffer', buffer_str) self._extra_data = {} if kwargs: self.extra_data(**kwargs)
def notify_quit_cb(data, signal, signal_data): """ callback for when a user in WeeChat's notify list quits IRC """ server, nick = signal_data.split(",") buf = w.info_get("irc_buffer", server + ",," + nick) if not buf: return w.WEECHAT_RC_OK w.prnt(buf, "{}{}{}{} has disconnected".format(w.prefix("quit"), w.info_get("nick_color", nick), nick, w.color("red"))) return w.WEECHAT_RC_OK
def auth_notice_check(data, buffer, args): server = buffer.split(',')[0] if args.startswith(":NickServ!NickServ@services") and \ args.find("If this is your nickname, type /msg NickServ") != -1 or args.find("This nickname is registered") != -1 : passwd = auth_get(weechat.info_get("irc_nick", server), server) if passwd != None: weechat.command(server, "/quote -server %s nickserv identify %s" % (server, passwd)) commands = auth_cmdget(server) if commands != '': for c in commands.split("|"): weechat.command(server, c.strip().replace("%n", weechat.info_get('irc_nick', server))) return weechat.WEECHAT_RC_OK
def notify_join_cb(data, signal, signal_data): """ callback for when a user in WeeChat's notify list connects to IRC """ server, nick = signal_data.split(",") buf = w.info_get("irc_buffer", server + ",," + nick) if not buf: return w.WEECHAT_RC_OK w.prnt(buf, "{}{}{}{} is back on the server".format(w.prefix("join"), w.info_get("nick_color", nick), nick, w.color("green"))) return w.WEECHAT_RC_OK
def weebuffer(reaction): rtnbuf = "{},{}".format(kserver, kchannel) buffer = w.info_get("irc_buffer", rtnbuf) botnick = w.info_get("irc_nick", kserver) if kchannel == botnick: #priate command = "msg {} {}".format(knick, reaction) else: #channel command = "msg {} {}".format(kchannel, reaction) #w.prnt("", command) cmdprefix = "/" w.command(buffer, cmdprefix + command)
def create_whois_regex_from_isupport(server): """Look into server ISUPPORT to create the ideal regex for removing channel modes from WHOIS output""" isupport_prefix = w.info_get("irc_server_isupport_value", "{},PREFIX".format(server)).split(")")[1] isupport_chantypes = w.info_get("irc_server_isupport_value", "{},CHANTYPES".format(server)) # Strip modes from WHOIS output. whois_regex = re.compile(r"(?:[{}]{})?(([{}])[^ ]+)".format( re.escape(isupport_prefix), "{1," + str(len(isupport_prefix)) + "}", re.escape(isupport_chantypes) )) return whois_regex
def fish_modifier_in_privmsg_cb(data, modifier, server_name, string): global fish_keys, fish_cyphers match = re.match( r"^(:(.*?)!.*? PRIVMSG (.*?) :)(\x01ACTION )?((\+OK |mcps )?.*?)(\x01)?$", string) #match.group(0): message #match.group(1): msg without payload #match.group(2): source #match.group(3): target #match.group(4): action #match.group(5): msg #match.group(6): +OK |mcps if not match: return string if match.group(3) == weechat.info_get("irc_nick", server_name): dest = match.group(2) else: dest = match.group(3) target = "%s/%s" % (server_name, dest.lower()) buffer = weechat.info_get("irc_buffer", "%s,%s" % (server_name, dest)) if not match.group(6): fish_announce_unencrypted(buffer, target) return string if target not in fish_keys: fish_announce_unencrypted(buffer, target) return string fish_announce_encrypted(buffer, target) if target not in fish_cyphers: b = Blowfish(fish_keys[target]) fish_cyphers[target] = b else: b = fish_cyphers[target] try: clean = blowcrypt_unpack(match.group(5), b) except MalformedError: return string if not match.group(4): return "%s%s" % (match.group(1), fish_msg_w_marker(clean)) return "%s%s%s\x01" % (match.group(1), match.group(4), fish_msg_w_marker(clean))
def show_notification(chan, message): weechat.prnt("", "Showing notification") payload_dic = { "endpoint": weechat.config_get_plugin("endpoint"), "key": weechat.config_get_plugin("key"), "authSecret": weechat.config_get_plugin("authSecret"), "payload": { "channel": chan, "msg": message }, "vapidDetails": { "publicKey": weechat.config_get_plugin("vapidPublicKey"), "privateKey": weechat.config_get_plugin("vapidPrivateKey"), "subject": weechat.config_get_plugin("vapidSubject") } } url = weechat.config_get_plugin("webpushProxy") weechat.prnt("", url) weechat.prnt("", json.dumps(payload_dic)) #payload = urllib.urlencode(payload_dic) python2_bin = weechat.info_get("python2_bin", "") or "python" weechat.prnt("", python2_bin) payload = json.dumps(payload_dic).replace('"', '\\"') #weechat.hook_process( # python2_bin + " -c \"import urllib2\n" # "req = urllib2.Request('" + url + "', '" + payload + "', {'Content-Type': 'application/json'})\n" # "res = urllib2.urlopen(req)\n\"", # 30 * 1000, "", "") cmd = """{} -c "import urllib2 req = urllib2.Request('{}', '{}', {{'Content-Type': 'application/json'}}) res = urllib2.urlopen(req) """.format(python2_bin, url, payload) weechat.prnt("", cmd) weechat.hook_process(cmd, 30 * 1000, "", "")
def go_main(): """Entry point.""" if not weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, 'go_unload_script', ''): return weechat.hook_command( SCRIPT_COMMAND, 'Quick jump to buffers', '[term(s)]', 'term(s): directly jump to buffer matching the provided term(s) single' 'or space dilimited list (without argument, list is displayed)\n\n' 'You can bind command to a key, for example:\n' ' /key bind meta-g /go\n\n' 'You can use completion key (commonly Tab and shift-Tab) to select ' 'next/previous buffer in list.', '%(buffers_names)', 'go_cmd', '') # set default settings version = weechat.info_get('version_number', '') or 0 for option, value in SETTINGS.items(): if not weechat.config_is_set_plugin(option): weechat.config_set_plugin(option, value[0]) if int(version) >= 0x00030500: weechat.config_set_desc_plugin( option, '%s (default: "%s")' % (value[1], value[0])) weechat.hook_info('go_running', 'Return "1" if go is running, otherwise "0"', '', 'go_info_running', '')
def fn_privmsg(data, bufferp, time, tags, display, is_hilight, prefix, msg): global bfList global settings servername = (w.buffer_get_string(bufferp, "name").split("."))[0] ownNick = w.info_get("irc_nick", servername) mySound = "" if not muted and prefix != ownNick: if settings["player"] == "": errMsg("'Player' isn't set!") else: for lEntry in bfList: if lEntry["buffer"] == w.buffer_get_string(bufferp, "name"): # we found a buffer of that name if lEntry["status"] == "on": if lEntry["sound"] == "": if settings["psound"] == "": errMsg( "No sound defined! Please set either the " + "regular 'psound'-option or the 'sound'-" + "option for this buffer.") else: mySound = settings["psound"] else: mySound = lEntry["sound"] s.Popen([settings["player"], expanduser(mySound)], stderr=s.STDOUT, stdout=s.PIPE) break return w.WEECHAT_RC_OK
def fish_modifier_in_332_cb(data, modifier, server_name, string): global fish_keys, fish_cyphers match = re.match(r"^(:.*? 332 .*? (.*?) :)((\+OK |mcps )?.*)$", string) if not match: return string target = "%s/%s" % (server_name, match.group(2)) targetl = ("%s/%s" % (server_name, match.group(2))).lower() buffer = weechat.info_get("irc_buffer", "%s,%s" % (server_name, match.group(2))) if targetl not in fish_keys or not match.group(4): fish_announce_unencrypted(buffer, target) return string if targetl not in fish_cyphers: b = Blowfish(fish_keys[targetl]) fish_cyphers[targetl] = b else: b = fish_cyphers[targetl] clean = blowcrypt_unpack(match.group(3), b) fish_announce_encrypted(buffer, target) return "%s%s" % (match.group(1), fish_msg_w_marker(clean))
def conversation_cb(data, buffer, args): """ Follows the reply trail until the original was found. NOTE: This might block for a while. """ global twitter conversation = [] reply_id = args # Loop as long as there was a reply_id. while reply_id: try: conversation.append(twitter.get_tweet(reply_id)) reply_id = conversation[-1].in_reply_to_status_id except TwitterError as error: print_error(error) break if conversation: # Reverse the conversation to get the oldest first. conversation.reverse() # Now display the conversation. print_to_current("%s-------------------" % wc.color("magenta")) for tweet in conversation: tweep_color = wc.info_get("irc_nick_color", tweet.screen_name) screen_name = tweep_color + tweet.screen_name output = "%s\t%s" % (screen_name, tweet.txt) if tweet.is_retweet: output += " (RT by @%s)" % tweet.rtscreen_name output += "\n[#STATUSID: %s]" % tweet.id print_to_current(output) print_to_current("%s-------------------" % wc.color("magenta")) return wc.WEECHAT_RC_OK
def wg_update_cache(): """ Download list of scripts and update local cache. """ global wg_config_option, wg_hook_process, wg_stdout # get data from website, via hook_process if wg_hook_process["update"] != "": weechat.unhook(wg_hook_process["update"]) wg_hook_process["update"] = "" weechat.prnt("", "%s: downloading list of scripts..." % SCRIPT_NAME) wg_stdout["update"] = "" wg_config_create_dir() url = weechat.config_string(wg_config_option["scripts_url"]) filename = wg_config_get_cache_filename() python2_bin = weechat.info_get("python2_bin", "") or "python" wg_hook_process["update"] = weechat.hook_process( python2_bin + " -c \"import urllib, urllib2\n" "req = urllib2.Request('" + url + "')\n" "try:\n" " response = urllib2.urlopen(req)\n" " file = open('" + filename + "', 'w')\n" " file.write(response.read())\n" " response.close()\n" " file.close()\n" "except urllib2.URLError, e:\n" " print 'error:%s' % e.code\n" "\"", TIMEOUT_UPDATE, "wg_process_update_cb", "")
def colorized_name(self): if colorize_nicks: color = w.info_get('irc_nick_color', self.name) def_color = w.color('default') return color + self.name + def_color else: return self.name
def unhide_buffer_cb(data, signal, signal_data): """Unhide a buffer on new activity. This callback unhides the buffer in which a new message has been received. If configuration option ``unhide_low`` is enabled, buffers with only low messages (like JOIN, PART, etc.) will be unhidden as well. """ server = signal.split(",")[0] message = weechat.info_get_hashtable( "irc_message_parse", {"message": signal_data}) channel = message["channel"] hotlist = hotlist_dict() buffer = weechat.info_get("irc_buffer", "{},{}".format(server, channel)) if not buffer in hotlist.keys(): # just some background noise return WEECHAT_RC_OK if (weechat.config_get_plugin("unhide_low") == "on" and hotlist[buffer]["count_low"] > 0 or hotlist[buffer]["count_message"] > 0 or hotlist[buffer]["count_private"] > 0 or hotlist[buffer]["count_highlight"] > 0): weechat.buffer_set(buffer, "hidden", "0") return WEECHAT_RC_OK
def on_command(buffer, args, actions): channel = w.buffer_get_string(buffer, 'localvar_channel') if not w.info_get("irc_is_channel", channel): w.prnt(buffer, "error: Active buffer does not appear to be a channel.") return w.WEECHAT_RC_ERROR server = w.buffer_get_string(buffer, 'localvar_server') nick, _, reason = args.partition(" ") info = w.infolist_get("irc_nick", '', f"{server},{channel},{nick}") w.infolist_next(info) host = w.infolist_string(info, "host") w.infolist_free(info) user, sep, host = host.rpartition("@") if sep and ("/" in host or _is_ip(host)): do_action(server, channel, actions, nick, user, host, reason) else: key = _waiting_key(server, nick) global WAITING if not key in WAITING: WAITING[key] = [] WAITING[key].append((channel, actions, reason)) w.command('', f"/quote -server {server} WHO {nick} %ihntu,582") return w.WEECHAT_RC_OK
def get_script_dir(): """Returns script's dir, creates it if needed.""" script_dir = weechat.info_get('weechat_dir', '') script_dir = os.path.join(script_dir, 'country') if not os.path.isdir(script_dir): os.makedirs(script_dir) return script_dir
def go_main(): """Entry point.""" if not weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, 'go_unload_script', ''): return weechat.hook_command( SCRIPT_COMMAND, 'Quick jump to buffers', '[name]', 'name: directly jump to buffer by name (without argument, list is ' 'displayed)\n\n' 'You can bind command to a key, for example:\n' ' /key bind meta-g /go\n\n' 'You can use completion key (commonly Tab and shift-Tab) to select ' 'next/previous buffer in list.', '%(buffers_names)', 'go_cmd', '') # set default settings version = weechat.info_get('version_number', '') or 0 for option, value in SETTINGS.items(): if not weechat.config_is_set_plugin(option): weechat.config_set_plugin(option, value[0]) if int(version) >= 0x00030500: weechat.config_set_desc_plugin( option, '%s (default: "%s")' % (value[1], value[0])) weechat.hook_info('go_running', 'Return "1" if go is running, otherwise "0"', '', 'go_info_running', '')
def main(): if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, "", ""): version = int(weechat.info_get('version_number', '')) or 0 # unset unused setting from older versions of script if weechat.config_is_set_plugin('display_unit'): weechat.prnt("", "Option plugins.var.python.bandwidth.display_unit no longer used, removing.") weechat.config_unset_plugin('display_unit') # set default settings for option in SCRIPT_SETTINGS.keys(): if not weechat.config_is_set_plugin(option): value = SCRIPT_SETTINGS[option][0] if isinstance(value, str): pass elif isinstance(value, bytes): pass elif isinstance(value, unicode): value = value.encode('utf8') weechat.config_set_plugin(option, value) if version >= 0x00030500: weechat.config_set_desc_plugin(option, SCRIPT_SETTINGS[option][1]) # ensure sane refresh_rate setting if int(weechat.config_get_plugin('refresh_rate')) < 1: weechat.prnt("", "{}Invalid value for option plugins.var.python.bandwidth.refresh_rate, setting to default of {}".format(weechat.prefix("error"), SCRIPT_SETTINGS['refresh_rate'][0])) weechat.config_set_plugin('refresh_rate', SCRIPT_SETTINGS['refresh_rate'][0]) # create the bandwidth monitor bar item weechat.bar_item_new('bandwidth', 'bandwidth_item_cb', '') # update it every plugins.var.python.bandwidth.refresh_rate seconds weechat.hook_timer(int(weechat.config_get_plugin('refresh_rate'))*1000, 0, 0, 'bandwidth_timer_cb', '')
def _parse_message(self): """Parse the signal_data""" if int(WEECHAT_VERSION) >= 0x00030400: # Newer (>=0.3.4) versions of WeeChat can prepare a hash with most # of what we want. self.details = weechat.info_get_hashtable("irc_message_parse", { "message": self.signal_data(), "server": self.server() }) else: # WeeChat <0.3.4 we have to construct it ourselves. (source, command, channel, message) = ( self._signal_data.split(" ", 3) ) self.details = {} self.details['arguments'] = "{channel} {message}".format( channel=channel, message=message) self.details['channel'] = channel self.details['command'] = command self.details['host'] = source.lstrip(":") self.details['nick'] = weechat.info_get( "irc_nick_from_host", self.signal_data()) # WeeChat leaves this important part to us. Get the actual message. self.details['message'] = self.arguments().split(" :", 1)[1]
def show_favorites_cb(data, buffer, args): """ Show all the tweets that are favourited by the user. """ global twitter try: favs = twitter.get_favorites() except TwitterError as error: print_error(error) return wc.WEECHAT_RC_OK if favs: print_to_current("%sFAVOURITES\t%s-------------------" % (wc.color("yellow"), wc.color("magenta"))) for fav in favs: nick_color = wc.info_get("irc_nick_color", fav.screen_name) screen_name = nick_color + fav.screen_name expand_urls = wc.config_string_to_boolean( wc.config_get_plugin("expand_urls")) text = fav.text_unescaped if expand_urls: text = fav.text output = "%s\t%s" % (screen_name, text) if fav.is_retweet: output += " (RT by @%s)" % fav.rtscreen_name output += "\n[#STATUSID: %s]" % fav.id print_to_current(output) print_to_current("%s-------------------" % wc.color("magenta")) return wc.WEECHAT_RC_OK
def redirect_isonhandler(data, signal, hashtable): if hashtable['output'] == '': return weechat.WEECHAT_RC_OK # ISON_nicks contains nicks that are already online on server (separated with space) message, ISON_nicks = hashtable['output'].split(':')[1:] ISON_nicks = [nick.lower() for nick in ISON_nicks.split()] for nick in server_nicks(hashtable['server']): mynick = weechat.info_get('irc_nick', hashtable['server']) if nick.lower() == mynick.lower(): return weechat.WEECHAT_RC_OK elif nick.lower() not in ISON_nicks and nick != '': if int(version) >= 0x00040200: password = weechat.string_eval_expression( weechat.config_get_plugin('%s.password' % hashtable['server']), {}, {}, {}) else: password = weechat.config_get_plugin( '%s.password' % hashtable['server']) # get password for given server grabnick(hashtable['server'], nick) # get your nick back if password != '' and OPTIONS['nickserv'] != '': t = Template(OPTIONS['nickserv']) run_msg = t.safe_substitute(server=hashtable['server'], passwd=password) weechat.command('', run_msg) return weechat.WEECHAT_RC_OK
def lb_line_run(): global lb_channels, lb_curline, lb_network buff = weechat.info_get("irc_buffer", lb_network) channel = lb_channels[lb_curline]['channel'] command = "/join %s" % channel weechat.command(buff, command) return
def shutdown(): for my_file in ['output_file', 'output_active_buffer']: filename = w.config_get_plugin(my_file).replace( "%h", w.info_get("weechat_dir", "")) if os.path.exists(filename): os.remove(filename) return w.WEECHAT_RC_OK
def fish_modifier_in_topic_cb(data, modifier, server_name, string): global fish_keys, fish_cyphers match = re.match( r"^(?:@time=[\d:TZ.-]+\s)?(:.*?!.*? TOPIC (.*?) :)((\+OK |mcps )?.*)$", string) #match.group(0): message #match.group(1): msg without payload #match.group(2): channel #match.group(3): topic #match.group(4): +OK |mcps if not match: return string target = "%s/%s" % (server_name, match.group(2)) targetl = target.lower() buffer = weechat.info_get("irc_buffer", "%s,%s" % (server_name, match.group(2))) if targetl not in fish_keys or not match.group(4): fish_announce_unencrypted(buffer, target) return string if targetl not in fish_cyphers: b = Blowfish(fish_keys[targetl]) fish_cyphers[targetl] = b else: b = fish_cyphers[targetl] clean, broken = blowcrypt_unpack(match.group(3), b) fish_announce_encrypted(buffer, target) if broken: fish_announce_broken(buffer, target) return "%s%s" % (match.group(1), fish_msg_w_marker(clean))
def triggerwatch(data, buffer, args): global kserver, kchannel, knick, mode if options["enabled"] == "on": try: null, srvmsg = args.split(" PRIVMSG ", 1) except: return w.WEECHAT_RC_OK try: kchannel, query = srvmsg.split( " :{} ".format(options["weather_trigger"]), 1) mode = "conditions" except ValueError: try: kchannel, query = srvmsg.split( " :{} ".format(options["forecast_trigger"]), 1) mode = "forecast" except ValueError: return w.WEECHAT_RC_OK kserver = str(buffer.split(",", 1)[0]) knick = w.info_get("irc_nick_from_host", args) query = query.replace(" ", "%20") autoc_url = "url:http://autocomplete.wunderground.com/aq?query={}&format=JSON".format( query) w.hook_process(autoc_url, 30 * 1000, "wu_autoc", "") return w.WEECHAT_RC_OK
def redirect_isonhandler(data, signal, hashtable): if hashtable['output'] == '': return weechat.WEECHAT_RC_OK nothing, message, nicks = hashtable['output'].split(':') nicks = [nick.lower() for nick in nicks.split()] for nick in servernicks(hashtable['server']): mynick = weechat.info_get('irc_nick', hashtable['server']) if nick.lower() == mynick.lower(): return weechat.WEECHAT_RC_OK elif nick.lower() not in nicks: password = weechat.config_get_plugin( '%s.password' % hashtable['server']) # get password for given server grabnick(hashtable['server'], nick) # get back your nick if (password) != '': if OPTIONS['nickserv'] == '': return weechat.WEECHAT_RC_OK t = Template(OPTIONS['nickserv']) run_msg = t.safe_substitute(server=hashtable['server'], passwd=password) weechat.command('', run_msg) return weechat.WEECHAT_RC_OK if 0 in [ nick.lower() in [mynick.lower() for mynick in servernicks(hashtable['server'])] for nick in nicks ]: # if any nick which is return by ison is not on our checklist we're not the caller return weechat.WEECHAT_RC_OK else: # seems like we're the caller -> ignore the output return weechat.WEECHAT_RC_OK
def fish_modifier_out_privmsg_cb(data, modifier, server_name, string): global fish_keys, fish_cyphers match = re.match(r"^(PRIVMSG (.*?) :)(.*)$", string) if not match: return string target = "%s/%s" % (server_name, match.group(2)) targetl = ("%s/%s" % (server_name, match.group(2))).lower() buffer = weechat.info_get("irc_buffer", "%s,%s" % (server_name, match.group(2))) if targetl not in fish_keys: fish_announce_unencrypted(buffer, target) return string if targetl not in fish_cyphers: b = Blowfish(fish_keys[targetl]) fish_cyphers[targetl] = b else: b = fish_cyphers[targetl] cypher = blowcrypt_pack(match.group(3), b) fish_announce_encrypted(buffer, target) return "%s%s" % (match.group(1), cypher)
def show_notification(chan, nick, message): API_TOKEN = weechat.config_get_plugin("api_token") if API_TOKEN != "": url = "https://irssinotifier.appspot.com/API/Message" postdata = urllib.urlencode({'apiToken':API_TOKEN,'nick':encrypt(nick),'channel':encrypt(chan),'message':encrypt(message),'version':13}) version = weechat.info_get("version_number", "") or 0 hook1 = weechat.hook_process_hashtable("url:"+url, { "postfields": postdata}, 2000, "", "")
def playFile(data,signal,signal_data): message=signal_data.split("PRIVMSG")[1].split(":",1)[1] msg = message.split(" ") if msg[0][0]== '>' and len(msg) == 3: if msg[0] == '>play' and msg[2] ==nick: ##Debugging weechat.prnt("",message) weechat.prnt("",msg[0]) weechat.prnt("",msg[1]) weechat.prnt("",msg[2]) try: weechat.prnt("","Playing with vlc") subprocess.Popen(["vlc",msg[1]],stdout=subprocess.PIPE,stderr=subprocess.STDOUT) except: weechat.prnt("","Error: Occurred") if msg[0] == '>url' and msg[2] ==nick: try: weechat.prnt("","Playing with web-browser") subprocess.Popen(["xdg-open",msg[1]],stdout=subprocess.PIPE,stderr=subprocess.STDOUT) except: weechat.prnt("","Error: Occurred") else: server = signal.split(",")[0] channel = signal_data.split(":")[-1] buffer = weechat.info_get("irc_buffer", "%s,%s" % (server,channel)) weechat.prnt(buffer,"Enter in form >play [url] [nickName]") return weechat.WEECHAT_RC_OK
def fish_modifier_out_topic_cb(data, modifier, server_name, string): global fish_keys, fish_cyphers match = re.match(r"^(TOPIC (.*?) :)(.*)$", string) if not match: return string if not match.group(3): return string target = "%s/%s" % (server_name, match.group(2)) targetl = ("%s/%s" % (server_name, match.group(2))).lower() buffer = weechat.info_get("irc_buffer", "%s,%s" % (server_name, match.group(2))) if targetl not in fish_keys: fish_announce_unencrypted(buffer, target) return string if targetl not in fish_cyphers: b = Blowfish(fish_keys[targetl]) fish_cyphers[targetl] = b else: b = fish_cyphers[targetl] cypher = blowcrypt_pack(match.group(3), b) fish_announce_encrypted(buffer, target) return "%s%s" % (match.group(1), cypher)
def print_as_lines(target, matches, data, limit, total): """Prints the output as a line-separated list of nicks.""" prefix = fmt_prefix(data) mstring = "{}{}".format(fmt_mode_char(data["mode"]), "" if data["set"] else " removal") mask = fmt_banmask(data["mask"]) target_in_prefix = "_target_" in prefix i = 0 for name in matches: if target_in_prefix: pf = prefix.replace("_target_", "{}{}{}".format( w.color(w.info_get("irc_nick_color_name", name)), name, w.color("reset"))) else: pf = prefix if (total - (limit - i) == 1) or (i >= limit): left = total - i left -= 1 if target_in_prefix else 0 w.prnt(target, "{}\tand {} more match{}..".format(pf, left, "es" if left != 1 else "")) break if target_in_prefix: w.prnt(target, "{}\tmatches {} {}".format(pf, mstring, mask)) else: w.prnt(target, "{}\t{} {} matches {}".format(pf, mstring, mask, fmt_nick(name))) i += 1
def completion_cb(data, completion_item, buffer, completion): if weechat.info_get('version', '') <= '2.8': for number in contacts: weechat.hook_completion_list_add(completion, number, 0, weechat.WEECHAT_LIST_POS_SORT) weechat.hook_completion_list_add(completion, contact_name(number).lower(), 0, weechat.WEECHAT_LIST_POS_SORT) weechat.hook_completion_list_add(completion, contact_name(number), 0, weechat.WEECHAT_LIST_POS_SORT) for group in groups: weechat.hook_completion_list_add(completion, groups[group]['name'].lower(), 0, weechat.WEECHAT_LIST_POS_SORT) weechat.hook_completion_list_add(completion, groups[group]['name'], 0, weechat.WEECHAT_LIST_POS_SORT) else: for number in contacts: weechat.completion_list_add(completion, number, 0, weechat.WEECHAT_LIST_POS_SORT) weechat.completion_list_add(completion, contact_name(number).lower(), 0, weechat.WEECHAT_LIST_POS_SORT) weechat.completion_list_add(completion, contact_name(number), 0, weechat.WEECHAT_LIST_POS_SORT) for group in groups: weechat.completion_list_add(completion, groups[group]['name'].lower(), 0, weechat.WEECHAT_LIST_POS_SORT) weechat.completion_list_add(completion, groups[group]['name'], 0, weechat.WEECHAT_LIST_POS_SORT) return weechat.WEECHAT_RC_OK
def inactive(): inactivity = int(weechat.info_get('inactivity', '')) #debug('user inactivity: %s' %inactivity) if inactivity > 20: return True else: return False
def kill_daemon(*args): pid_path = '%s/signal.pid' % weechat.info_get("weechat_dir", "") try: pf = file(pid_path, 'r') pid = int(pf.read().strip()) pf.close() except IOError: logger.debug( "IOError while reading %s, proly not gonna kill the daemon", pid_path) return weechat.WEECHAT_RC_OK try: os.kill(pid, SIGTERM) except OSError: pass if signalpid is not None: logger.debug("Killing signal-cli process (PID %s)", signalpid) try: os.kill(signalpid, SIGTERM) except: logger.exception("Failed to kill signal-cli process %s", signalpid) else: logger.debug("No known signal-cli process to kill :/") return weechat.WEECHAT_RC_OK
def triggerwatch(data, buffer, args): global kserver, kchannel, knick, mode if options["enabled"] == "on": try: null, srvmsg = args.split(" PRIVMSG ", 1) except: return w.WEECHAT_RC_OK try: kchannel, query = srvmsg.split(" :{} ".format(options["weather_trigger"]), 1) mode = "conditions" except ValueError: try: kchannel, query = srvmsg.split(" :{} ".format(options["forecast_trigger"]), 1) mode = "forecast" except ValueError: return w.WEECHAT_RC_OK kserver = str(buffer.split(",", 1)[0]) knick = w.info_get("irc_nick_from_host", args) query = query.replace(" ", "%20") autoc_url = "url:http://autocomplete.wunderground.com/aq?query={}&format=JSON".format(query) w.hook_process(autoc_url, 30 * 1000, "wu_autoc", "") return w.WEECHAT_RC_OK
def privmsg(data, signal, signal_data): (server, signal) = signal.split(",") channels = wc.config_get_plugin('channels').replace(',', '|') api_key = wc.config_get_plugin('api_key') if re.match('^\${sec\.data\.(.*)}$', api_key): api_key = wc.string_eval_expression(api_key, {}, {}, {}) bots_list = wc.config_get_plugin('other_bots').split(",") details = wc.info_get_hashtable("irc_message_parse", {"message": signal_data, "server": server}) youtube_regex_match = re.compile(r'(.*https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/(watch\?v=|embed/|v/|.+\?v=)?([a-zA-Z0-9_-]{11})').match(details['text']) buffer_name = details['channel'] buffer_pointer = wc.info_get("irc_buffer", "%s,%s" % (server, buffer_name)) channels_regex = re.compile(r'(%s)' % (channels), re.I) bots_exist = False if channels_regex.match(buffer_name) and youtube_regex_match: if not bots_list == "not_set": for other_bots in bots_list: bots_test = wc.nicklist_search_nick(buffer_pointer, "", other_bots) if bots_test: bots_exist = True if not bots_exist: if not api_key == "not_set": vid_id = youtube_regex_match.group(6) rvt = requests.get('https://www.googleapis.com/youtube/v3/videos/?id={0}&part=snippet&key={1}'.format(vid_id, api_key)) rvc = requests.get('https://www.googleapis.com/youtube/v3/videos/?id={0}&part=statistics&key={1}'.format(vid_id, api_key)) try: vid_title = rvt.json()['items'][0]['snippet']['title'].encode('utf-8') vid_channel = rvt.json()['items'][0]['snippet']['channelTitle'].encode('utf-8') vid_views = rvc.json()['items'][0]['statistics']['viewCount'] wc.command("", r"/msg {0} [Youtube] {1} | Channel: {2} | Views: {3}".format(buffer_name, vid_title, vid_channel, vid_views)) except: wc.command("", r"/msg {0} [Youtube] Error getting video info.".format(buffer_name)) else: wc.command("", r"/msg {0} Youtube api key not set.".format(buffer_name)) return wc.WEECHAT_RC_OK
def wg_get_local_scripts(): """ Get list of all local scripts (in languages and autoload dirs). Return a dictionary with language as key and list of paths as value, with autoloaded scripts at beginning of list, for example: { 'perl': [ 'autoload/buffers.pl', 'autoload/weetris.pl', 'beep.pl', 'launcher.pl' ], 'python': [ 'autoload/weeget.py', 'go.py', 'vdm.py' ] } """ files = {} for language in SCRIPT_EXTENSION.keys(): files[language] = [] autoloaded_files = [] rootdir = weechat.info_get("weechat_dir", "") + os.sep + language for root, dirs, listfiles in os.walk(rootdir): if root == rootdir: files[language] = listfiles elif root == rootdir + os.sep + "autoload": autoloaded_files = listfiles for file in autoloaded_files: if file in files[language]: files[language].remove(file) files[language].insert(0, "autoload" + os.sep + file) return files
def colorize_nick_color(nick, my_nick): if nick == my_nick: return weechat.color( weechat.config_string( weechat.config_get('weechat.color.chat_nick_self'))) else: return weechat.info_get('irc_nick_color', nick)
def launch_daemon(*_): if len(options.get('number', '')) > 0: init_socket() pid_path = '%s/signal.pid' % weechat.info_get("weechat_dir", "") sock_path = '%s/signal.sock' % weechat.info_get("weechat_dir", "") daemon_command = [ 'python', daemon_path, sock_path, pid_path, options.get('number'), options.get('signal_cli_command') ] if options.get('debug', '') != '': daemon_command.append(options.get('debug', '')) logger.debug("Preparing to launch daemon with comand %s" % " ".join(daemon_command)) weechat.hook_process(" ".join(daemon_command), 1000, "daemon_cb", "") return weechat.WEECHAT_RC_OK
def default_umodes(server): modes = {} for mode, color in DEFAULT_UMODES.items(): if not mode[0] == "+": # ISUPPORT mode name, default = mode.split("=", 1) if w.info_get("irc_server_isupport", f"{server},{name}") == "1": mode = w.info_get("irc_server_isupport_value", f"{server},{name}") or default else: continue else: mode = mode[1:] modes[mode] = color return modes
def get_char (c): try: with open( weechat.info_get('weechat_dir', '') + '/python/asciiwrite/font/' + str(ord(c)), 'r' ) as f: return f.read().split('\n') except: weechat.prnt('', 'Did not found char %s [%d]. Replacing by NULL.' % (c, ord(c))) return []
def wg_download_file(url, filename, timeout, callback, callback_data): """Download a file with an URL. Return hook_process created.""" version = weechat.info_get("version_number", "") or 0 if int(version) >= 0x00030700: return weechat.hook_process_hashtable("url:%s" % url, { "file_out": filename }, timeout, callback, callback_data) else: script = [ "import sys", "try:", " if sys.version_info >= (3,):", " import urllib.request", " response = urllib.request.urlopen('%s')" % url, " else:", " import urllib2", " response = urllib2.urlopen(urllib2.Request('%s'))" % url, " f = open('%s', 'wb')" % filename, " f.write(response.read())", " response.close()", " f.close()", "except Exception as e:", " print('error:' + str(e))" ] return weechat.hook_process("python -c \"%s\"" % "\n".join(script), timeout, callback, callback_data)
class TermTitleMapping(Mapping): substitutions = { 'buffer_title': lambda : weechat.buffer_get_string(weechat.current_buffer(), "title"), 'buffer_name': lambda : weechat.buffer_get_string(weechat.current_buffer(), "name"), 'buffer_plugin': lambda : weechat.buffer_get_string(weechat.current_buffer(), "plugin"), 'buffer_number': lambda : weechat.buffer_get_integer(weechat.current_buffer(), "number"), 'buffer_nicklist_count': lambda : weechat.buffer_get_integer(weechat.current_buffer(), "nicklist_visible_count"), 'buffer_count': lambda : buffer_count(), 'hotlist': lambda : hotlist(), 'version': lambda : weechat.info_get("version", "") } def __getitem__(self, key): return self.substitutions[key]() def __iter__(self): return self.substitutions.iterkeys() def __len__(self): return len(self.substitutions)