Prepare hooks to be used for other things than Message

This commit is contained in:
nemunaire 2014-09-11 21:20:56 +02:00
parent 877041bb12
commit 8c52f75b6a
16 changed files with 171 additions and 136 deletions

15
bot.py
View File

@ -31,8 +31,8 @@ __author__ = 'nemunaire'
from consumer import Consumer, EventConsumer, MessageConsumer
from event import ModuleEvent
import hooks
from hooksmanager import HooksManager
from hooks.messagehook import MessageHook
from hooks.manager import HooksManager
from networkbot import NetworkBot
from server.IRC import IRCServer
from server.DCC import DCC
@ -82,7 +82,7 @@ class Bot(threading.Thread):
def in_ping(msg):
if re.match("^ *(m[' ]?entends?[ -]+tu|h?ear me|do you copy|ping)", msg.text, re.I) is not None:
return response.Response(msg.sender, message="pong", channel=msg.receivers, nick=msg.nick)
self.hooks.add_hook(hooks.Hook(in_ping), "in", "PRIVMSG")
self.hooks.add_hook(MessageHook(in_ping), "in", "PRIVMSG")
def _help_msg(msg):
"""Parse and response to help messages"""
@ -119,7 +119,7 @@ class Bot(threading.Thread):
" de tous les modules disponibles localement",
message=["\x03\x02%s\x03\x02 (%s)" % (im, self.modules[im].__doc__) for im in self.modules if self.modules[im].__doc__])
return res
self.hooks.add_hook(hooks.Hook(_help_msg, "help"), "in", "PRIVMSG", "cmd")
self.hooks.add_hook(MessageHook(_help_msg, "help"), "in", "PRIVMSG", "cmd")
# Other known bots, making a bots network
self.network = dict()
@ -479,9 +479,10 @@ def reload():
import hooks
imp.reload(hooks)
import hooksmanager
imp.reload(hooksmanager)
import hooks.manager
imp.reload(hooks.manager)
import hooks.messagehook
imp.reload(hooks.messagehook)
import importer
imp.reload(importer)

106
hooks.py
View File

@ -1,106 +0,0 @@
# -*- coding: utf-8 -*-
# Nemubot is a modulable IRC bot, built around XML configuration files.
# Copyright (C) 2012 Mercier Pierre-Olivier
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
import re
from response import Response
from exception import IRCException
logger = logging.getLogger("nemubot.hooks")
class Hook:
"""Class storing hook informations"""
def __init__(self, call, name=None, data=None, regexp=None, channels=list(), server=None, end=None, call_end=None, help=None):
self.name = name
self.end = end
self.call = call
if call_end is None:
self.call_end = self.call
else:
self.call_end = call_end
self.regexp = regexp
self.data = data
self.times = -1
self.server = server
self.channels = channels
self.help = help
def match(self, message, channel=None, server=None):
if isinstance(message, Response):
return self.is_matching(None, channel, server)
elif message.qual == "cmd":
return self.is_matching(message.cmds[0], channel, server)
elif hasattr(message, "text"):
return self.is_matching(message.text, channel, server)
elif len(message.params) > 0:
return self.is_matching(message.params[0], channel, server)
else:
return self.is_matching(message.cmd, channel, server)
def is_matching(self, strcmp, channel=None, server=None):
"""Test if the current hook correspond to the message"""
return (channel is None or len(self.channels) <= 0 or
channel in self.channels) and (server is None or
self.server is None or self.server == server) and (
(self.name is None or strcmp == self.name) and (
self.end is None or strcmp == self.end) and (
self.regexp is None or re.match(self.regexp, strcmp)))
def run(self, msg, data2=None, strcmp=None):
"""Run the hook"""
if self.times != 0:
self.times -= 1
if (self.end is not None and strcmp is not None and
self.call_end is not None and strcmp == self.end):
call = self.call_end
self.times = 0
else:
call = self.call
try:
if self.data is None:
if data2 is None:
return call(msg)
elif isinstance(data2, dict):
return call(msg, **data2)
else:
return call(msg, data2)
elif isinstance(self.data, dict):
if data2 is None:
return call(msg, **self.data)
else:
return call(msg, data2, **self.data)
else:
if data2 is None:
return call(msg, self.data)
elif isinstance(data2, dict):
return call(msg, self.data, **data2)
else:
return call(msg, self.data, data2)
except IRCException as e:
return e.fill_response(msg)
last_registered = []
def hook(store, *args, **kargs):
def sec(call):
last_registered.append((store, Hook(call, *args, **kargs)))
return call
return sec

76
hooks/__init__.py Normal file
View File

