Introducing new hooks manager

Currently, the manager use a naive implementation, this is
	mainly for architectural testing purpose.
This commit is contained in:
nemunaire 2014-09-01 19:21:54 +02:00
parent 29819c7874
commit 3bc53bb4ef
14 changed files with 343 additions and 438 deletions

219
bot.py
View File

@ -32,6 +32,7 @@ __author__ = 'nemunaire'
from consumer import Consumer, EventConsumer, MessageConsumer from consumer import Consumer, EventConsumer, MessageConsumer
from event import ModuleEvent from event import ModuleEvent
import hooks import hooks
from hooksmanager import HooksManager
from networkbot import NetworkBot from networkbot import NetworkBot
from server.IRC import IRCServer from server.IRC import IRCServer
from server.DCC import DCC from server.DCC import DCC
@ -77,7 +78,11 @@ class Bot(threading.Thread):
self.event_timer = None self.event_timer = None
# Own hooks # Own hooks
self.hooks = hooks.MessagesHook(self, self) self.hooks = HooksManager()
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")
# Other known bots, making a bots network # Other known bots, making a bots network
self.network = dict() self.network = dict()
@ -88,10 +93,6 @@ class Bot(threading.Thread):
self.cnsr_thrd = list() self.cnsr_thrd = list()
self.cnsr_thrd_size = -1 self.cnsr_thrd_size = -1
self.hooks.add_hook("irc_hook",
hooks.Hook(self.treat_prvmsg, "PRIVMSG"),
self)
def run(self): def run(self):
from server import _rlist, _wlist, _xlist from server import _rlist, _wlist, _xlist
@ -299,9 +300,6 @@ class Bot(threading.Thread):
def add_server(self, node, nick, owner, realname): def add_server(self, node, nick, owner, realname):
"""Add a new server to the context""" """Add a new server to the context"""
srv = IRCServer(node, nick, owner, realname) srv = IRCServer(node, nick, owner, realname)
srv.add_hook = lambda h: self.hooks.add_hook("irc_hook", h, self)
srv.add_networkbot = self.add_networkbot
srv.send_bot = lambda d: self.send_networkbot(srv, d)
#srv.register_hooks() #srv.register_hooks()
if srv.id not in self.servers: if srv.id not in self.servers:
self.servers[srv.id] = srv self.servers[srv.id] = srv
@ -348,7 +346,7 @@ class Bot(threading.Thread):
self.modules[name].unload(self) self.modules[name].unload(self)
# Remove registered hooks # Remove registered hooks
for (s, h) in self.modules[name].REGISTERED_HOOKS: for (s, h) in self.modules[name].REGISTERED_HOOKS:
self.hooks.del_hook(s, h) self.hooks.del_hook(h, s)
# Remove registered events # Remove registered events
for e in self.modules[name].REGISTERED_EVENTS: for e in self.modules[name].REGISTERED_EVENTS:
self.del_event(e) self.del_event(e)
@ -361,7 +359,7 @@ class Bot(threading.Thread):
def receive_message(self, srv, raw_msg, private=False, data=None): def receive_message(self, srv, raw_msg, private=False, data=None):
"""Queued the message for treatment""" """Queued the message for treatment"""
#print (raw_msg) #print("READ", raw_msg)
self.cnsr_queue.put_nowait(MessageConsumer(srv, raw_msg, datetime.now(), private, data)) self.cnsr_queue.put_nowait(MessageConsumer(srv, raw_msg, datetime.now(), private, data))
# Launch a new thread if necessary # Launch a new thread if necessary
@ -399,57 +397,6 @@ class Bot(threading.Thread):
self.stop = True self.stop = True
# Hooks cache
def create_cache(self, name):
if name not in self.hooks_cache:
if isinstance(self.hooks.__dict__[name], list):
self.hooks_cache[name] = list()
# Start by adding locals hooks
for h in self.hooks.__dict__[name]:
tpl = (h, 0, self.hooks.__dict__[name], self.hooks.bot)
self.hooks_cache[name].append(tpl)
# Now, add extermal hooks
level = 0
while level == 0 or lvl_exist:
lvl_exist = False
for ext in self.network:
if len(self.network[ext].hooks) > level:
lvl_exist = True
for h in self.network[ext].hooks[level].__dict__[name]:
if h not in self.hooks_cache[name]:
self.hooks_cache[name].append((h, level + 1,
self.network[ext].hooks[level].__dict__[name], self.network[ext].hooks[level].bot))
level += 1
elif isinstance(self.hooks.__dict__[name], dict):
self.hooks_cache[name] = dict()
# Start by adding locals hooks
for h in self.hooks.__dict__[name]:
self.hooks_cache[name][h] = (self.hooks.__dict__[name][h], 0,
self.hooks.__dict__[name],
self.hooks.bot)
# Now, add extermal hooks
level = 0
while level == 0 or lvl_exist:
lvl_exist = False
for ext in self.network:
if len(self.network[ext].hooks) > level:
lvl_exist = True
for h in self.network[ext].hooks[level].__dict__[name]:
if h not in self.hooks_cache[name]:
self.hooks_cache[name][h] = (self.network[ext].hooks[level].__dict__[name][h], level + 1, self.network[ext].hooks[level].__dict__[name], self.network[ext].hooks[level].bot)
level += 1
else:
raise Exception(name + " hook type unrecognized")
return self.hooks_cache[name]
# Treatment # Treatment
def check_rest_times(self, store, hook): def check_rest_times(self, store, hook):
@ -462,48 +409,6 @@ class Bot(threading.Thread):
elif isinstance(store, list): elif isinstance(store, list):
store.remove(hook) store.remove(hook)
def treat_pre(self, msg, srv):
"""Treat a message before all other treatment"""
# Treat all messages starting with 'nemubot:' as distinct commands
if msg.cmd == "PRIVMSG" and msg.text.find("%s:"%srv.nick) == 0:
# Remove the bot name
msg.text = msg.text[len(srv.nick)+1:].strip()
msg.parse_content()
msg.private = True
for h, lvl, store, bot in self.create_cache("all_pre"):
if h.is_matching(None, server=srv):
h.run(msg, self.create_cache)
self.check_rest_times(store, h)
def treat_post(self, res):
"""Treat a message before send"""
for h, lvl, store, bot in self.create_cache("all_post"):
if h.is_matching(None, channel=res.channel, server=res.server):
c = h.run(res)
self.check_rest_times(store, h)
if not c:
return False
return True
def treat_irc(self, msg, srv):
"""Treat all incoming IRC commands"""
treated = list()
irc_hooks = self.create_cache("irc_hook")
if msg.cmd in irc_hooks:
(hks, lvl, store, bot) = irc_hooks[msg.cmd]
for h in hks:
if h.is_matching(msg.cmd, server=srv):
res = h.run(msg, srv, msg.cmd)
if res is not None and res != False:
treated.append(res)
self.check_rest_times(store, h)
return treated
def treat_prvmsg_ask(self, msg, srv): def treat_prvmsg_ask(self, msg, srv):
# Treat ping # Treat ping
@ -559,114 +464,6 @@ class Bot(threading.Thread):
return res return res
def treat_cmd(self, msg, srv):
"""Treat a command message"""
treated = list()
# First, treat simple hook
cmd_hook = self.create_cache("cmd_hook")
if msg.cmds[0] in cmd_hook:
(hks, lvl, store, bot) = cmd_hook[msg.cmds[0]]
for h in hks:
if h.is_matching(msg.cmds[0], channel=msg.receivers, server=srv) and (msg.private or lvl == 0 or bot.nick not in srv.channels[msg.receivers].people):
res = h.run(msg, strcmp=msg.cmds[0])
if res is not None and res != False:
treated.append(res)
self.check_rest_times(store, h)
# Then, treat regexp based hook
cmd_rgxp = self.create_cache("cmd_rgxp")
for hook, lvl, store, bot in cmd_rgxp:
if hook.is_matching(msg.cmds[0], msg.receivers, server=srv) and (msg.private or lvl == 0 or bot.nick not in srv.channels[msg.receivers].people):
res = hook.run(msg)
if res is not None and res != False:
treated.append(res)
self.check_rest_times(store, hook)
# Finally, treat default hooks if not catched before
cmd_default = self.create_cache("cmd_default")
for hook, lvl, store, bot in cmd_default:
if treated:
break
res = hook.run(msg)
if res is not None and res != False:
treated.append(res)
self.check_rest_times(store, hook)
return treated
def treat_ask(self, msg, srv):
"""Treat an ask message"""
treated = list()
# First, treat simple hook
ask_hook = self.create_cache("ask_hook")
if msg.text in ask_hook:
hks, lvl, store, bot = ask_hook[msg.text]
for h in hks:
if h.is_matching(msg.text, channel=msg.receivers, server=srv) and (msg.private or lvl == 0 or bot.nick not in srv.channels[msg.receivers].people):
res = h.run(msg, strcmp=msg.text)
if res is not None and res != False:
treated.append(res)
self.check_rest_times(store, h)
# Then, treat regexp based hook
ask_rgxp = self.create_cache("ask_rgxp")
for hook, lvl, store, bot in ask_rgxp:
if hook.is_matching(msg.text, channel=msg.receivers, server=srv) and (msg.private or lvl == 0 or bot.nick not in srv.channels[msg.receivers].people):
res = hook.run(msg, strcmp=msg.text)
if res is not None and res != False:
treated.append(res)
self.check_rest_times(store, hook)
# Finally, treat default hooks if not catched before
ask_default = self.create_cache("ask_default")
for hook, lvl, store, bot in ask_default:
if treated:
break
res = hook.run(msg)
if res is not None and res != False:
treated.append(res)
self.check_rest_times(store, hook)
return treated
def treat_answer(self, msg, srv):
"""Treat a normal message"""
treated = list()
# First, treat simple hook
msg_hook = self.create_cache("msg_hook")
if msg.text in msg_hook:
hks, lvl, store, bot = msg_hook[msg.text]
for h in hks:
if h.is_matching(msg.text, channel=msg.receivers, server=srv) and (msg.private or lvl == 0 or bot.nick not in srv.channels[msg.receivers].people):
res = h.run(msg, strcmp=msg.text)
if res is not None and res != False:
treated.append(res)
self.check_rest_times(store, h)
# Then, treat regexp based hook
msg_rgxp = self.create_cache("msg_rgxp")
for hook, lvl, store, bot in msg_rgxp:
if hook.is_matching(msg.text, channel=msg.receivers, server=srv) and (msg.private or lvl == 0 or bot.nick not in srv.channels[msg.receivers].people):
res = hook.run(msg, strcmp=msg.text)
if res is not None and res != False:
treated.append(res)
self.check_rest_times(store, hook)
# Finally, treat default hooks if not catched before
msg_default = self.create_cache("msg_default")
for hook, lvl, store, bot in msg_default:
if len(treated) > 0:
break
res = hook.run(msg)
if res is not None and res != False:
treated.append(res)
self.check_rest_times(store, hook)
return treated
def _ctcp_response(sndr, msg): def _ctcp_response(sndr, msg):
return response.Response(sndr, msg, ctcp=True) return response.Response(sndr, msg, ctcp=True)

View File

@ -42,26 +42,151 @@ class MessageConsumer:
self.prvt = prvt self.prvt = prvt
self.data = data self.data = data
self.msgs = list()
self.responses = None
def treat_in(self, context, msg):
"""Treat the input message"""
if msg.cmd == "PING":
self.srv.write("%s :%s" % ("PONG", msg.params[0]))
elif hasattr(msg, "receivers"):
if msg.receivers:
# All messages
context.treat_pre(msg, self.srv)
return context.treat_irc(msg, self.srv) def first_treat(self, msg):
"""Qualify a new message/response
def treat_out(self, context, res): Argument:
"""Treat the output message""" msg -- The Message or Response to qualify
if isinstance(res, list): """
for r in res:
if r is not None: self.treat_out(context, r)
elif isinstance(res, response.Response): if not hasattr(msg, "qual") or msg.qual is None:
# Define the destination server # Assume this is a message with no particulariry
msg.qual = "def"
# Define the source server if not already done
if not hasattr(msg, "server") or msg.server is None:
msg.server = self.srv.id
if isinstance(msg, Message):
if msg.cmd == "PRIVMSG" or msg.cmd == "NOTICE":
msg.is_owner = (msg.nick == self.srv.owner)
msg.private = msg.private or (len(msg.receivers) == 1 and msg.receivers[0] == self.srv.nick)
if msg.private:
msg.qual = "ask"
# Remove nemubot:
if msg.qual != "cmd" and msg.text.find(self.srv.nick) == 0 and len(msg.text) > len(self.srv.nick) + 2 and msg.text[len(self.srv.nick)] == ":":
msg.text = msg.text[len(self.srv.nick) + 1:].strip()
msg.qual = "ask"
return msg
def pre_treat(self, hm):
"""Modify input Messages
Arguments:
hm -- Hooks manager
"""
new_msg = list()
new_msg += self.msgs
self.msgs = list()
while len(new_msg) > 0:
msg = new_msg.pop(0)
for h in hm.get_hooks("pre", msg.cmd, msg.qual):
if h.match(message=msg, server=self.srv):
res = h.run(msg)
if isinstance(res, list):
for i in xrange(len(res)):
if res[i] == msg:
res.pop(i)
break
new_msg += res
elif res is not None and res != msg:
new_msg.append(res)
msg = None
break
elif res is None or res == False:
msg = None
break
if msg is not None:
self.msgs.append(msg)
def in_treat(self, hm):
"""Treat Messages and store responses
Arguments:
hm -- Hooks manager
"""
self.responses = list()
for msg in self.msgs:
for h in hm.get_hooks("in", msg.cmd, msg.qual):
if msg.cmd == "PING":
self.srv.write("%s :%s" % ("PONG", msg.params[0]))
elif h.match(message=msg, server=self.srv):
res = h.run(msg)
if isinstance(res, list):
self.responses += res
elif res is not None:
self.responses.append(res)
def post_treat(self, hm):
"""Modify output Messages
Arguments:
hm -- Hooks manager
"""
new_msg = list()
new_msg += self.responses
self.responses = list()
while len(new_msg) > 0:
msg = self.first_treat(new_msg.pop(0))
for h in hm.get_hooks("post"):
if h.match(message=msg, server=self.srv):
res = h.run(msg)
if isinstance(res, list):
for i in xrange(len(res)):
if res[i] == msg:
res.pop(i)
break
new_msg += res
elif res is not None and res != msg:
new_msg.append(res)
msg = None
break
elif res is None or res == False:
msg = None
break
if msg is not None:
self.responses.append(msg)
def run(self, context):
"""Create, parse and treat the message"""
try:
# Parse and create the original message
msg = Message(self.raw, self.time, self.prvt)
self.first_treat(msg)
self.msgs.append(msg)
# Run pre-treatment: from Message to [ Message ]
self.pre_treat(context.hooks)
# Run in-treatment: from Message to [ Response ]
if len(self.msgs) > 0:
self.in_treat(context.hooks)
# Run post-treatment: from Response to [ Response ]
if self.responses is not None and len(self.responses) > 0:
self.post_treat(context.hooks)
except:
logger.exception("Error occurred during the processing of the message: %s", self.raw)
return
#return self.responses
for res in self.responses:
to_server = None to_server = None
if res.server is None: if res.server is None:
to_server = self.srv to_server = self.srv
@ -75,39 +200,7 @@ class MessageConsumer:
return False return False
# Sent the message only if treat_post authorize it # Sent the message only if treat_post authorize it
if context.treat_post(res): to_server.send_response(res)
if type(res.channel) != list:
res.channel = [ res.channel ]
for channel in res.channel:
if channel is not None and channel != to_server.nick:
to_server.write("%s %s :%s" % ("PRIVMSG", channel, res.get_message()))
else:
channel = res.sender.split("!")[0]
to_server.write("%s %s :%s" % ("NOTICE" if res.is_ctcp else "PRIVMSG", channel, res.get_message()))
elif res is not None:
logger.error("Unrecognized response type: %s", res)
def run(self, context):
"""Create, parse and treat the message"""
try:
msg = Message(self.raw, self.time, self.prvt)
msg.server = self.srv.id
if msg.cmd == "PRIVMSG":
msg.is_owner = (msg.nick == self.srv.owner)
msg.private = msg.private or (len(msg.receivers) == 1 and msg.receivers[0] == self.srv.nick)
res = self.treat_in(context, msg)
except:
logger.exception("Error occurred during the processing of the message: %s", self.raw)
return
# Send message
self.treat_out(context, res)
# Inform that the message has been treated
#self.srv.msg_treated(self.data)
class EventConsumer: class EventConsumer:
"""Store a event before treating""" """Store a event before treating"""

157
hooks.py
View File

@ -24,151 +24,6 @@ from exception import IRCException
logger = logging.getLogger("nemubot.hooks") logger = logging.getLogger("nemubot.hooks")
class MessagesHook:
def __init__(self, context, bot):
self.context = context
self.bot = bot
# Store specials hooks
self.all_pre = list() # Treated before any parse
self.all_post = list() # Treated before send message to user
# Store IRC commands hooks
self.irc_hook = dict()
# Store direct hooks
self.cmd_hook = dict()
self.ask_hook = dict()
self.msg_hook = dict()
# Store regexp hooks
self.cmd_rgxp = list()
self.ask_rgxp = list()
self.msg_rgxp = list()
# Store default hooks (after other hooks if no match)
self.cmd_default = list()
self.ask_default = list()
self.msg_default = list()
def add_hook(self, store, hook, module_src=None):
"""Insert in the right place a hook into the given store"""
logger.info("Adding hook '%s' to store '%s' from module '%s'", hook, store, module_src)
if module_src is None:
logger.warn("No source module was passed to add_hook function, "
"please fix it in order to be compatible with unload "
"feature")
if store in self.context.hooks_cache:
logger.debug("Cleaning hooks cache for %s", store)
del self.context.hooks_cache[store]
if not hasattr(self, store):
# TODO: raise custom exception, this is a user problem, not internal one!
logger.error("Unrecognized hook store: %s", store)
return
attr = getattr(self, store)
if isinstance(attr, dict) and hook.name is not None:
if hook.name not in attr:
attr[hook.name] = list()
attr[hook.name].append(hook)
if hook.end is not None:
if hook.end not in attr:
attr[hook.end] = list()
attr[hook.end].append(hook)
elif isinstance(attr, list):
attr.append(hook)
else:
logger.critical("Unrecognized hook store type: %s", type(attr))
return
if module_src is not None and hasattr(module_src, "REGISTERED_HOOKS"):
module_src.REGISTERED_HOOKS.append((store, hook))
def register_hook_attributes(self, store, module, node):
if node.hasAttribute("data"):
data = node["data"]
else:
data = None
if node.hasAttribute("name"):
self.add_hook(store + "_hook", Hook(getattr(module, node["call"]),
node["name"], data=data),
module)
elif node.hasAttribute("regexp"):
self.add_hook(store + "_rgxp", Hook(getattr(module, node["call"]),
regexp=node["regexp"], data=data),
module)
def register_hook(self, module, node):
"""Create a hook from configuration node"""
if node.name == "message" and node.hasAttribute("type"):
if node["type"] == "cmd" or node["type"] == "all":
self.register_hook_attributes("cmd", module, node)
if node["type"] == "ask" or node["type"] == "all":
self.register_hook_attributes("ask", module, node)
if (node["type"] == "msg" or node["type"] == "answer" or
node["type"] == "all"):
self.register_hook_attributes("answer", module, node)
def clear(self):
for h in self.all_pre:
self.del_hook("all_pre", h)
for h in self.all_post:
self.del_hook("all_post", h)
for l in self.irc_hook:
for h in self.irc_hook[l]:
self.del_hook("irc_hook", h)
for l in self.cmd_hook:
for h in self.cmd_hook[l]:
self.del_hook("cmd_hook", h)
for l in self.ask_hook:
for h in self.ask_hook[l]:
self.del_hook("ask_hook", h)
for l in self.msg_hook:
for h in self.msg_hook[l]:
self.del_hook("msg_hook", h)
for h in self.cmd_rgxp:
self.del_hook("cmd_rgxp", h)
for h in self.ask_rgxp:
self.del_hook("ask_rgxp", h)
for h in self.msg_rgxp:
self.del_hook("msg_rgxp", h)
for h in self.cmd_default:
self.del_hook("cmd_default", h)
for h in self.ask_default:
self.del_hook("ask_default", h)
for h in self.msg_default:
self.del_hook("msg_default", h)
def del_hook(self, store, hook, module_src=None):
"""Remove a registered hook from a given store"""
if store in self.context.hooks_cache:
del self.context.hooks_cache[store]
if not hasattr(self, store):
logger.warn("unrecognized hook store type")
return
attr = getattr(self, store)
if isinstance(attr, dict) and hook.name is not None:
if hook.name in attr:
attr[hook.name].remove(hook)
if hook.end is not None and hook.end in attr:
attr[hook.end].remove(hook)
else:
attr.remove(hook)
if module_src is not None:
module_src.REGISTERED_HOOKS.remove((store, hook))
class Hook: class Hook:
"""Class storing hook informations""" """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): def __init__(self, call, name=None, data=None, regexp=None, channels=list(), server=None, end=None, call_end=None, help=None):
@ -186,6 +41,18 @@ class Hook:
self.channels = channels self.channels = channels
self.help = help 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): def is_matching(self, strcmp, channel=None, server=None):
"""Test if the current hook correspond to the message""" """Test if the current hook correspond to the message"""
return (channel is None or len(self.channels) <= 0 or return (channel is None or len(self.channels) <= 0 or

107
hooksmanager.py Normal file
View File

@ -0,0 +1,107 @@
# -*- 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/>.
class HooksManager:
"""Class to manage hooks"""
def __init__(self):
"""Initialize the manager"""
self.hooks = dict()
def add_hook(self, hook, *triggers):
"""Add a hook to the manager
Argument:
hook -- a Hook instance
triggers -- string that trigger the hook
"""
trigger = "_".join(triggers)
if trigger not in self.hooks:
self.hooks[trigger] = list()
#print("ADD hook: %s => %s" % (trigger, hook))
self.hooks[trigger].append(hook)
def del_hook(self, hook=None, *triggers):
"""Remove the given hook from the manager
Return:
Boolean value reporting the deletion success
Argument:
triggers -- trigger string to remove
Keyword argument:
hook -- a Hook instance to remove from the trigger string
"""
trigger = "_".join(triggers)
if trigger in self.hooks:
if hook is None:
del self.hooks[trigger]
return True
else:
return self.hooks[trigger].remove(hook)
return False
def get_hooks(self, *triggers):
"""Returns list of trigger hooks that match the given trigger string
Argument:
triggers -- the trigger string
Keyword argument:
data -- Data to pass to the hook as argument
"""
trigger = "_".join(triggers)
res = list()
for key in self.hooks:
if trigger.find(key) == 0:
res += self.hooks[key]
#print("GET hooks: %s => %d" % (trigger, len(res)))
return res
def exec_hook(self, *triggers, **data):
"""Trigger hooks that match the given trigger string
Argument:
trigger -- the trigger string
Keyword argument:
data -- Data to pass to the hook as argument
"""
trigger = "_".join(triggers)
for key in self.hooks:
if trigger.find(key) == 0:
for hook in self.hooks[key]:
hook.run(**data)

View File

@ -159,15 +159,19 @@ class ModuleLoader(SourceLoader):
def send_response(server, res): def send_response(server, res):
if server in self.context.servers: if server in self.context.servers:
return self.context.servers[server].send_response(res, None) return self.context.servers[server].send_response(res)
else: else:
module.logger.error("Try to send a message to the unknown server: %s", server) module.logger.error("Try to send a message to the unknown server: %s", server)
return False return False
def add_hook(store, hook): def add_hook(store, hook):
return self.context.hooks.add_hook(store, hook, module) store = convert_legacy_store(store)
module.REGISTERED_HOOKS.append((store, hook))
return self.context.hooks.add_hook(hook, store)
def del_hook(store, hook): def del_hook(store, hook):
return self.context.hooks.del_hook(store, hook) store = convert_legacy_store(store)
module.REGISTERED_HOOKS.remove((store, hook))
return self.context.hooks.del_hook(hook, store)
def add_event(evt, eid=None): def add_event(evt, eid=None):
return self.context.add_event(evt, eid, module_src=module) return self.context.add_event(evt, eid, module_src=module)
def add_event_eid(evt, eid): def add_event_eid(evt, eid):
@ -236,6 +240,21 @@ class ModuleLoader(SourceLoader):
return module return module
def convert_legacy_store(old):
if old == "cmd_hook" or old == "cmd_rgxp" or old == "cmd_default":
return "in_PRIVMSG_cmd"
elif old == "ask_hook" or old == "ask_rgxp" or old == "ask_default":
return "in_PRIVMSG_ask"
elif old == "msg_hook" or old == "msg_rgxp" or old == "msg_default":
return "in_PRIVMSG_def"
elif old == "all_post":
return "post"
elif old == "all_pre":
return "pre"
else:
print("UNKNOWN store:", old)
return old
def add_cap_hook(prompt, module, cmd): def add_cap_hook(prompt, module, cmd):
if hasattr(module, cmd["call"]): if hasattr(module, cmd["call"]):
prompt.add_cap_hook(cmd["name"], getattr(module, cmd["call"])) prompt.add_cap_hook(cmd["name"], getattr(module, cmd["call"]))
@ -250,5 +269,7 @@ def register_hooks(module, context, prompt):
if s == "prompt_cmd": if s == "prompt_cmd":
prompt.add_cap_hook(h.name, h.call) prompt.add_cap_hook(h.name, h.call)
else: else:
context.hooks.add_hook(s, h, module) s = convert_legacy_store(s)
module.REGISTERED_HOOKS.append((s, h))
context.hooks.add_hook(h, s)
hooks.last_registered = [] hooks.last_registered = []

View File

@ -68,7 +68,7 @@ class Message:
self.params.append(p.group("trailing")) self.params.append(p.group("trailing"))
# Special commands # Special commands
if self.cmd == 'PRIVMSG': if self.cmd == 'PRIVMSG' or self.cmd == 'NOTICE':
self.receivers = self.decode(self.params[0]).split(',') self.receivers = self.decode(self.params[0]).split(',')
# If CTCP, remove 0x01 # If CTCP, remove 0x01
@ -106,6 +106,11 @@ class Message:
def parse_content(self): def parse_content(self):
"""Parse or reparse the message content""" """Parse or reparse the message content"""
# Remove !
if self.text[0] == '!':
self.qual = "cmd"
self.text = self.text[1:].strip()
# Split content by words # Split content by words
try: try:
self.cmds = shlex.split(self.text) self.cmds = shlex.split(self.text)

View File

@ -148,28 +148,22 @@ def treat_variables(res):
res.messages[i] = replace_variables(", ".join(res.messages[i]), res) res.messages[i] = replace_variables(", ".join(res.messages[i]), res)
else: else:
res.messages[i] = replace_variables(res.messages[i], res) res.messages[i] = replace_variables(res.messages[i], res)
return True return res
@hook("all_pre") @hook("pre_PRIVMSG_cmd")
def treat_alias(msg, hooks_cache): def treat_alias(msg):
if msg.cmd == "PRIVMSG": if msg.cmds[0] in DATAS.getNode("aliases").index:
if len(msg.cmds) > 0 and (len(msg.cmds[0]) > 0 msg.text = msg.text.replace(msg.cmds[0],
and msg.cmds[0][0] == "!" DATAS.getNode("aliases").index[msg.cmds[0]]["origin"], 1)
and msg.cmds[0][1:] in DATAS.getNode("aliases").index msg.qual = "def"
and msg.cmds[0][1:] not in hooks_cache("cmd_hook")): msg.parse_content()
msg.text = msg.text.replace(msg.cmds[0],
DATAS.getNode("aliases").index[msg.cmds[0][1:]]["origin"], 1)
msg.parse_content() return treat_alias(msg)
treat_alias(msg, hooks_cache) else:
return True msg.text = replace_variables(msg.text, msg)
msg.parse_content()
else: return msg
msg.text = replace_variables(msg.text, msg)
msg.parse_content()
return False
return False
@hook("ask_default") @hook("ask_default")
def parseask(msg): def parseask(msg):
@ -187,4 +181,4 @@ def parseask(msg):
res = Response(msg.sender, "Nouvel alias %s défini avec succès." % result.group(1)) res = Response(msg.sender, "Nouvel alias %s défini avec succès." % result.group(1))
save() save()
return res return res
return False return None

View File

@ -33,7 +33,7 @@ def parseresponse(res):
SERVERS[res.server] = dict() SERVERS[res.server] = dict()
for receiver in res.receivers: for receiver in res.receivers:
SERVERS[res.server][receiver] = res SERVERS[res.server][receiver] = res
return True return res
@hook("cmd_hook", "more") @hook("cmd_hook", "more")

View File

@ -67,9 +67,8 @@ def parselisten(msg):
except: except:
pass pass
return False return msg
@hook("all_post") @hook("all_post")
def parseresponse(res): def parseresponse(res):
parselisten(res) return parselisten(res)
return True

View File

@ -66,9 +66,8 @@ def parselisten(msg):
LAST_URLS[msg.channel].append(url) LAST_URLS[msg.channel].append(url)
except: except:
pass pass
return False return msg
@hook("all_post") @hook("all_post")
def parseresponse(res): def parseresponse(res):
parselisten(res) return parselisten(res)
return True

View File

@ -69,6 +69,14 @@ class Response:
self.sender = sender self.sender = sender
def append_message(self, message, title=None, shown_first_count=-1): def append_message(self, message, title=None, shown_first_count=-1):
if type(message) is str:
message = message.split('\n')
if len(message) > 1:
for m in message:
self.append_message(m)
return
else:
message = message[0]
if message is not None and len(message) > 0: if message is not None and len(message) > 0:
if shown_first_count >= 0: if shown_first_count >= 0:
self.messages.append(message[:shown_first_count]) self.messages.append(message[:shown_first_count])

View File

@ -31,7 +31,7 @@ import server
#Store all used ports #Store all used ports
PORTS = list() PORTS = list()
class DCC(server.Server): class DCC(server.AbstractServer):
def __init__(self, srv, dest, socket=None): def __init__(self, srv, dest, socket=None):
server.Server.__init__(self) server.Server.__init__(self)

View File

@ -42,6 +42,17 @@ class IRCServer(SocketServer):
return True return True
return False return False
def send_response(self, res):
if type(res.channel) != list:
res.channel = [ res.channel ]
for channel in res.channel:
if channel is not None and channel != self.nick:
self.write("%s %s :%s" % ("PRIVMSG", channel, res.get_message()))
else:
channel = res.sender.split("!")[0]
self.write("%s %s :%s" % ("NOTICE" if res.is_ctcp else "PRIVMSG", channel, res.get_message()))
def _close(self): def _close(self):
self.write("QUIT") self.write("QUIT")
SocketServer._close(self) SocketServer._close(self)

View File

@ -63,6 +63,10 @@ class AbstractServer(io.IOBase):
_xlist.remove(self) _xlist.remove(self)
def send_response(self, res):
return NotImplemented
def write(self, message): def write(self, message):
"""Send a message to the server using send_callback""" """Send a message to the server using send_callback"""
self._send_callback(message) self._send_callback(message)