# Get the punishment and reason. punishment = mhl_settings.get('punishment', MASSHIGHLIGHT_DEFAULTS['punishment']).lower() reason = mhl_settings.get('reason', MASSHIGHLIGHT_DEFAULTS['reason']) log.info("(%s) antispam: punishing %s => %s for mass highlight spam", irc.name, irc.get_friendly_name(source), channel) punished = _punish(irc, source, channel, punishment, reason) break log.debug('(%s) antispam.masshighlight: got %s/%s nicks on message to %r', irc.name, len(nicks_caught), min_nicks, channel) return not punished # Filter this message from relay, etc. if it triggered protection utils.add_hook(handle_masshighlight, 'PRIVMSG', priority=1000) utils.add_hook(handle_masshighlight, 'NOTICE', priority=1000) TEXTFILTER_DEFAULTS = { 'reason': "Spam is prohibited", 'punishment': 'kick+ban+block', 'watch_pms': False, 'enabled': False, 'munge_unicode': True, } def handle_textfilter(irc, source, command, args): """Antispam text filter handler.""" target = args['target'] text = args['text'] txf_settings = irc.get_service_option('antispam', 'textfilter', TEXTFILTER_DEFAULTS)
staffchans = irc.get_service_option('operlock', 'channels', default=None) or [] staffchans = list(map(irc.to_lower, staffchans)) if not staffchans: return elif not _should_enforce(irc, source, args): return for channel in args['channels']: if irc.to_lower(channel) in staffchans and irc.is_oper(source): if irc.protoname in ('inspircd', 'unreal'): irc.msg(source, "Warning: You must deoper to leave %r." % channel, notice=True) irc._send_with_prefix(irc.sid, 'SAJOIN %s %s' % (source, channel)) else: log.warning('(%s) Force join is not supported on this IRCd %r!', irc.name, irc.protoname) utils.add_hook(handle_part, 'PART') DEOPER_KICK_REASON = 'User has deopered' def handle_mode(irc, source, command, args): """Kicks users who deoper from designated staff channels.""" staffchans = irc.get_service_option('operlock', 'channels', default=None) or [] staffchans = list(map(irc.to_lower, staffchans)) if not staffchans: return elif not _should_enforce(irc, source, args): return if ('-o', None) in args['modes']: # User is deopering for channel in irc.users[source].channels: if irc.to_lower(channel) in staffchans:
# irc.pseudoclient stores the IrcUser object of the main PyLink client. # (i.e. the user defined in the bot: section of the config) if 'used REGISTER on' in text and channel == '#debug': nick = text.split() nick = nick[1] nick = nick.split('!') nick = nick[0] regchannel = text.split() regchannel = regchannel[6] irc.proto.join(weuid, regchannel) irc.proto.message(weuid, regchannel, 'Welcome to ElectroCode, %s' % nick) irc.proto.message( weuid, regchannel, "I've auto-assigned a bot for you to use. If you want a different one, you can look at '/bs botlist'." ) irc.proto.message(weuid, regchannel, "If you have any problems, please join '#help'.") irc.proto.message(weuid, regchannel, "If you've seen this before, just ignore me.") irc.proto.part(weuid, regchannel, "Welcome") utils.add_hook(hook_privmsg, 'PRIVMSG') def die(irc): utils.unregisterService('welcome')
for chan in channels: if utils.isChannel(chan): irc.proto.join(u, chan) irc.callHooks([ irc.sid, 'PYLINK_SERVICE_JOIN', { 'channel': chan, 'users': [u] } ]) else: log.warning('(%s) Ignoring invalid autojoin channel %r.', irc.name, chan) utils.add_hook(spawn_service, 'PYLINK_NEW_SERVICE') def handle_disconnect(irc, source, command, args): """Handles network disconnections.""" for name, sbot in world.services.items(): try: del sbot.uids[irc.name] log.debug( "coremods.service_support: removing uids[%s] from service bot %s", irc.name, sbot.name) except KeyError: continue utils.add_hook(handle_disconnect, 'PYLINK_DISCONNECT')
quote.join(irc, "%s" % channel) if channel_row: pass else: ins = dbc.insert().values(id=channel_id, channel=channel, added_by=irc.getHostmask(source), private=options.private, mode=options.mode, invited=0) result = engine.execute(ins).rowcount if result > 0: log.info("Joined %s due to invite." % channel) utils.add_hook(hook_invite, "INVITE") ## # # End Admin Commands # #### # # Maintenance Commands # ## join_parser = utils.IRCParser() join_parser.add_argument("channel", type=str) join_parser.add_argument("-p", "--private", default=0, choices=[0, 1]) join_parser.add_argument("-m", "--mode",
irc.callHooks([ source, 'PYLINK_CUSTOM_WHOIS', { 'target': target, 'server': server } ]) else: log.debug( '(%s) coremods.handlers.handle_whois: skipping custom whois handlers because ' 'caller %s is marked as a bot', irc.name, source) # 318: End of WHOIS. f(318, source, "%s :End of /WHOIS list" % nick) utils.add_hook(handle_whois, 'WHOIS') def handle_mode(irc, source, command, args): """Protect against forced deoper attempts.""" target = args['target'] modes = args['modes'] # If the sender is not a PyLink client, and the target IS a protected # client, revert any forced deoper attempts. if irc.isInternalClient(target) and not irc.isInternalClient(source): if ('-o', None) in modes and (target == irc.pseudoclient.uid or not irc.isManipulatableClient(target)): irc.proto.mode(irc.sid, target, {('+o', None)}) utils.add_hook(handle_mode, 'MODE')
# :charybdis.midnight.vpn 317 jlu5 jlu5 1946 1499867833 :seconds idle, signon time if irc.get_service_bot(target) and conf.conf['pylink'].get('whois_show_startup_time', True): f(317, source, "%s 0 %s :seconds idle (placeholder), signon time" % (nick, irc.start_ts)) # Call custom WHOIS handlers via the PYLINK_CUSTOM_WHOIS hook, unless the # caller is marked a bot and the whois_show_extensions_to_bots option is False if (source_is_bot and conf.conf['pylink'].get('whois_show_extensions_to_bots')) or (not source_is_bot): irc.call_hooks([source, 'PYLINK_CUSTOM_WHOIS', {'target': target, 'server': server}]) else: log.debug('(%s) coremods.handlers.handle_whois: skipping custom whois handlers because ' 'caller %s is marked as a bot', irc.name, source) # 318: End of WHOIS. f(318, source, "%s :End of /WHOIS list" % nick) utils.add_hook(handle_whois, 'WHOIS') def handle_mode(irc, source, command, args): """Protect against forced deoper attempts.""" target = args['target'] modes = args['modes'] # If the sender is not a PyLink client, and the target IS a protected # client, revert any forced deoper attempts. if irc.is_internal_client(target) and not irc.is_internal_client(source): if ('-o', None) in modes and (target == irc.pseudoclient.uid or not irc.is_manipulatable_client(target)): irc.mode(irc.sid, target, {('+o', None)}) utils.add_hook(handle_mode, 'MODE') def handle_operup(irc, source, command, args): """Logs successful oper-ups on networks.""" otype = args.get('text', 'IRC Operator')
# Store the service name in the User object for easier access. userobj.service = name sbot.uids[irc.name] = u = userobj.uid # Special case: if this is the main PyLink client being spawned, # assign this as irc.pseudoclient. if name == 'pylink' and not irc.pseudoclient: log.debug('(%s) spawn_service: irc.pseudoclient set to UID %s', irc.name, u) irc.pseudoclient = userobj # Enumerate & join network defined channels. sbot.join(irc, sbot.get_persistent_channels(irc)) utils.add_hook(spawn_service, 'PYLINK_NEW_SERVICE') def handle_disconnect(irc, source, command, args): """Handles network disconnections.""" for name, sbot in world.services.items(): try: del sbot.uids[irc.name] log.debug("coremods.service_support: removing uids[%s] from service bot %s", irc.name, sbot.name) except KeyError: continue utils.add_hook(handle_disconnect, 'PYLINK_DISCONNECT') def handle_endburst(irc, source, command, args): """Handles network bursts.""" if source == irc.uplink:
reason = irc.get_service_option('sshbl', 'reason', "Your host runs an SSH daemon commonly used by spammer IPs. Consider upgrading your machines " "or contacting network staff for an exemption.") if result: _, port, blacklisted, score = result if not blacklisted: return log.info("sshbl: caught IP %s:%s (%s/%s) with score %s", ip, port, irc.name, args['nick'], score) if args['uid'] in irc.users: irc.kill(irc.pseudoclient.uid, args['uid'], reason) def handle_uid(irc, source, command, args): """Checks incoming connections against SSHBL.""" ip = args['ip'] exemptions = irc.get_service_option('sshbl', 'exempt_hosts', default=None) or [] if not irc.connected.is_set(): # Don't scan users until we've finished bursting. return elif not ipaddress.ip_address(ip).is_global: log.debug("sshbl: skipping scanning local address %s", ip) return elif args['uid'] in irc.users and exemptions: for glob in exemptions: if irc.match_host(glob, args['uid']): log.debug("sshbl: skipping scanning exempt address %s (%s/%s)", ip, irc.name, args['nick']) return pool.submit(_check_connection, irc, args) utils.add_hook(handle_uid, 'UID')
# Ignore if no nicks are affected on the channel. if not nicklist: continue colored_nicks = [color_text(nick) for nick in nicklist] # Join both the nicks and colored_nicks fields into a comma separated string. cargs['nicks'] = ', '.join(nicklist) cargs['colored_nicks'] = ', '.join(colored_nicks) text = text_template.safe_substitute(cargs) # PMs are always sent as notice - this prevents unknown command loops with bots. irc.msg(target, text, loopback=False, notice=private) utils.add_hook(cb_relay_core, 'CLIENTBOT_MESSAGE') utils.add_hook(cb_relay_core, 'CLIENTBOT_KICK') utils.add_hook(cb_relay_core, 'CLIENTBOT_PART') utils.add_hook(cb_relay_core, 'CLIENTBOT_JOIN') utils.add_hook(cb_relay_core, 'CLIENTBOT_QUIT') utils.add_hook(cb_relay_core, 'CLIENTBOT_NICK') utils.add_hook(cb_relay_core, 'CLIENTBOT_SJOIN') utils.add_hook(cb_relay_core, 'CLIENTBOT_SQUIT') @utils.add_cmd def rpm(irc, source, args): """<target> <text> Sends PMs to users over the relay, if Clientbot PMs are enabled. """
if irc.is_oper(user): irc.msg(user, "Warning: %s kills unopered users!" % channel, notice=True, source=asm_uid or irc.pseudoclient.uid) else: log.info('(%s) badchans: punishing user %s (server: %s) for joining channel %s', irc.name, nuh, irc.get_friendly_name(irc.get_server(user)), channel) if use_kline: irc.set_server_ban(asm_uid or irc.sid, kline_duration, host=ip, reason=REASON) else: irc.kill(asm_uid or irc.sid, user, REASON) if ip not in seen_ips: dronebl_key = irc.get_service_option('badchans', 'dronebl_key') if dronebl_key: log.info('(%s) badchans: submitting IP %s (%s) to DroneBL', irc.name, ip, nuh) pool.submit(_submit_dronebl, irc, ip, dronebl_key, nuh) dnsblim_key = irc.get_service_option('badchans', 'dnsblim_key') if dnsblim_key: log.info('(%s) badchans: submitting IP %s (%s) to DNSBL.im', irc.name, ip, nuh) pool.submit(_submit_dnsblim, irc, ip, dnsblim_key, nuh) seen_ips[ip] = time.time() else: log.debug('(%s) badchans: ignoring already submitted IP %s', irc.name, ip) utils.add_hook(handle_join, 'JOIN')
# Note, do NOT use irc.reply() in hook handlers because nothing except the # command handler system actually updates the last caller. irc.msg(source, '\x01%s %s\x01' % (ctcp_command, result), notice=True, source=target) return False # Block this message from reaching the general command handler else: log.info('(%s) Received unknown CTCP %s from %s to %s', irc.name, ctcp_command, irc.get_hostmask(source), irc.get_friendly_name(target)) return False utils.add_hook(handle_ctcp, 'PRIVMSG', priority=200) def handle_ctcpversion(irc, source, ctcp, data): """ Handles CTCP version requests. """ return irc.version() def handle_ctcpeaster(irc, source, ctcp, data): """ Secret easter egg. """ responses = ["Legends say the cord monster was born only %s years ago..." % \
irc.proto.updateClient(target, 'HOST', new_host) # Only operate on the first match. break def handle_uid(irc, sender, command, args): """ Changehost listener for new connections. """ target = args['uid'] _changehost(irc, target, args) utils.add_hook(handle_uid, 'UID') @utils.add_cmd def applyhosts(irc, sender, args): """[<network>] Applies all configured hosts for users on the given network, or the current network if none is specified.""" try: # Try to get network from the command line. network = world.networkobjects[args[0]] except IndexError: # No network was given network = irc except KeyError: # Unknown network irc.reply("Error: Unknown network '%s'." % network) return
Tracks kills against PyLink clients. If too many are received, automatically disconnects from the network. """ if (args['userdata'] and irc.isInternalServer( args['userdata'].server)) or irc.isInternalClient(args['target']): if killcache.setdefault(irc.name, 1) >= length: log.error('(%s) servprotect: Too many kills received, aborting!', irc.name) irc.disconnect() log.debug('(%s) servprotect: Incrementing killcache by 1', irc.name) killcache[irc.name] += 1 utils.add_hook(handle_kill, 'KILL') def handle_save(irc, numeric, command, args): """ Tracks SAVEs (nick collision) against PyLink clients. If too many are received, automatically disconnects from the network. """ if irc.isInternalClient(args['target']): if savecache.setdefault(irc.name, 0) >= length: log.error('(%s) servprotect: Too many nick collisions, aborting!', irc.name) irc.disconnect() log.debug('(%s) servprotect: Incrementing savecache by 1', irc.name) savecache[irc.name] += 1
# We're cramming as much as we can into the class field... _num( 213, "C %s * %s %s [%s:%s:%s]" % (serverdata.get('ip', '0.0.0.0'), netname, serverdata.get('port', 0), serverdata['protocol'], 'ssl' if serverdata.get('ssl') else 'no-ssl', serverdata.get('encoding', 'utf-8'))) elif stats_type == 'o': # 243/RPL_STATSOLINE: "O <hostmask> * <nick> [:<info>]" # New style accounts only! for accountname, accountdata in conf.conf['login'].get('accounts', {}).items(): networks = accountdata.get('networks', []) if irc.name in networks or not networks: hosts = ' '.join(accountdata.get('hosts', ['*@*'])) needoper = 'needoper' if accountdata.get( 'require_oper') else '' _num(243, "O %s * %s :%s" % (hosts, accountname, needoper)) elif stats_type == 'u': # 242/RPL_STATSUPTIME: ":Server Up <days> days <hours>:<minutes>:<seconds>" _num(242, ':Server Up %s' % timediff(world.start_ts, int(time.time()))) else: log.info('(%s) Unknown /STATS type %r requested by %s', irc.name, stats_type, irc.get_hostmask(source)) _num(219, "%s :End of /STATS report" % stats_type) utils.add_hook(handle_stats, 'STATS')
'modes': outgoing_modes, 'parse_as': 'MODE' } ]) def handle_join(irc, source, command, args): """ Automode JOIN listener. This sets modes accordingly if the person joining matches a mask in the ACL. """ channel = irc.toLower(args['channel']) match(irc, channel, args['users']) utils.add_hook(handle_join, 'JOIN') utils.add_hook(handle_join, 'PYLINK_RELAY_JOIN') # Handle the relay version of join utils.add_hook(handle_join, 'PYLINK_SERVICE_JOIN') # And the version for service bots def handle_services_login(irc, source, command, args): """ Handles services login change, to trigger Automode matching. """ for channel in irc.users[source].channels: # Look at all the users' channels for any possible changes. match(irc, channel, [source])
irc.update_client(target, 'HOST', new_host) # Only operate on the first match. break def handle_uid(irc, sender, command, args): """ Changehost listener for new connections. """ target = args['uid'] _changehost(irc, target, args) utils.add_hook(handle_uid, 'UID') def handle_chghost(irc, sender, command, args): """ Handles incoming CHGHOST requests for optional host-change enforcement. """ changehost_conf = conf.conf.get("changehost") if not changehost_conf: return target = args['target'] if (not irc.is_internal_client(sender)) and ( not irc.is_internal_server(sender)): if irc.name in changehost_conf.get('enforced_nets', []):
# Try to look up a prefix specific for this bot in # bot: prefixes: <botname>, falling back to the default prefix if not # specified. prefixes = [irc.botdata.get('prefixes', {}).get(botname) or irc.botdata.get('prefix')] # If responding to nick is enabled, add variations of the current nick # to the prefix list: "<nick>," and "<nick>:" nick = irc.users[servuid].nick if respondtonick: prefixes += [nick+',', nick+':'] if not any(prefixes): # We finished with an empty prefixes list, meaning fantasy is misconfigured! log.warning("(%s) Fantasy prefix for bot %s was not set in configuration - " "fantasy commands will not work!", irc.name, botname) continue for prefix in prefixes: # Cycle through the prefixes list we finished with. if prefix and orig_text.startswith(prefix): # Cut off the length of the prefix from the text. text = orig_text[len(prefix):] # Finally, call the bot command and loop to the next bot. sbot.call_cmd(irc, source, text, called_in=channel) continue utils.add_hook(handle_fantasy, 'PRIVMSG')
result = result.split() result = result[1:] result = (" ").join(result) result = result.strip('\x01') if nowts <= nickts + 15: if nicksid in oursids: myuid = ctcp.uids.get(irc.name) if args['text'].startswith('\x01') and args['text'].endswith( '\x01'): irc.proto.message( myuid, '#debug', "Received VERSION reply from %s, using: %s" % (irc.getFriendlyName(source), result)) utils.add_hook(hook_connversion, 'NOTICE') def hook_uid(irc, source, command, args): try: myuid = ctcp.uids.get(irc.name) oursids = irc.serverdata.get('sids', []) nick = args['nick'] nickuid = args['uid'] nowts = int(time.time()) theirts = int(args['ts']) if source in oursids: irc.proto.message(myuid, nickuid, "\x01VERSION\x01") except LookupError: pass