DrBotIRC: add regex-matching global handlers

add_global_regex_handler and remove_global_regex_handler are new methods
that work the same as irclib's non-regex versions, but check patterns
before adding/removing. this allows for a more direct link between event
loop and module methods

i'm hoping that one day it will allow for the removal of ancient
shortcuts that just get in the way now, like do() and
on_pub_or_privmsg()

lightly tested, if anything is still brittle it's recursion, no doubt
This commit is contained in:
Brian S. Stephan 2012-12-20 09:58:25 -06:00
parent 53a3c40102
commit 52970894a9
1 changed files with 128 additions and 1 deletions

View File

@ -17,6 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import bisect
import copy
from ConfigParser import NoOptionError, NoSectionError
import logging
@ -213,6 +214,8 @@ class DrBotIRC(irclib.IRC):
self.config = config
self.xmlrpc = None
self.regex_handlers = dict()
# handle SIGINT
signal.signal(signal.SIGINT, self.sigint_handler)
@ -257,8 +260,62 @@ class DrBotIRC(irclib.IRC):
return self.server
def add_global_regex_handler(self, event, regex, handler, priority=0):
"""Adds a global handler function for a specific event type and regex.
The handler function is called whenever the specified event is
triggered in any of the connections and the regex matches.
See documentation for the Event class.
The handler functions are called in priority order (lowest
number is highest priority). If a handler function returns
"NO MORE", no more handlers will be called.
This is basically an extension of add_global_handler(), either may
work, though it turns out most modules probably want this one.
The provided method should take as arguments:
* nick the nick creating the event, or None
* userhost the userhost creating the event, or None
* event the raw IRC event, which may be None
* from_admin whether or not the event came from an admin
* groups list of match.groups(), from re.search()
Args:
event event type (a string), as in numeric_events
regex regex string to match before doing callback invocation
handler callback function to invoke
priority integer, the lower number, the higher priority
"""
if not event in self.regex_handlers:
self.regex_handlers[event] = []
bisect.insort(self.regex_handlers[event], ((priority, regex, handler)))
def remove_global_regex_handler(self, event, regex, handler):
"""Removes a global regex handler function.
Args:
event event type (a string), as in numeric_events
regex regex string that was used in matching
handler callback function to remove
Returns:
1 on success, otherwise 0.
"""
if not event in self.regex_handlers:
return 0
for h in self.regex_handlers[event]:
if regex == h[1] and handler == h[2]:
self.regex_handlers[event].remove(h)
return 1
def _handle_event(self, connection, event):
"""Override event handler to do recursion.
"""Override event handler to do recursion and regex checking.
Args:
connection source connection
@ -287,6 +344,41 @@ class DrBotIRC(irclib.IRC):
self.try_recursion(connection, event)
self.try_alias(connection, event)
nick = None
userhost = None
admin = False
if event.source() is not None:
nick = irclib.nm_to_n(event.source())
try:
userhost = irclib.nm_to_uh(event.source())
except IndexError: pass
try:
if userhost == self.config.get('dr.botzo', 'admin_userhost'):
admin = True
except NoOptionError: pass
# try regex handlers first, since they're more specific
rh = self.regex_handlers
trh = sorted(rh.get('all_events', []) + rh.get(event.eventtype(), []))
for handler in trh:
try:
prio, regex, method = handler
for line in event.arguments():
match = re.search(regex, line)
if match:
log.debug("pattern matched, calling "
"{0:s}".format(method))
# pattern matched, call method with pattern groups
ret = method(nick, userhost, event, admin,
match.groups())
if ret == 'NO MORE':
return
except Exception as e:
log.error("exception floated up to DrBotIrc!")
log.exception(e)
h = self.handlers
th = sorted(h.get('all_events', []) + h.get(event.eventtype(), []))
for handler in th:
@ -335,6 +427,41 @@ class DrBotIRC(irclib.IRC):
replies = []
nick = None
userhost = None
admin = False
if event.source() is not None:
nick = irclib.nm_to_n(event.source())
try:
userhost = irclib.nm_to_uh(event.source())
except IndexError: pass
try:
if userhost == self.config.get('dr.botzo', 'admin_userhost'):
admin = True
except NoOptionError: pass
# try regex handlers first, since they're more specific
rh = self.regex_handlers
trh = sorted(rh.get('all_events', []) + rh.get(event.eventtype(), []))
for handler in trh:
try:
prio, regex, method = handler
for line in event.arguments():
match = re.search(regex, line)
if match:
log.debug("pattern matched, calling "
"{0:s}".format(method))
# pattern matched, call method with pattern groups
ret = method(nick, userhost, event, admin,
match.groups())
if ret:
replies.append(ret)
except Exception as e:
log.error("exception floated up to DrBotIrc!")
log.exception(e)
h = self.handlers
th = sorted(h.get('all_events', []) + h.get(event.eventtype(), []))
for handler in th: