]> jfr.im git - erebus.git/blobdiff - erebus.py
use new bind_bot, make sure the bot-on-channel sends the replies
[erebus.git] / erebus.py
index 42a2f416cefef07c20fdcfc3935431a63934ec9a..61dd1c9b0e4f97642ad93132fe62aafc0538ceed 100644 (file)
--- a/erebus.py
+++ b/erebus.py
@@ -6,7 +6,7 @@
 
 from __future__ import print_function
 
-import os, sys, select, MySQLdb, MySQLdb.cursors, time, random, gc
+import os, sys, select, MySQLdb, MySQLdb.cursors, time, traceback, random, gc
 import bot, config, ctlmod
 
 class Erebus(object): #singleton to pass around
@@ -18,6 +18,7 @@ class Erebus(object): #singleton to pass around
        numhandlers = {}
        msghandlers = {}
        chanhandlers = {}
+       exceptionhandlers = [] # list of (Exception_class, handler_function) tuples
        users = {}
        chans = {}
 
@@ -32,6 +33,9 @@ class Erebus(object): #singleton to pass around
 
                        self.chans = []
 
+               def bind_bot(self, bot):
+                       return main._BoundUser(self, bot)
+
                def msg(self, *args, **kwargs):
                        main.randbot().msg(self, *args, **kwargs)
                def slowmsg(self, *args, **kwargs):
@@ -94,6 +98,22 @@ class Erebus(object): #singleton to pass around
                def __str__(self): return self.nick
                def __repr__(self): return "<User %r (%d)>" % (self.nick, self.glevel)
 
+       class _BoundUser(object):
+               def __init__(self, user, bot):
+                       self.__dict__['_bound_user'] = user
+                       self.__dict__['_bound_bot'] = bot
+               def __getattr__(self, name):
+                       return getattr(self._bound_user, name)
+               def __setattr__(self, name, value):
+                       setattr(self._bound_user, name, value)
+               def msg(self, *args, **kwargs):
+                       self._bound_bot.msg(self._bound_user, *args, **kwargs)
+               def slowmsg(self, *args, **kwargs):
+                       self._bound_bot.slowmsg(self._bound_user, *args, **kwargs)
+               def fastmsg(self, *args, **kwargs):
+                       self._bound_bot.fastmsg(self._bound_user, *args, **kwargs)
+               def __repr__(self): return "<_BoundUser %r %r>" % (self._bound_user, self._bound_bot)
+
        class Channel(object):
                def __init__(self, name, bot):
                        self.name = name
@@ -213,6 +233,12 @@ class Erebus(object): #singleton to pass around
                        self.po.register(fileno, select.POLLIN)
                elif self.potype == "select":
                        self.fdlist.append(fileno)
+       def delfd(self, fileno):
+               del self.fds[fileno]
+               if self.potype == "poll":
+                       self.po.unregister(fileno)
+               elif self.potype == "select":
+                       self.fdlist.remove(fileno)
 
        def bot(self, name): #get Bot() by name (nick)
                return self.bots[name.lower()]
@@ -221,17 +247,17 @@ class Erebus(object): #singleton to pass around
        def randbot(self): #get Bot() randomly
                return self.bots[random.choice(list(self.bots.keys()))]
 
-       def user(self, _nick, justjoined=False, create=True):
+       def user(self, _nick, send_who=False, create=True):
                nick = _nick.lower()
+
+               if send_who and (nick not in self.users or not self.users[nick].isauthed()):
+                       self.randbot().conn.send("WHO %s n%%ant,1" % (nick))
+
                if nick in self.users:
                        return self.users[nick]
                elif create:
                        user = self.User(_nick)
                        self.users[nick] = user
-
-                       if justjoined:
-                               self.randbot().conn.send("WHO %s n%%ant,1" % (nick))
-
                        return user
                else:
                        return None
@@ -315,6 +341,15 @@ class Erebus(object): #singleton to pass around
        def getchanhook(self, chan):
                return self.chanhandlers[chan]
 
+       def hookexception(self, exc, handler):
+               self.exceptionhandlers.append((exc, handler))
+       def unhookexception(self, exc, handler):
+               self.exceptionhandlers.remove((exc, handler))
+       def hasexceptionhook(self, exc):
+               return any((True for x,h in self.exceptionhandlers if isinstance(exc, x)))
+       def getexceptionhook(self, exc):
+               return (h for x,h in self.exceptionhandlers if isinstance(exc, x))
+
 
 def dbsetup():
        main.db = None
@@ -353,8 +388,23 @@ def setup():
 def loop():
        poready = main.poll()
        for fileno in poready:
-               for line in main.fd(fileno).getdata():
-                       main.fd(fileno).parse(line)
+               try:
+                       data = main.fd(fileno).getdata()
+               except:
+                       main.log('*', '!', 'Super-mega-emergency: getdata raised exception for socket %d' % (fileno))
+                       traceback.print_exc()
+                       data = None
+               if data is None:
+                       main.fd(fileno).close()
+               else:
+                       for line in data:
+                               if cfg.getboolean('debug', 'io'):
+                                       main.log(str(main.fd(fileno)), 'I', line)
+                               try:
+                                       main.fd(fileno).parse(line)
+                               except:
+                                       main.log('*', '!', 'Super-mega-emergency: parse raised exception for socket %d data %r' % (fileno, line))
+                                       traceback.print_exc()
        if main.mustquit is not None:
                main.log('*', '!', 'Core exiting due to: %s' % (main.mustquit))
                raise main.mustquit