@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
# Nemubot is a smart and modulable IM bot.
# Copyright (C) 2012-2014 nemunaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from exception import IRCException
def call_game(call, *args, **kargs):
"""TODO"""
l = list()
d = kargs
for a in args:
if a is not None:
if isinstance(a, dict):
d.update(a)
else:
l.append(a)
return call(*l, **d)
class AbstractHook:
"""Abstract class for Hook implementation"""
def __init__(self, call, data=None, mtimes=-1, end_call=None):
self.call = call
self.data = data
self.times = mtimes
self.end_call = end_call
def match(self, data1, server):
return NotImplemented
def run(self, data1, *args):
"""Run the hook"""
self.times -= 1
try:
ret = call_game(self.call, data1, self.data, *args)
except IRCException as e:
ret = e.fill_response(data1)
finally:
if self.times == 0:
self.call_end(ret)
return ret
from hooks.messagehook import MessageHook
last_registered = []
def hook(store, *args, **kargs):
"""Function used as a decorator for module loading"""
def sec(call):
last_registered.append((store, MessageHook(call, *args, **kargs)))
return call
return sec

61
hooks/messagehook.py Normal file
View File

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
# Nemubot is a smart and modulable IM bot.
# Copyright (C) 2012-2014 nemunaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
from exception import IRCException
import hooks
from response import Response
class MessageHook(hooks.AbstractHook):
"""Class storing hook information, specialized for a generic Message"""
def __init__(self, call, name=None, data=None, regexp=None,
channels=list(), server=None, mtimes=-1, end_call=None):
hooks.AbstractHook.__init__(self, call=call, data=data,
end_call=end_call, mtimes=mtimes)
self.name = name
self.regexp = regexp
self.server = server
self.channels = channels
def match(self, message, server=None):
if isinstance(message, Response):
return self.is_matching(None, message.channel, server)
elif message.qual == "cmd":
return self.is_matching(message.cmds[0], message.channel, server)
elif hasattr(message, "text"):
return self.is_matching(message.text, message.channel, server)
elif len(message.params) > 0:
return self.is_matching(message.params[0], message.channel, server)
else:
return self.is_matching(message.cmd, message.channel, server)
def is_matching(self, strcmp, channel=None, server=None):
"""Test if the current hook correspond to the message"""
return (channel is None or len(self.channels) <= 0 or
channel in self.channels) and (server is None or
self.server is None or self.server == server) and (
(self.name is None or strcmp == self.name) and (
self.regexp is None or re.match(self.regexp, strcmp)))

View File

@ -34,7 +34,7 @@ def help_full():
return "!conjugaison <tens> <verb>: give the conjugaison for <verb> in <tens>."
@hook("cmd_hook", "conjugaison", help="!conjugaison <tens> <verb>: give the conjugaison for <verb> in <tens>.")
@hook("cmd_hook", "conjugaison")
def cmd_conjug(msg):
if len(msg.cmds) < 3:
raise IRCException("donne moi un temps et un verbe, et je te donnerai sa conjugaison!")

View File

@ -14,7 +14,7 @@ import traceback
nemubotversion = 3.4
from event import ModuleEvent
from hooks import Hook, hook
from hooks import hook
from tools.date import extractDate
from tools.countdown import countdown_format, countdown
@ -60,8 +60,9 @@ def cmd_we(msg):
"Youhou, on est en week-end depuis %s."),
channel=msg.channel)
@hook("cmd_hook", "start", help="!start /something/: launch a timer")
@hook("cmd_hook", "start")
def start_countdown(msg):
"""!start /something/: launch a timer"""
if len(msg.cmds) < 2:
raise IRCException("indique le nom d'un événement à chronométrer")
if msg.cmds[1] in DATAS.index:
@ -154,8 +155,9 @@ def end_countdown(msg):
else:
return Response(msg.sender, "%s n'est pas un compteur connu."% (msg.cmds[1]), channel=msg.channel, nick=msg.nick)
@hook("cmd_hook", "eventslist", help="!eventslist: gets list of timer")
@hook("cmd_hook", "eventslist")
def liste(msg):
"""!eventslist: gets list of timer"""
if len(msg.cmds) > 1:
res = list()
for user in msg.cmds[1:]:

View File

@ -15,8 +15,8 @@ def load(context):
"http://developer.mapquest.com/")
return None
from hooks import Hook
add_hook("cmd_hook", Hook(cmd_geocode, "geocode"))
from hooks.messagehook import MessageHook
add_hook("cmd_hook", MessageHook(cmd_geocode, "geocode"))
def help_tiny ():

View File

