mirror of
				https://github.com/jlu5/PyLink.git
				synced 2025-11-04 00:47:21 +01:00 
			
		
		
		
	Initial pass of a mass-highlight blocking plugin (#359)
This commit is contained in:
		
							parent
							
								
									4d9fbc55ba
								
							
						
					
					
						commit
						3825b93dee
					
				
							
								
								
									
										141
									
								
								plugins/antispam.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								plugins/antispam.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,141 @@
 | 
			
		||||
# antispam.py: Basic services-side spamfilters for IRC
 | 
			
		||||
 | 
			
		||||
from pylinkirc import utils, world, conf
 | 
			
		||||
from pylinkirc.log import log
 | 
			
		||||
 | 
			
		||||
mydesc = ("Provides anti-spam functionality.")
 | 
			
		||||
sbot = utils.register_service("antispam", default_nick="AntiSpam", desc=mydesc)
 | 
			
		||||
 | 
			
		||||
def die(irc=None):
 | 
			
		||||
    utils.unregister_service("antispam")
 | 
			
		||||
 | 
			
		||||
PUNISH_OPTIONS = ['kill', 'ban', 'quiet', 'kick']
 | 
			
		||||
EXEMPT_OPTIONS = ['voice', 'halfop', 'op']
 | 
			
		||||
DEFAULT_EXEMPT_OPTION = 'halfop'
 | 
			
		||||
def _punish(irc, target, channel, reason):
 | 
			
		||||
    """Punishes the target user."""
 | 
			
		||||
    if irc.is_oper(target, allowAuthed=False):
 | 
			
		||||
        log.debug("(%s) antispam: refusing to punish oper %s/%s", irc.name, target, irc.get_friendly_name(target))
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    exempt_level = irc.get_service_option('antispam', 'exempt_level', DEFAULT_EXEMPT_OPTION).lower()
 | 
			
		||||
    c = irc.channels[channel]
 | 
			
		||||
 | 
			
		||||
    if exempt_level not in EXEMPT_OPTIONS:
 | 
			
		||||
        log.error('(%s) Antispam exempt %r is not a valid setting, '
 | 
			
		||||
                  'falling back to defaults; accepted settings include: %s',
 | 
			
		||||
                  irc.name, exempt_level, ', '.join(EXEMPT_OPTIONS))
 | 
			
		||||
        exempt_level = DEFAULT_EXEMPT_OPTION
 | 
			
		||||
 | 
			
		||||
    if exempt_level == 'voice' and c.is_voice_plus(target):
 | 
			
		||||
        log.debug("(%s) antispam: refusing to punish voiced and above %s/%s", irc.name, target, irc.get_friendly_name(target))
 | 
			
		||||
        return
 | 
			
		||||
    elif exempt_level == 'halfop' and c.is_halfop_plus(target):
 | 
			
		||||
        log.debug("(%s) antispam: refusing to punish halfop and above %s/%s", irc.name, target, irc.get_friendly_name(target))
 | 
			
		||||
        return
 | 
			
		||||
    elif exempt_level == 'op' and c.is_op_plus(target):
 | 
			
		||||
        log.debug("(%s) antispam: refusing to punish op and above %s/%s", irc.name, target, irc.get_friendly_name(target))
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    my_uid = sbot.uids.get(irc.name)
 | 
			
		||||
 | 
			
		||||
    punishment = irc.get_service_option('antispam', 'punishment',
 | 
			
		||||
                                        'ban+kill').lower()
 | 
			
		||||
    bans = set()
 | 
			
		||||
    log.debug('(%s) antispam: got %r as punishment for %s/%s', irc.name, punishment,
 | 
			
		||||
              target, irc.get_friendly_name(target))
 | 
			
		||||
 | 
			
		||||
    def _ban():
 | 
			
		||||
        bans.add(irc.make_channel_ban(target))
 | 
			
		||||
    def _quiet():
 | 
			
		||||
        bans.add(irc.make_channel_ban(target, ban_type='quiet'))
 | 
			
		||||
    def _kick():
 | 
			
		||||
        irc.kick(my_uid, channel, target, reason)
 | 
			
		||||
        irc.call_hooks([my_uid, 'ANTISPAM_KICK', {'channel': channel, 'text': reason, 'target': target,
 | 
			
		||||
                                                  'parse_as': 'KICK'}])
 | 
			
		||||
    def _kill():
 | 
			
		||||
        userdata = irc.users[target]
 | 
			
		||||
        irc.kill(my_uid, target, reason)
 | 
			
		||||
        irc.call_hooks([my_uid, 'ANTISPAM_KILL', {'target': target, 'text': reason,
 | 
			
		||||
                                                  'userdata': userdata, 'parse_as': 'KILL'}])
 | 
			
		||||
 | 
			
		||||
    kill = False
 | 
			
		||||
    for action in set(punishment.split('+')):
 | 
			
		||||
        if action not in PUNISH_OPTIONS:
 | 
			
		||||
            log.error('(%s) Antispam punishment %r is not a valid setting; '
 | 
			
		||||
                      'accepted settings include: %s OR any combination of '
 | 
			
		||||
                      'these joined together with a "+".',
 | 
			
		||||
                      irc.name, punishment, ', '.join(PUNISH_OPTIONS))
 | 
			
		||||
            return
 | 
			
		||||
        elif action == 'kill':
 | 
			
		||||
            kill = True  # Delay kills so that the user data doesn't disappear.
 | 
			
		||||
        elif action == 'kick':
 | 
			
		||||
            _kick()
 | 
			
		||||
        elif action == 'ban':
 | 
			
		||||
            _ban()
 | 
			
		||||
        elif action == 'quiet':
 | 
			
		||||
            _quiet()
 | 
			
		||||
 | 
			
		||||
    if bans:  # Set all bans at once to prevent spam
 | 
			
		||||
        irc.mode(my_uid, channel, bans)
 | 
			
		||||
        irc.call_hooks([my_uid, 'ANTISPAM_BAN',
 | 
			
		||||
                        {'target': channel, 'modes': bans, 'parse_as': 'MODE'}])
 | 
			
		||||
    if kill:
 | 
			
		||||
        _kill()
 | 
			
		||||
 | 
			
		||||
MASSHIGHLIGHT_DEFAULTS = {
 | 
			
		||||
    'min_length': 50,
 | 
			
		||||
    'min_nicks': 5,
 | 
			
		||||
    'reason': "Mass highlight spam is prohibited"
 | 
			
		||||
}
 | 
			
		||||
def handle_masshighlight(irc, source, command, args):
 | 
			
		||||
    """Handles mass highlight attacks."""
 | 
			
		||||
    channel = args['target']
 | 
			
		||||
    text = args['text']
 | 
			
		||||
    mhl_settings = irc.get_service_option('antispam', 'masshighlight',
 | 
			
		||||
                                          MASSHIGHLIGHT_DEFAULTS)
 | 
			
		||||
    my_uid = sbot.uids.get(irc.name)
 | 
			
		||||
 | 
			
		||||
    if (not irc.connected.is_set()) or (not my_uid):
 | 
			
		||||
        # Break if the network isn't ready.
 | 
			
		||||
        log.debug("(%s) antispam: skipping processing; network isn't ready", irc.name)
 | 
			
		||||
        return
 | 
			
		||||
    elif not irc.is_channel(channel):
 | 
			
		||||
        # Not a channel.
 | 
			
		||||
        log.debug("(%s) antispam: skipping processing; %r is not a channel", irc.name, channel)
 | 
			
		||||
        return
 | 
			
		||||
    elif irc.is_internal_client(source):
 | 
			
		||||
        # Ignore messages from our own clients.
 | 
			
		||||
        log.debug("(%s) antispam: skipping processing message from internal client %s", irc.name, source)
 | 
			
		||||
        return
 | 
			
		||||
    elif channel not in irc.channels or my_uid not in irc.channels[channel].users:
 | 
			
		||||
        # We're not monitoring this channel.
 | 
			
		||||
        log.debug("(%s) antispam: skipping processing message from channel %r we're not in", irc.name, channel)
 | 
			
		||||
        return
 | 
			
		||||
    elif len(text) < mhl_settings.get('min_length', MASSHIGHLIGHT_DEFAULTS['min_length']):
 | 
			
		||||
        log.debug("(%s) antispam: skipping processing message %r; it's too short", irc.name, text)
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    # Strip :, from potential nicks
 | 
			
		||||
    words = [word.rstrip(':,') for word in text.split()]
 | 
			
		||||
 | 
			
		||||
    userlist = [irc.users[uid].nick for uid in irc.channels[channel].users.copy()]
 | 
			
		||||
    min_nicks = mhl_settings.get('min_nicks', MASSHIGHLIGHT_DEFAULTS['min_nicks'])
 | 
			
		||||
 | 
			
		||||
    # Don't allow repeating the same nick to trigger punishment
 | 
			
		||||
    nicks_caught = set()
 | 
			
		||||
 | 
			
		||||
    for word in words:
 | 
			
		||||
        if word in userlist:
 | 
			
		||||
            nicks_caught.add(word)
 | 
			
		||||
        if len(nicks_caught) >= min_nicks:
 | 
			
		||||
            reason = mhl_settings.get('reason', MASSHIGHLIGHT_DEFAULTS['reason'])
 | 
			
		||||
            log.debug('(%s) antispam: calling _punish on %s/%s', irc.name,
 | 
			
		||||
                      source, irc.get_friendly_name(source))
 | 
			
		||||
            _punish(irc, source, channel, reason)
 | 
			
		||||
            break
 | 
			
		||||
 | 
			
		||||
    log.debug('(%s) antispam: got %s/%s nicks on message to %r', irc.name, len(nicks_caught), min_nicks, channel)
 | 
			
		||||
 | 
			
		||||
utils.add_hook(handle_masshighlight, 'PRIVMSG')
 | 
			
		||||
utils.add_hook(handle_masshighlight, 'NOTICE')
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user