diff --git a/.gitignore b/.gitignore index 55be276..a43d37f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ __pycache__/ # C extensions *.so +# Config files +*.ini + # Distribution / packaging .Python build/ diff --git a/spamkicker.py b/spamkicker.py new file mode 100755 index 0000000..b4e10b4 --- /dev/null +++ b/spamkicker.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 + +import irc.bot +import irc.strings +import configparser + +from expiringdict import ExpiringDict + +configfile = 'spamkicker.ini' + +class BotConfig: + def __init__(self, config): + general = config['general'] + + connections = [] + for connection in general['connections'].split(): + connections.append((config[connection]['server'], int(config[connection]['port']))) + + self.server_list=connections + self.nickname=general['nick'] + self.realname=general['real'] + self.honeypots=[x.strip() for x in general['honeypots'].split(',')] + self.announce=[x.strip() for x in general['announce'].split(',')] + self.password=general['password'] + self.ban_type=general['ban_type'] + self.ban_duration=general['ban_duration'] + + def __repr__(self): + return "BotConfig(server_list={},nickname='{}',realname='{}',honeypots={},announce={},password='{}',ban_type='{}',ban_duration='{}')".format(self.server_list, self.nickname, self.realname, self.honeypots, self.announce, self.password, self.ban_type, self.ban_duration) + +class SpamKickerBot(irc.bot.SingleServerIRCBot): + def __init__(self, botconfig): + self.botconfig = botconfig + irc.bot.SingleServerIRCBot.__init__(self, botconfig.server_list, botconfig.nickname, botconfig.realname) + self.spammers = ExpiringDict(max_age_seconds=300, max_len=100) + + def on_nicknameinuse(self, c, e): + c.nick(c.get_nickname() + "_") + + def on_welcome(self, c, e): + for channel in self.botconfig.honeypots: + c.join(channel) + for channel in self.botconfig.announce: + c.join(channel) + c.send_raw("OPER {} {}".format(c.get_nickname(), self.botconfig.password)) + + def on_privmsg(self, c, e): + self.flag_spammer(c, e.source) + + def on_pubmsg(self, c, e): + if e.target in self.botconfig.honeypots: + self.flag_spammer(c, e.source) + + + def flag_spammer(self, c, source): + if source in self.spammers: + print("Spammer already flagged") + else: + self.spammers[source] = 1 + print("Spammer flagged: {}".format(source.nick)) + for target in self.botconfig.announce: + c.privmsg(target, 'Spammer identified: {} ({})'.format(source.nick, source.userhost)) + if self.botconfig.ban_type == 'GLINE': + spec = source.userhost + elif self.botconfig.ban_type == 'ZLINE': + spec = source.host + else: + spec = source.userhost # Just in case, right? + command = ' '.join(filter(None, [self.botconfig.ban_type, spec, self.botconfig.ban_duration, ':SpamKicker'])) + print(command) + c.send_raw(command) + +def main(): + config = configparser.ConfigParser() + config.read(configfile) + + botconfig = BotConfig(config) + + bot = SpamKickerBot(botconfig) + bot.start() + +if __name__ == "__main__": + main()