# -*- coding: utf-8 -*- ### # Copyright (c) 2009-2010 by Elián Hanisch # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . ### ### # # Script for auto op/voice users. # # It uses expressions for match user's usermasks when they join. Use at your own risk. # # Commands: # * /automode: see /help automode # # Settings: # * plugins.var.python.automode.enabled: # Self-explanatory, disables/enables automodes. # Valid values: 'on', 'off' Default: 'on' # # 2011-09-20 # version 0.1.1: fix bug with channels with uppercase letters. # # 2014-04-15 # version 0.1.2: fix bug where mode commands weren't sent properly # # 2016-06-28 # version 0.1.3: support extended-join messages # # 2019-03-09 # version 0.1.4: support python3 ### from __future__ import print_function SCRIPT_NAME = "automode" SCRIPT_AUTHOR = "Elián Hanisch " SCRIPT_VERSION = "0.1.4" SCRIPT_LICENSE = "GPL3" SCRIPT_DESC = "Script for auto op/voice users when they join." try: import weechat from weechat import prnt WEECHAT_RC_OK = weechat.WEECHAT_RC_OK import_ok = True except ImportError: print ("This script must be run under WeeChat.") print ("Get WeeChat now at: http://weechat.flashtux.org/") import_ok = False from fnmatch import fnmatch def debug(s, *args): if not isinstance(s, basestring): s = str(s) if args: s = s %args prnt('', '%s\t%s' % (script_nick, s)) # settings settings = { 'enabled': 'on' } ################ ### Messages ### script_nick = SCRIPT_NAME def error(s, buffer=''): """Error msg""" weechat.prnt(buffer, '%s%s %s' %(weechat.prefix('error'), script_nick, s)) def say(s, buffer=''): """normal msg""" weechat.prnt(buffer, '%s\t%s' %(script_nick, s)) ############## ### Config ### boolDict = {'on':True, 'off':False} def get_config_boolean(config): value = weechat.config_get_plugin(config) try: return boolDict[value] except KeyError: default = settings[config] error("Error while fetching config '%s'. Using default value '%s'." %(config, default)) error("'%s' is invalid, allowed: 'on', 'off'" %value) return boolDict[default] def get_config_list(config): value = weechat.config_get_plugin(config) if value: return value.split(',') else: return [] ################# ### Functions ### def find_matching_users(server, channel, pattern): # this is for check patterns when they are added infolist = weechat.infolist_get('irc_nick', '', '%s,%s' %(server, channel)) L = [] while weechat.infolist_next(infolist): nick = weechat.infolist_string(infolist, 'name') host = weechat.infolist_string(infolist, 'host') userhost = '%s!%s' %(nick, host) if fnmatch(userhost.lower(), pattern): L.append(nick) weechat.infolist_free(infolist) return L def get_userhost(server, channel, nick): try: infolist = weechat.infolist_get('irc_nick', '', '%s,%s' %(server, channel)) while weechat.infolist_next(infolist): _nick = weechat.infolist_string(infolist, 'name').lower() if _nick == nick: host = weechat.infolist_string(infolist, 'host') userhost = '%s!%s' %(nick, host) return userhost.lower() finally: weechat.infolist_free(infolist) def get_patterns_in_config(filter): d = {} infolist = weechat.infolist_get('option', '', 'plugins.var.python.%s.%s' %(SCRIPT_NAME, filter)) while weechat.infolist_next(infolist): name = weechat.infolist_string(infolist, 'option_name') name = name[len('python.%s.' %SCRIPT_NAME):] # channels might have dots in their names, so we'll strip type from right and server # from left. Lets hope that users doesn't use dots in server names. name, _, type = name.rpartition('.') if type not in ('op', 'halfop', 'voice'): # invalid option continue server, _, channel = name.partition('.') value = weechat.infolist_string(infolist, 'value') if not value: continue else: value = value.split(',') key = (server, channel) if key not in d: d[key] = {type:value} else: d[key][type] = value weechat.infolist_free(infolist) return d ######################## ### Script callbacks ### def join_cb(data, signal, signal_data): #debug('JOIN: %s %s', signal, signal_data) prefix, _, channel = signal_data.split()[:3] prefix = prefix[1:].lower() if channel[0] == ':': channel = channel[1:] server = signal[:signal.find(',')] for mode_type, shorthand in {'op':'o', 'halfop':'h', 'voice':'v'}.items(): l = get_config_list('.'.join((server.lower(), channel.lower(), mode_type))) for pattern in l: #debug('checking: %r - %r', prefix, pattern) if fnmatch(prefix, pattern): buf = weechat.buffer_search('irc', '%s.%s' %(server, channel)) if buf: weechat.command(buf, '/wait 1 /mode {} +{} {}'.format(channel, shorthand, prefix[:prefix.find('!')])) return WEECHAT_RC_OK return WEECHAT_RC_OK def command(data, buffer, args): global join_hook if not args: args = 'list' channel = weechat.buffer_get_string(buffer, 'localvar_channel') server = weechat.buffer_get_string(buffer, 'localvar_server') args = args.split() cmd = args[0] try: if cmd in ('add', 'del'): if not weechat.info_get('irc_is_channel', channel): error("Not an IRC channel buffer.") return WEECHAT_RC_OK type, match = args[1], args[2:] if type not in ('op', 'voice', 'halfop'): raise ValueError("valid values are 'op', 'halfop' and 'voice'.") if not match: raise ValueError("missing pattern or nick.") match = match[0].lower() config = '.'.join((server, channel.lower(), type)) L = get_config_list(config) if cmd == 'add': # check if pattern is a nick if weechat.info_get('irc_is_nick', match): userhost = get_userhost(server, channel, match) if userhost: match = userhost.lower() nicks = find_matching_users(server, channel, match) n = len(nicks) if n == 0: say("'%s' added, matches 0 users." %match, buffer) elif n == 1: say("'%s' added, matches 1 user: %s" %(match, nicks[0]), buffer) elif n > 1: say("'%s' added, matches %s%s%s users: %s" %( match, weechat.color('lightred'), n, color_reset, ' '.join(nicks)), buffer) if match not in L: L.append(match) elif cmd == 'del': if match not in L: say("'%s' not found in %s.%s" %(match, server, channel), buffer) else: say("'%s' removed." %match, buffer) del L[L.index(match)] if L: weechat.config_set_plugin(config, ','.join(L)) else: weechat.config_unset_plugin(config) elif cmd == 'disable': if join_hook: weechat.unhook(join_hook) weechat.config_set_plugin('enabled', 'off') say("%s script disabled." %SCRIPT_NAME, buffer) elif cmd == 'enable': if join_hook: weechat.unhook(join_hook) join_hook = weechat.hook_signal('*,irc_in_join', 'join_cb', '') weechat.config_set_plugin('enabled', 'on') say("%s script enabled." %SCRIPT_NAME, buffer) elif cmd == 'list': if weechat.info_get('irc_is_channel', channel): filter = '%s.%s.*' %(server, channel) else: filter = '*' buffer = '' # print in core buffer if not get_config_boolean('enabled'): say('Automodes currently disabled.', buffer) patterns = get_patterns_in_config(filter) if not patterns: if buffer: say('No automodes for %s.' %channel, buffer) else: say('No automodes.', buffer) return WEECHAT_RC_OK for key, items in patterns.items(): say('%s[%s%s.%s%s]' %(color_chat_delimiters, color_chat_buffer, key[0], key[1], color_chat_delimiters), buffer) for type, masks in items.items(): for mask in masks: say(' %s%s%s: %s%s' %(color_chat_nick, type, color_chat_delimiters, color_reset, mask), buffer) else: raise ValueError("'%s' isn't a valid option. See /help %s" %(cmd, SCRIPT_NAME)) except ValueError as e: error('Bad argument: %s' %e) return WEECHAT_RC_OK return WEECHAT_RC_OK def completer(data, completion_item, buffer, completion): channel = weechat.buffer_get_string(buffer, 'localvar_channel') if not weechat.info_get('irc_is_channel', channel): return WEECHAT_RC_OK server = weechat.buffer_get_string(buffer, 'localvar_server') input = weechat.buffer_get_string(buffer, 'input') type = input.split()[2] patterns = get_patterns_in_config('%s.%s.%s' %(server, channel, type)) if not patterns: return WEECHAT_RC_OK for mask in patterns[(server, channel)][type]: weechat.hook_completion_list_add(completion, mask, 0, weechat.WEECHAT_LIST_POS_END) return WEECHAT_RC_OK if __name__ == '__main__' and import_ok and \ weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, SCRIPT_DESC, '', ''): # colors color_chat_delimiters = weechat.color('chat_delimiters') color_chat_nick = weechat.color('chat_nick') color_reset = weechat.color('reset') color_chat_buffer = weechat.color('chat_buffer') # pretty [automode] script_nick = '%s[%s%s%s]%s' %(color_chat_delimiters, color_chat_nick, SCRIPT_NAME, color_chat_delimiters, color_reset) for opt, val in list(settings.items()): if not weechat.config_is_set_plugin(opt): weechat.config_set_plugin(opt, val) global join_hook if get_config_boolean('enabled'): join_hook = weechat.hook_signal('*,irc_in_join', 'join_cb', '') else: join_hook = '' weechat.hook_completion('automode_patterns', 'automode patterns', 'completer', '') weechat.hook_command(SCRIPT_NAME, SCRIPT_DESC , "[ (add|del) | list | disable | enable ]", " add: Adds a new automode for current channel. If a nick is given instead of an" " expression, it will use nick's exact usermask.\n" " del: Removes an automode in current channel.\n" " type: Specifies the user mode, it should be either 'op', 'halfop' or 'voice'.\n" "expression: Case insensible expression for match users when they join current channel." " It should be of the format 'nick!user@host', wildcards '?', '*', and character groups" " are allowed.\n" " list: List automodes for current channel, or all automodes if current buffer" " isn't an IRC channel. This is the default action if no option is given.\n" " disable: Disables the script.\n" " enable: Enables the script.\n" "\n" "Be careful with the expressions you use, they must be specific and match only one" " user, if they are too vague, like 'nick!*' you might op users you don't want and lose" " control of your channel.", "add op|halfop|voice %(nicks)"\ "||del op|halfop|voice %(automode_patterns)"\ "||list||disable||enable", 'command', '')