@ -9,7 +9,7 @@ import socket
import subprocess
import urllib
from hooks import Hook, hook
from hooks import hook
from tools import web
nemubotversion = 3.4
@ -21,7 +21,8 @@ def load(context):
"<whoisxmlapi username=\"XX\" password=\"XXX\" />\nRegister at "
"http://www.whoisxmlapi.com/newaccount.php")
else:
add_hook("cmd_hook", Hook(cmd_whois, "netwhois"))
from hooks.messagehook import MessageHook
add_hook("cmd_hook", MessageHook(cmd_whois, "netwhois"))
def help_full():
return "!traceurl /url/: Follow redirections from /url/."

View File

@ -15,7 +15,7 @@ def help_full():
LAST_SUBS = dict()
@hook("cmd_hook", "subreddit", help="!subreddit /subreddit/: Display information on the subreddit.")
@hook("cmd_hook", "subreddit")
def cmd_subreddit(msg):
global LAST_SUBS
if len(msg.cmds) <= 1:

View File

@ -12,9 +12,9 @@ from hooks import hook
nemubotversion = 3.4
def help_full():
return "If you would like to sleep soon, use !sleepytime to know the best time to wake up; use !sleepytime hh:mm if you want to wake up at hh:mm"
return "If you would like to sleep soon, use !sleepytime to know the best time to wake up; use !sleepytime hh:mm if you want to wake up at hh:mm"
@hook("cmd_hook", "sleepytime", help="If you would like to sleep soon, use !sleepytime to know the best time to wake up; use !sleepytime hh:mm if you want to wake up at hh:mm")
@hook("cmd_hook", "sleepytime")
def cmd_sleep(msg):
if len (msg.cmds) > 1 and re.match("[0-9]{1,2}[h':.,-]([0-9]{1,2})?[m'\":.,-]?",
msg.cmds[1]) is not None:

View File

@ -15,7 +15,7 @@ nemubotversion = 3.4
def help_full():
return "!syno <word>: give a list of synonyms for <word>."
@hook("cmd_hook", "synonymes", help="!syno <word>: give a list of synonyms for <word>.")
@hook("cmd_hook", "synonymes")
def cmd_syno(msg):
return go("synonymes", msg)

View File

@ -28,8 +28,8 @@ def load(context):
else:
URL = URL % CONF.getNode("wrapi")["key"]
from hooks import Hook
add_hook("cmd_hook", Hook(cmd_translate, "translate"))
from hooks.messagehook import MessageHook
add_hook("cmd_hook", MessageHook(cmd_translate, "translate"))
def help_full():

View File

@ -25,10 +25,10 @@ def load(context):
"http://developer.forecast.io/")
return None
from hooks import Hook
add_hook("cmd_hook", Hook(cmd_weather, "météo"))
add_hook("cmd_hook", Hook(cmd_alert, "alert"))
add_hook("cmd_hook", Hook(cmd_coordinates, "coordinates"))
from hooks.messagehook import MessageHook
add_hook("cmd_hook", MessageHook(cmd_weather, "météo"))
add_hook("cmd_hook", MessageHook(cmd_alert, "alert"))
add_hook("cmd_hook", MessageHook(cmd_coordinates, "coordinates"))
def help_full ():

View File

@ -10,13 +10,11 @@ from urllib.request import urlopen
nemubotversion = 3.4
from hooks import hook
API_URL="http://worldcup.sfg.io/%s"
def load(context):
from hooks import Hook
add_hook("cmd_hook", Hook(cmd_watch, "watch_worldcup"))
add_hook("cmd_hook", Hook(cmd_worldcup, "worldcup"))
from event import ModuleEvent
add_event(ModuleEvent(func=lambda url: urlopen(url, timeout=10).read().decode(), func_data=API_URL % "matches/current?by_date=DESC", call=current_match_new_action, intervalle=30))
@ -37,6 +35,7 @@ def start_watch(msg):
save()
raise IRCException("This channel is now watching world cup events!")
@hook("cmd_hook", "watch_worldcup")
def cmd_watch(msg):
global DATAS
@ -178,6 +177,7 @@ def get_matches(url):
if is_valid(match):
yield match
@hook("cmd_hook", "worldcup")
def cmd_worldcup(msg):
res = Response(msg.sender, channel=msg.channel, nomore="No more match to display", count=" (%d more matches)")
nb = len(msg.cmds)

View File

@ -24,7 +24,7 @@ def gen_response(res, msg, srv):
else:
raise IRCException("mauvaise URL : %s" % srv)
@hook("cmd_hook", "ycc", help="!ycc [<url>]: with an argument, reduce the given <url> thanks to ycc.fr; without argument, reduce the last URL said on the current channel.")
@hook("cmd_hook", "ycc")
def cmd_ycc(msg):
if len(msg.cmds) == 1:
global LAST_URLS