--- /dev/null
+#!/usr/bin/python
+
+# if you brick and have 0 -> kick from chan
+# brick quantity must be queried, not automatically given
+# if A bricks B, A--, B++
+# cant give them nicely
+# new person gets 5 bricks
+
+import irclib
+
+import config
+
+class Bricker(object):
+
+ LISTEN_EVENTS = [
+ 'welcome', 'disconnect', 'nicknameinuse', 'kick', 'privmsg', 'privnotice',
+ 'bannedfromchan', 'erroneusnickname', 'join', 'namreply', 'nick', 'quit',
+ 'part', 'userhost'
+ ]
+
+ OVERBRICKED_MESSAGE = 'Overbricked, haha!'
+ BRICKED_MESSAGE = '*** %s was bricked.'
+ PLEASE_REGISTER_MESSAGE = 'You need to register. Use register <password>.'
+ BAD_PASSWORD_MESSAGE = 'Bad password.'
+ REGISTERED_MESSAGE = 'Registered successfully.'
+ SHARED_CHANNEL_MESSAGE = 'Both you and your target must be in the channel.'
+ COUNT_MESSAGE = 'You have %d bricks remaining.'
+ HELP_MESSAGE = (
+ 'Available commands are:\n'
+ ' register <password> - register yourself, or create a new password\n'
+ ' brick <nick> <channel> - brick someone in a channel\n'
+ ' (we must all be in the channel)\n'
+ ' count - tells you how many bricks you have left\n'
+ ' help - show this text\n')
+
+ def __init__(self, confignetwork, irc):
+ """A brickbot instance, which exists for an irc network..
+
+ Args:
+ confignetwork: A ConfigNetwork object.
+ irc: An irclib.IRC instance.
+ """
+ self.irc = irc
+ self.config = confignetwork
+ self._server_index = 0
+ self._nick = None
+ self._channels = {}
+ self.state = State(self.config.statefile)
+ self.state.load()
+
+ def run(self):
+ self._reconnect()
+
+ def _on_event(self, connection, event):
+ print 'event %s' % event.eventtype()
+
+ def _brick(self, connection, nick, target, channel):
+ if channel in self._channels:
+ fromuser = self.state.user(nick)
+ touser = self.state.user(target)
+ if not fromuser.registered:
+ connection.notice(nick, self.PLEASE_REGISTER_MESSAGE)
+ elif channel not in fromuser.visible or channel not in touser.visible:
+ connection.notice(nick, self.SHARED_CHANNEL_MESSAGE)
+ elif fromuser.bricks < 1:
+ # uh oh ! overbricked !
+ connection.kick(channel, nick, self.OVERBRICKED_MESSAGE)
+ else:
+ connection.privmsg(channel, self.BRICKED_MESSAGE % target)
+ fromuser.bricks -= 1
+ touser.bricks += 1
+ self.state.save()
+
+ def _register(self, connection, nick, password):
+ user = self.state.user(nick)
+ if not password or (user.password and password != user.password):
+ connection.notice(nick, self.BAD_PASSWORD_MESSAGE)
+ return
+ if not user.password:
+ user.password = password
+ self.state.save()
+ user.registered = True
+ user.lasthost = user.onhost
+ self.state.save()
+ connection.notice(nick, self.REGISTERED_MESSAGE)
+
+ def _count(self, connection, nick):
+ user = self.state.user(nick)
+ if not user.registered:
+ connection.notice(nick, self.PLEASE_REGISTER_MESSAGE)
+ else:
+ connection.notice(nick, self.COUNT_MESSAGE % user.bricks)
+
+ def _help(self, connection, nick):
+ for line in self.HELP_MESSAGE.split('\n'):
+ connection.notice(nick, line)
+
+ def _reconnect(self):
+ try:
+ server = self.config.servers[self._server_index]
+ self._nick = self.config.nickname
+ print 'Connecting to %s:%s' % (server.host, server.port)
+ conn = self.irc.server().connect(server.host, server.port, self._nick)
+ for s in self.LISTEN_EVENTS:
+ conn.add_global_handler(s, getattr(self, '_on_%s' % s), -10)
+ conn.add_global_handler('all_events', self._on_event, -30)
+ except irclib.ServerConnectionError:
+ self._server_index = (self._server_index + 1) % len(self.config.servers)
+ net.connection.execute_delayed(net.reconnect_delay,
+ self._reconnect)
+
+ def _rejoin(self, connection, channel):
+ password = ''
+ for c in self.config.channels:
+ if channel == c.name:
+ password = c.password
+ break
+ print 'rejoining %s with password %s' % (channel, password)
+ connection.join(channel, password)
+
+ def _on_welcome(self, connection, event):
+ print 'welcome %s' % event
+ for s in self.config.autocommands:
+ connection.send_raw(s)
+ if self.config.nickservpw:
+ connection.privmsg('nickserv', 'identify %s %s' % (
+ self.config.nickservpw, self.config.nickname))
+ for c in self.config.channels:
+ connection.join(c.name, c.password)
+
+ def _on_disconnect(self, connection, event):
+ print 'disconnect %s' % event
+ connection.execute_delayed(self.config.reconnect_delay,
+ self._reconnect)
+
+ def _on_erroneusnickname(self, connection, event):
+ print 'unable to use nickname'
+
+ def _on_nicknameinuse(self, connection, event):
+ print 'nicknameinuse %s' % event.target()
+ if not self.config.nickservpw:
+ self._nick += '_'
+ print 'using nickname %s' % self._nick
+ connection.nick(self._nick)
+
+ def _on_kick(self, connection, event):
+ nick = event.arguments()[0]
+ channel = event.target()
+ print 'kick %s from %s' % (nick, channel)
+ if nick == self._nick:
+ self._channels[channel] = False
+ connection.execute_delayed(self.config.rejoin_delay, self._rejoin,
+ (connection, event.target()))
+ else:
+ self._rm_nick(connection, channel, nick)
+
+ def _on_bannedfromchan(self, connection, event):
+ print 'bannedfromchan %s' % event.arguments()[0]
+ connection.execute_delayed(self.config.rejoin_delay, self._rejoin,
+ (connection, event.arguments()[0]))
+
+ def _on_privmsg(self, connection, event):
+ print 'privmsg %s' % event.target()
+ print ' %s' % event.source()
+ print ' %s' % event.arguments()
+ data = event.arguments()[0].split(' ')
+ nick = irclib.nm_to_n(event.source())
+ cmd = data[0]
+ args = data[1:]
+ if cmd == 'brick' and len(args) == 2:
+ target = args[0]
+ channel = args[1]
+ self._brick(connection, nick, target, channel)
+ if cmd == 'register' and len(args) == 1:
+ password = args[0]
+ self._register(connection, nick, password)
+ if cmd == 'count':
+ self._count(connection, nick)
+ if cmd == 'help':
+ self._help(connection, nick)
+
+ def _on_privnotice(self, connection, event):
+ print 'privnotice %s' % event.target()
+ print ' %s' % event.source()
+ print ' %s' % event.arguments()
+ data = event.arguments()[0].split(' ')
+ cmd = data[0]
+ args = data[1:]
+
+ def _add_nick(self, connection, channel, nick, host=None):
+ print 'adding nick %s (%s)' % (nick, host)
+ user = self.state.user(nick)
+ user.visible[channel] = True
+ user.onhost = host
+ if host:
+ if host == user.lasthost:
+ user.registered = True
+ print 'registered %s by host %s' % (nick, host)
+ user.ignore_userhost_reply = True
+ else:
+ connection.userhost([nick]) # get their userhost
+ user.ignore_userhost_reply = False
+ self.state.save()
+
+ def _rm_nick(self, connection, channel, nick):
+ print 'removing nick %s' % nick
+ user = self.state.user(nick)
+ del user.visible[channel]
+ if not user.visible:
+ user.registered = False
+ user.onhost = None
+
+ def _on_join(self, connection, event):
+ nick, host = event.source().split('!', 1)
+ channel = event.target()
+ print 'join %s (%s) on %s' % (nick, host, channel)
+ if nick == self._nick:
+ self._channels[channel] = True
+ else:
+ self._add_nick(connection, channel, nick, host=host)
+
+ def _on_namreply(self, connection, event):
+ nicks = event.arguments()[2].split(' ')
+ channel = event.arguments()[1]
+ print 'namreply found nicks %s' % nicks
+ nicks = [n.strip('@+ ') for n in nicks]
+ for nick in nicks:
+ if nick != self._nick:
+ self._add_nick(connection, channel, nick)
+
+ def _on_nick(self, connection, event):
+ tonick = event.target()
+ fromnick = irclib.nm_to_n(event.source())
+ host = irclib.nm_to_uh(event.source())
+ print 'nick %s -> %s' % fromnick, tonick
+ if tonick != self._nick:
+ for c in self._channels:
+ user = self.state.user(fromnick)
+ if c in user.visible:
+ self._rm_nick(connection, c, fromnick)
+ self._add_nick(connection, c, tonick, host=host)
+
+ def _on_userhost(self, connection, event):
+ if not event.arguments()[0]: return
+ nick, host = event.arguments()[0].split('=', 1)
+ nick = nick.strip('* ')
+ host = host.strip('+- ')
+ print 'userhost for %s is %s' % (nick, host)
+ user = self.state.user(nick)
+ if not user.ignore_userhost_reply:
+ for c in user.visible:
+ self._add_nick(connection, c, nick, host=host)
+
+ def _on_quit(self, connection, event):
+ nick = irclib.nm_to_n(event.source())
+ print 'quit %s' % nick
+ if nick == self._nick:
+ self._channels[channel] = False
+ else:
+ user = self.state.user(nick)
+ for c in user.visible:
+ self._rm_nick(connection, c, nick)
+
+ def _on_part(self, connection, event):
+ nick = irclib.nm_to_n(event.source())
+ channel = event.target()
+ print 'part %s from %s' % (nick, channel)
+ if nick == self._nick:
+ self._channels[channel] = False
+ else:
+ self._rm_nick(connection, channel, nick)
+
+
+class State(object):
+
+ def __init__(self, filename):
+ # a dictionary of User objects keyed by their nicknames
+ self._users = {}
+ self._filename = filename
+
+ def load(self):
+ if not self._filename:
+ print 'WARNING: not using a state file'
+ return
+ try:
+ f = open(self._filename, 'r+')
+ except IOError, e:
+ print 'IOError: %s' % e
+ print 'WARNING: not using a state file'
+ return
+
+ curnick = None
+ for line in f:
+ data = line.strip().split(' ', 1)
+ if data[0] == 'nick' and len(data) > 1:
+ curnick = data[1]
+ if curnick and data[0] == 'lasthost' and len(data) > 1:
+ self.user(curnick).lasthost = data[1]
+ if curnick and data[0] == 'bricks' and len(data) > 1:
+ self.user(curnick).bricks = int(data[1])
+ if curnick and data[0] == 'password' and len(data) > 1:
+ self.user(curnick).password = data[1]
+ f.close()
+
+ def save(self):
+ if not self._filename: return
+ try:
+ f = open(self._filename, 'w')
+ except IOError, e:
+ print 'IOError: %s' % e
+ print 'WARNING: failed to write state file'
+ return
+ for u in self._users.values():
+ f.write('nick %s\n' % u.nick)
+ if u.lasthost: f.write('lasthost %s\n' % u.lasthost)
+ if u.password: f.write('password %s\n' % u.password)
+ f.write('bricks %s\n' % u.bricks)
+ f.close()
+
+ def user(self, nick):
+ nick = irclib.irc_lower(nick)
+ if nick not in self._users:
+ user = State.User(self, nick)
+ self._users[nick] = user
+ self.save()
+ else:
+ user = self._users[nick]
+ return user
+
+ class User(object):
+
+ def __init__(self, state, nick):
+ self.state = state
+ self.nick = nick
+ self.onhost = None
+ self.lasthost = None
+ self.registered = False
+ self.visible = {}
+ self.ignore_userhost_reply = True
+ self.bricks = 5
+ self.password = None
+
+
+def main():
+ irc = irclib.IRC()
+ for n in config.Config.networks:
+ b = Bricker(n, irc)
+ b.run()
+ irc.process_forever()
+
+if __name__ == '__main__':
+ main()