Add new hook that treat all messages, not only PRIVMSG ; can respond with a response.Hook to create a hook

This commit is contained in:
Némunaire 2012-11-04 04:28:24 +01:00
parent 21f2af1cad
commit dc52593953
10 changed files with 365 additions and 243 deletions

11
DCC.py
View File

@ -35,7 +35,6 @@ class DCC(server.Server):
def __init__(self, srv, dest, socket=None): def __init__(self, srv, dest, socket=None):
server.Server.__init__(self) server.Server.__init__(self)
self.DCC = False
self.error = False # An error has occur, closing the connection? self.error = False # An error has occur, closing the connection?
self.messages = list() # Message queued before connexion self.messages = list() # Message queued before connexion
@ -132,9 +131,10 @@ class DCC(server.Server):
if self.error: if self.error:
self.srv.send_msg_final(self.nick, msg) self.srv.send_msg_final(self.nick, msg)
elif not self.connected or self.s is None: elif not self.connected or self.s is None:
if not self.DCC: try:
self.DCC = True
self.start() self.start()
except RuntimeError:
pass
self.messages.append(msg) self.messages.append(msg)
else: else:
for line in msg.split("\n"): for line in msg.split("\n"):
@ -146,9 +146,10 @@ class DCC(server.Server):
"""Send a file over DCC""" """Send a file over DCC"""
if os.path.isfile(filename): if os.path.isfile(filename):
self.messages = filename self.messages = filename
if not self.DCC: try:
self.start() self.start()
self.DCC = True except RuntimeError:
pass
else: else:
print("File not found `%s'" % filename) print("File not found `%s'" % filename)

View File

@ -22,8 +22,9 @@ import socket
import threading import threading
import traceback import traceback
import channel from channel import Channel
from DCC import DCC from DCC import DCC
from hooks import Hook
import message import message
import server import server
import xmlparser import xmlparser
@ -56,7 +57,7 @@ class IRCServer(server.Server):
self.channels = dict() self.channels = dict()
for chn in self.node.getNodes("channel"): for chn in self.node.getNodes("channel"):
chan = channel.Channel(chn, self) chan = Channel(chn["name"], chn["password"])
self.channels[chan.name] = chan self.channels[chan.name] = chan
@ -104,6 +105,23 @@ class IRCServer(server.Server):
"""Gives the server identifiant""" """Gives the server identifiant"""
return self.host + ":" + str(self.port) return self.host + ":" + str(self.port)
def register_hooks(self):
self.add_hook(Hook(self.evt_channel, "JOIN"))
self.add_hook(Hook(self.evt_channel, "PART"))
self.add_hook(Hook(self.evt_server, "NICK"))
self.add_hook(Hook(self.evt_server, "QUIT"))
self.add_hook(Hook(self.evt_channel, "332"))
self.add_hook(Hook(self.evt_channel, "353"))
def evt_server(self, msg, srv):
for chan in self.channels:
self.channels[chan].treat(msg.cmd, msg)
def evt_channel(self, msg, srv):
if msg.channel is not None:
if msg.channel in self.channels:
self.channels[msg.channel].treat(msg.cmd, msg)
def accepted_channel(self, chan, sender = None): def accepted_channel(self, chan, sender = None):
"""Return True if the channel (or the user) is authorized""" """Return True if the channel (or the user) is authorized"""
if self.allow_all: if self.allow_all:
@ -120,11 +138,7 @@ class IRCServer(server.Server):
"""Join a channel""" """Join a channel"""
if force or (chan is not None and if force or (chan is not None and
self.connected and chan not in self.channels): self.connected and chan not in self.channels):
chn = xmlparser.module_state.ModuleState("channel") self.channels[chan] = Channel(chan, password)
chn["name"] = chan
chn["password"] = password
self.node.addChild(chn)
self.channels[chan] = channel.Channel(chn, self)
if password is not None: if password is not None:
self.s.send(("JOIN %s %s\r\n" % (chan, password)).encode()) self.s.send(("JOIN %s %s\r\n" % (chan, password)).encode())
else: else:
@ -136,7 +150,7 @@ class IRCServer(server.Server):
def leave(self, chan): def leave(self, chan):
"""Leave a channel""" """Leave a channel"""
if chan is not None and self.connected and chan in self.channels: if chan is not None and self.connected and chan in self.channels:
if chan.instanceof(list): if isinstance(chan, list):
for c in chan: for c in chan:
self.leave(c) self.leave(c)
else: else:

165
bot.py
View File

@ -21,6 +21,7 @@ from datetime import timedelta
from queue import Queue from queue import Queue
import threading import threading
import time import time
import re
import consumer import consumer
import event import event
@ -67,6 +68,10 @@ class Bot:
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 init_ctcp_capabilities(self): def init_ctcp_capabilities(self):
"""Reset existing CTCP capabilities to default one""" """Reset existing CTCP capabilities to default one"""
@ -98,7 +103,7 @@ class Bot:
print ("DCC: unable to connect to %s:%s" % (ip, msg.cmds[4])) print ("DCC: unable to connect to %s:%s" % (ip, msg.cmds[4]))
def add_event(self, evt, eid=None): def add_event(self, evt, eid=None, module_src=None):
"""Register an event and return its identifiant for futur update""" """Register an event and return its identifiant for futur update"""
if eid is None: if eid is None:
# Find an ID # Find an ID
@ -123,18 +128,26 @@ class Bot:
if len(self.events) <= 0 or self.events[i+1] != evt: if len(self.events) <= 0 or self.events[i+1] != evt:
return None return None
if module_src is not None:
module_src.REGISTERED_EVENTS.append(evt.id)
return evt.id return evt.id
def del_event(self, id): def del_event(self, id, module_src=None):
"""Find and remove an event from list""" """Find and remove an event from list"""
if len(self.events) > 0 and id == self.events[0].id: if len(self.events) > 0 and id == self.events[0].id:
self.events.remove(self.events[0]) self.events.remove(self.events[0])
self.update_timer() self.update_timer()
if module_src is not None:
module_src.REGISTERED_EVENTS.remove(evt.id)
return True return True
for evt in self.events: for evt in self.events:
if evt.id == id: if evt.id == id:
self.events.remove(evt) self.events.remove(evt)
if module_src is not None:
module_src.REGISTERED_EVENTS.remove(evt.id)
return True return True
return False return False
@ -172,6 +185,8 @@ class Bot:
def addServer(self, node, nick, owner, realname): def addServer(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.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
if srv.autoconnect: if srv.autoconnect:
@ -326,24 +341,111 @@ class Bot:
elif isinstance(store, list): elif isinstance(store, list):
store.remove(hook) store.remove(hook)
def treat_pre(self, msg): def treat_pre(self, msg, srv):
"""Treat a message before all other treatment""" """Treat a message before all other treatment"""
for h, lvl, store in self.create_cache("all_pre"): for h, lvl, store in self.create_cache("all_pre"):
h.run(self.create_cache, msg) if h.is_matching(None, server=srv):
self.check_rest_times(store, h) h.run(msg, self.create_cache)
self.check_rest_times(store, h)
def treat_post(self, msg): def treat_post(self, res):
"""Treat a message before send""" """Treat a message before send"""
for h, lvl, store in self.create_cache("all_post"): for h, lvl, store in self.create_cache("all_post"):
c = h.run(msg) if h.is_matching(None, channel=res.channel, server=res.server):
self.check_rest_times(store, h) c = h.run(msg)
if not c: self.check_rest_times(store, h)
return False if not c:
return False
return True return True
def treat_cmd(self, msg): 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) = 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):
# Treat ping
if re.match("^ *(m[' ]?entends?[ -]+tu|h?ear me|do you copy|ping)",
msg.content, re.I) is not None:
return response.Response(msg.sender, message="pong",
channel=msg.channel, nick=msg.nick)
# Ask hooks
else:
return self.treat_ask(msg, srv)
def treat_prvmsg(self, msg, srv):
# First, treat CTCP
if msg.ctcp:
if msg.cmds[0] in self.ctcp_capabilities:
return self.ctcp_capabilities[msg.cmds[0]](srv, msg)
else:
return _ctcp_response(msg.sender, "ERRMSG Unknown or unimplemented CTCP request")
# Treat all messages starting with 'nemubot:' as distinct commands
elif msg.content.find("%s:"%srv.nick) == 0:
# Remove the bot name
msg.content = msg.content[len(srv.nick)+1:].strip()
return self.treat_prvmsg_ask(msg, srv)
# Owner commands
elif msg.content[0] == '`' and msg.nick == srv.owner:
#TODO: owner commands
pass
elif msg.content[0] == '!' and len(msg.content) > 1:
# Remove the !
msg.cmds[0] = msg.cmds[0][1:]
if msg.cmds[0] == "help":
return _help_msg(msg.sender)
elif msg.cmds[0] == "more":
if msg.channel == srv.nick:
if msg.sender in srv.moremessages:
return srv.moremessages[msg.sender]
else:
if msg.channel in srv.moremessages:
return srv.moremessages[msg.channel]
elif msg.cmds[0] == "dcc":
print("dcctest for", msg.sender)
srv.send_dcc("Hello %s!" % msg.nick, msg.sender)
elif msg.cmds[0] == "pvdcctest":
print("dcctest")
return Response(msg.sender, message="Test DCC")
elif msg.cmds[0] == "dccsendtest":
print("dccsendtest")
conn = DCC(srv, msg.sender)
conn.send_file("bot_sample.xml")
else:
return self.treat_cmd(msg, srv)
else:
res = self.treat_answer(msg, srv)
# Assume the message starts with nemubot:
if (res is None or len(res) <= 0) and msg.private:
return self.treat_prvmsg_ask(msg, srv)
return res
def treat_cmd(self, msg, srv):
"""Treat a command message""" """Treat a command message"""
treated = list() treated = list()
@ -352,15 +454,16 @@ class Bot:
if msg.cmds[0] in cmd_hook: if msg.cmds[0] in cmd_hook:
(hks, lvl, store) = cmd_hook[msg.cmds[0]] (hks, lvl, store) = cmd_hook[msg.cmds[0]]
for h in hks: for h in hks:
res = h.run(msg) if h.is_matching(msg.cmds[0], channel=msg.channel, server=srv):
if res is not None and res != False: res = h.run(msg, strcmp=msg.cmds[0])
treated.append(res) if res is not None and res != False:
self.check_rest_times(store, h) treated.append(res)
self.check_rest_times(store, h)
# Then, treat regexp based hook # Then, treat regexp based hook
cmd_rgxp = self.create_cache("cmd_rgxp") cmd_rgxp = self.create_cache("cmd_rgxp")
for hook, lvl, store in cmd_rgxp: for hook, lvl, store in cmd_rgxp:
if hook.is_matching(msg.cmds[0], msg.channel): if hook.is_matching(msg.cmds[0], msg.channel, server=srv):
res = hook.run(msg) res = hook.run(msg)
if res is not None and res != False: if res is not None and res != False:
treated.append(res) treated.append(res)
@ -378,7 +481,7 @@ class Bot:
return treated return treated
def treat_ask(self, msg): def treat_ask(self, msg, srv):
"""Treat an ask message""" """Treat an ask message"""
treated = list() treated = list()
@ -387,16 +490,17 @@ class Bot:
if msg.content in ask_hook: if msg.content in ask_hook:
hks, lvl, store = ask_hook[msg.content] hks, lvl, store = ask_hook[msg.content]
for h in hks: for h in hks:
res = h.run(msg) if h.is_matching(msg.content, channel=msg.channel, server=srv):
if res is not None and res != False: res = h.run(msg, strcmp=msg.content)
treated.append(res) if res is not None and res != False:
self.check_rest_times(store, h) treated.append(res)
self.check_rest_times(store, h)
# Then, treat regexp based hook # Then, treat regexp based hook
ask_rgxp = self.create_cache("ask_rgxp") ask_rgxp = self.create_cache("ask_rgxp")
for hook, lvl, store in ask_rgxp: for hook, lvl, store in ask_rgxp:
if hook.is_matching(msg.content, msg.channel): if hook.is_matching(msg.content, channel=msg.channel, server=srv):
res = hook.run(msg) res = hook.run(msg, strcmp=msg.content)
if res is not None and res != False: if res is not None and res != False:
treated.append(res) treated.append(res)
self.check_rest_times(store, hook) self.check_rest_times(store, hook)
@ -413,7 +517,7 @@ class Bot:
return treated return treated
def treat_answer(self, msg): def treat_answer(self, msg, srv):
"""Treat a normal message""" """Treat a normal message"""
treated = list() treated = list()
@ -422,16 +526,17 @@ class Bot:
if msg.content in msg_hook: if msg.content in msg_hook:
hks, lvl, store = msg_hook[msg.content] hks, lvl, store = msg_hook[msg.content]
for h in hks: for h in hks:
res = h.run(msg) if h.is_matching(msg.content, channel=msg.channel, server=srv):
if res is not None and res != False: res = h.run(msg, strcmp=msg.content)
treated.append(res) if res is not None and res != False:
self.check_rest_times(store, h) treated.append(res)
self.check_rest_times(store, h)
# Then, treat regexp based hook # Then, treat regexp based hook
msg_rgxp = self.create_cache("msg_rgxp") msg_rgxp = self.create_cache("msg_rgxp")
for hook, lvl, store in msg_rgxp: for hook, lvl, store in msg_rgxp:
if hook.is_matching(msg.content, msg.channel): if hook.is_matching(msg.content, channel=msg.channel, server=srv):
res = hook.run(msg) res = hook.run(msg, strcmp=msg.content)
if res is not None and res != False: if res is not None and res != False:
treated.append(res) treated.append(res)
self.check_rest_times(store, hook) self.check_rest_times(store, hook)

View File

@ -1,71 +1,102 @@
# coding=utf-8 # 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/>.
class Channel: class Channel:
def __init__(self, node, srv): def __init__(self, name, password=None):
self.node = node self.name = name
self.name = node["name"] self.password = password
self.password = node["password"] self.people = dict()
self.people = dict() self.topic = ""
self.srv = srv
self.topic = ""
def join(self, nick, level = 0): def treat(self, cmd, msg):
#print ("%s arrive sur %s" % (nick, self.name)) if cmd == "353":
self.people[nick] = level self.parse353(msg)
elif cmd == "332":
self.parse332(msg)
elif cmd == "MODE":
self.mode(msg)
elif cmd == "JOIN":
self.join(msg.nick)
elif cmd == "NICK":
self.nick(msg.nick, msg.content)
elif cmd == "PART" or cmd == "QUIT":
self.part(msg.nick)
elif cmd == "TOPIC":
self.topic = self.content
def chtopic(self, newtopic): def join(self, nick, level = 0):
"""Send command to change the topic""" """Someone join the channel"""
self.srv.send_msg(self.name, newtopic, "TOPIC") #print ("%s arrive sur %s" % (nick, self.name))
self.topic = newtopic self.people[nick] = level
def nick(self, oldnick, newnick): def chtopic(self, newtopic):
#print ("%s change de nom pour %s" % (oldnick, newnick)) """Send command to change the topic"""
if oldnick in self.people: self.srv.send_msg(self.name, newtopic, "TOPIC")
lvl = self.people[oldnick] self.topic = newtopic
del self.people[oldnick]
else:
lvl = 0
self.people[newnick] = lvl
def part(self, nick): def nick(self, oldnick, newnick):
#print ("%s vient de quitter %s" % (self.sender, self.channel)) """Someone change his nick"""
if nick in self.people: if oldnick in self.people:
del self.people[nick] #print ("%s change de nom pour %s sur %s" % (oldnick, newnick, self.name))
lvl = self.people[oldnick]
del self.people[oldnick]
self.people[newnick] = lvl
def mode(self, msg): def part(self, nick):
if msg.content[0] == "-k": """Someone leave the channel"""
self.password = "" if nick in self.people:
elif msg.content[0] == "+k": #print ("%s vient de quitter %s" % (nick, self.name))
if len(msg.content) > 1: del self.people[nick]
self.password = ' '.join(msg.content[1:])[1:]
else:
self.password = msg.content[1]
elif msg.content[0] == "+o":
self.people[msg.nick] |= 4
elif msg.content[0] == "-o":
self.people[msg.nick] &= ~4
elif msg.content[0] == "+h":
self.people[msg.nick] |= 2
elif msg.content[0] == "-h":
self.people[msg.nick] &= ~2
elif msg.content[0] == "+v":
self.people[msg.nick] |= 1
elif msg.content[0] == "-v":
self.people[msg.nick] &= ~1
def parse332(self, msg): def mode(self, msg):
self.topic = msg.content if msg.content[0] == "-k":
self.password = ""
elif msg.content[0] == "+k":
if len(msg.content) > 1:
self.password = ' '.join(msg.content[1:])[1:]
else:
self.password = msg.content[1]
elif msg.content[0] == "+o":
self.people[msg.nick] |= 4
elif msg.content[0] == "-o":
self.people[msg.nick] &= ~4
elif msg.content[0] == "+h":
self.people[msg.nick] |= 2
elif msg.content[0] == "-h":
self.people[msg.nick] &= ~2
elif msg.content[0] == "+v":
self.people[msg.nick] |= 1
elif msg.content[0] == "-v":
self.people[msg.nick] &= ~1
def parse353(self, msg): def parse332(self, msg):
for p in msg.content: self.topic = msg.content
p = p.decode()
if p[0] == "@": def parse353(self, msg):
level = 4 for p in msg.content:
elif p[0] == "%": p = p.decode()
level = 2 if p[0] == "@":
elif p[0] == "+": level = 4
level = 1 elif p[0] == "%":
else: level = 2
self.people[p] = 0 elif p[0] == "+":
continue level = 1
self.people[p[1:]] = level else:
self.join(p, 0)
continue
self.join(p[1:], level)

View File

@ -25,7 +25,7 @@ import sys
import bot import bot
from DCC import DCC from DCC import DCC
from message import Message from message import Message
from response import Response import response
import server import server
class MessageConsumer: class MessageConsumer:
@ -43,104 +43,48 @@ class MessageConsumer:
if msg.cmd == "PING": if msg.cmd == "PING":
self.srv.send_pong(msg.content) self.srv.send_pong(msg.content)
else: else:
# TODO: Manage credits here # All messages
context.treat_pre(msg) context.treat_pre(msg, self.srv)
if msg.cmd == "PRIVMSG" and msg.ctcp:
if msg.cmds[0] in context.ctcp_capabilities:
return context.ctcp_capabilities[msg.cmds[0]](self.srv, msg)
else:
return bot._ctcp_response(msg.sender, "ERRMSG Unknown or unimplemented CTCP request")
elif msg.cmd == "PRIVMSG" and self.srv.accepted_channel(msg.channel):
return self.treat_prvmsg(context, msg)
# TODO: continue here
pass
def treat_prvmsg_ask(self, context, msg):
# Treat ping
if re.match("^ *(m[' ]?entends?[ -]+tu|h?ear me|do you copy|ping)",
msg.content, re.I) is not None:
return Response(msg.sender, message="pong",
channel=msg.channel, nick=msg.nick)
# Ask hooks
else:
return context.treat_ask(msg)
def treat_prvmsg(self, context, msg):
# Treat all messages starting with 'nemubot:' as distinct commands
if msg.content.find("%s:"%self.srv.nick) == 0:
# Remove the bot name
msg.content = msg.content[len(self.srv.nick)+1:].strip()
return self.treat_prvmsg_ask(context, msg)
# Owner commands
elif msg.content[0] == '`' and msg.nick == self.srv.owner:
#TODO: owner commands
pass
elif msg.content[0] == '!' and len(msg.content) > 1:
# Remove the !
msg.cmds[0] = msg.cmds[0][1:]
if msg.cmds[0] == "help":
return _help_msg(msg.sender)
elif msg.cmds[0] == "more":
if msg.channel == self.srv.nick:
if msg.sender in self.srv.moremessages:
return self.srv.moremessages[msg.sender]
else:
if msg.channel in self.srv.moremessages:
return self.srv.moremessages[msg.channel]
elif msg.cmds[0] == "dcc":
print("dcctest for", msg.sender)
self.srv.send_dcc("Hello %s!" % msg.nick, msg.sender)
elif msg.cmds[0] == "pvdcctest":
print("dcctest")
return Response(msg.sender, message="Test DCC")
elif msg.cmds[0] == "dccsendtest":
print("dccsendtest")
conn = DCC(self.srv, msg.sender)
conn.send_file("bot_sample.xml")
else:
return context.treat_cmd(msg)
else:
res = context.treat_answer(msg)
# Assume the message starts with nemubot:
if (res is None or len(res) <= 0) and self.prvt:
return self.treat_prvmsg_ask(context, msg)
return res
# TODO: Manage credits
return context.treat_irc(msg, self.srv)
def treat_out(self, context, res): def treat_out(self, context, res):
"""Treat the output message""" """Treat the output message"""
if isinstance(res, list):
for r in res:
if r is not None: self.treat_out(context, r)
elif isinstance(res, response.Response):
# Define the destination server # Define the destination server
if (res.server is not None and if (res.server is not None and
res.server.instanceof(string) and res.server in context.servers): isinstance(res.server, str) and res.server in context.servers):
res.server = context.servers[res.server] res.server = context.servers[res.server]
if (res.server is not None and if (res.server is not None and
not res.server.instanceof(server.Server)): not isinstance(res.server, server.Server)):
print ("\033[1;35mWarning:\033[0m the server defined in this " print ("\033[1;35mWarning:\033[0m the server defined in this "
"response doesn't exist: %s" % (res.server)) "response doesn't exist: %s" % (res.server))
res.server = None res.server = None
if res.server is None: if res.server is None:
res.server = self.srv res.server = self.srv
# Sent the message only if treat_post authorize it # Sent the message only if treat_post authorize it
if context.treat_post(res): if context.treat_post(res):
res.server.send_response(res, self.data) res.server.send_response(res, self.data)
elif isinstance(res, response.Hook):
context.hooks.add_hook(res.type, res.hook, res.src)
elif res is not None:
print ("\033[1;35mWarning:\033[0m unrecognized response type "
": %s" % res)
def run(self, context): def run(self, context):
"""Create, parse and treat the message""" """Create, parse and treat the message"""
try: try:
msg = Message(self.raw, self.time, self.prvt) msg = Message(self.raw, self.time, self.prvt)
msg.is_owner = (msg.nick == self.srv.owner) if msg.cmd == "PRIVMSG":
msg.is_owner = (msg.nick == self.srv.owner)
res = self.treat_in(context, msg) res = self.treat_in(context, msg)
except: except:
print ("\033[1;31mERROR:\033[0m occurred during the " print ("\033[1;31mERROR:\033[0m occurred during the "
@ -151,17 +95,7 @@ class MessageConsumer:
return return
# Send message # Send message
if res is not None: self.treat_out(context, res)
if isinstance(res, list):
for r in res:
if isinstance(r, Response):
self.treat_out(context, r)
elif isinstance(r, list):
for s in r:
if isinstance(s, Response):
self.treat_out(context, s)
elif isinstance(res, Response):
self.treat_out(context, res)
# Inform that the message has been treated # Inform that the message has been treated
self.srv.msg_treated(self.data) self.srv.msg_treated(self.data)
@ -210,7 +144,7 @@ class Consumer(threading.Thread):
def _help_msg(modules, sndr, cmd): def _help_msg(modules, sndr, cmd):
"""Parse and response to help messages""" """Parse and response to help messages"""
res = Response(sndr) res = response.Response(sndr)
if len(cmd) > 1: if len(cmd) > 1:
if cmd[1] in modules: if cmd[1] in modules:
if len(cmd) > 2: if len(cmd) > 2:

View File

@ -22,16 +22,19 @@ class MessagesHook:
def __init__(self, context): def __init__(self, context):
self.context = context self.context = context
# Store specials hook # Store specials hooks
self.all_pre = list() # Treated before any parse self.all_pre = list() # Treated before any parse
self.all_post = list() # Treated before send message to user self.all_post = list() # Treated before send message to user
# Store direct hook # Store IRC commands hooks
self.irc_hook = dict()
# Store direct hooks
self.cmd_hook = dict() self.cmd_hook = dict()
self.ask_hook = dict() self.ask_hook = dict()
self.msg_hook = dict() self.msg_hook = dict()
# Store regexp hook # Store regexp hooks
self.cmd_rgxp = list() self.cmd_rgxp = list()
self.ask_rgxp = list() self.ask_rgxp = list()
self.msg_rgxp = list() self.msg_rgxp = list()
@ -61,12 +64,16 @@ class MessagesHook:
if hook.name not in attr: if hook.name not in attr:
attr[hook.name] = list() attr[hook.name] = list()
attr[hook.name].append(hook) 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): elif isinstance(attr, list):
attr.append(hook) attr.append(hook)
else: else:
print ("\033[1;32mWarning:\033[0m unrecognized hook store type") print ("\033[1;32mWarning:\033[0m unrecognized hook store type")
return return
if module_src is not None: if module_src is not None and hasattr(module_src, "REGISTERED_HOOKS"):
module_src.REGISTERED_HOOKS.append((store, hook)) module_src.REGISTERED_HOOKS.append((store, hook))
def register_hook_attributes(self, store, module, node): def register_hook_attributes(self, store, module, node):
@ -92,7 +99,7 @@ class MessagesHook:
node["type"] == "all"): node["type"] == "all"):
self.register_hook_attributes("answer", module, node) self.register_hook_attributes("answer", module, node)
def del_hook(self, store, hook): def del_hook(self, store, hook, module_src=None):
"""Remove a registered hook from a given store""" """Remove a registered hook from a given store"""
if store in self.context.hooks_cache: if store in self.context.hooks_cache:
del self.context.hooks_cache[store] del self.context.hooks_cache[store]
@ -105,33 +112,68 @@ class MessagesHook:
if isinstance(attr, dict) and hook.name is not None: if isinstance(attr, dict) and hook.name is not None:
if hook.name in attr: if hook.name in attr:
attr[hook.name].remove(hook) attr[hook.name].remove(hook)
if hook.end is not None and hook.end in attr:
attr[hook.end].remove(hook)
else: else:
attr.remove(hook) 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()): def __init__(self, call, name=None, data=None, regexp=None, channels=list(), server=None, end=None, call_end=None):
self.name = name self.name = name
self.end = end
self.call = call self.call = call
if call_end is None:
self.call_end = self.call
else:
self.call_end = call_end
self.regexp = regexp self.regexp = regexp
self.data = data self.data = data
self.times = -1 self.times = -1
self.server = server
self.channels = channels self.channels = channels
def is_matching(self, strcmp, channel): 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 (len(self.channel) <= 0 or channel in self.channels) and ( 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 not None and strcmp == self.name) or ( (self.name is not None and strcmp == self.name) or (
self.end is not None and strcmp == self.end) or (
self.regexp is not None and re.match(self.regexp, strcmp))) self.regexp is not None and re.match(self.regexp, strcmp)))
def run(self, msg): def run(self, msg, data2=None, strcmp=None):
"""Run the hook""" """Run the hook"""
if self.times != 0: if self.times != 0:
self.times -= 1 self.times -= 1
if self.data is None: if (self.end is not None and strcmp is not None and
return self.call(msg) self.call_end is not None and strcmp == self.end):
elif isinstance(self.data, dict): call = self.call_end
return self.call(msg, **self.data) self.times = 0
else: else:
return self.call(msg, self.data) call = self.call
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)

View File

@ -145,6 +145,7 @@ class ModuleLoader(SourceLoader):
# Set module common functions and datas # Set module common functions and datas
module.REGISTERED_HOOKS = list() module.REGISTERED_HOOKS = list()
module.REGISTERED_EVENTS = list()
module.DEBUG = False module.DEBUG = False
module.DIR = self.mpath module.DIR = self.mpath
module.name = fullname module.name = fullname
@ -152,6 +153,10 @@ class ModuleLoader(SourceLoader):
module.print_debug = lambda msg: mod_print_dbg(module, msg) module.print_debug = lambda msg: mod_print_dbg(module, msg)
module.send_response = lambda srv, res: mod_send_response(self.context, srv, res) module.send_response = lambda srv, res: mod_send_response(self.context, srv, res)
module.add_hook = lambda store, hook: self.context.hooks.add_hook(store, hook, module) module.add_hook = lambda store, hook: self.context.hooks.add_hook(store, hook, module)
module.del_hook = lambda store, hook: self.context.hooks.del_hook(store, hook)
module.add_event = lambda evt: self.context.add_event(evt, module_src=module)
module.add_event_eid = lambda evt, eid: self.context.add_event(evt, eid, module_src=module)
module.del_event = lambda evt: self.context.add_event(evt, module_src=module)
if not hasattr(module, "NODATA"): if not hasattr(module, "NODATA"):
module.DATAS = xmlparser.parse_file(self.context.datas_path module.DATAS = xmlparser.parse_file(self.context.datas_path

View File

@ -105,8 +105,8 @@ class Message:
def parse_content(self): def parse_content(self):
"""Parse or reparse the message content""" """Parse or reparse the message content"""
# If CTCP, remove 0x01 # If CTCP, remove 0x01
#if self.ctcp: if self.ctcp:
# self.content = self.content[1:len(self.content)-1] self.content = self.content[1:len(self.content)-1]
# Split content by words # Split content by words
try: try:
@ -146,29 +146,6 @@ class Message:
return False return False
return self.srv.accepted_channel(self.channel) return self.srv.accepted_channel(self.channel)
def treat(self):
"""Parse and treat the message"""
if self.channel in self.srv.channels:
if self.cmd == "353":
self.srv.channels[self.channel].parse353(self)
elif self.cmd == "332":
self.srv.channels[self.channel].parse332(self)
elif self.cmd == "MODE":
self.srv.channels[self.channel].mode(self)
elif self.cmd == "JOIN":
self.srv.channels[self.channel].join(self.nick)
elif self.cmd == "PART":
self.srv.channels[self.channel].part(self.nick)
elif self.cmd == "TOPIC":
self.srv.channels[self.channel].topic = self.content
elif self.cmd == "NICK":
for chn in self.srv.channels.keys():
self.srv.channels[chn].nick(self.nick, self.content)
elif self.cmd == "QUIT":
for chn in self.srv.channels.keys():
self.srv.channels[chn].part(self.nick)
return None
############################## ##############################
# # # #
# Extraction/Format text # # Extraction/Format text #

View File

@ -154,3 +154,13 @@ class Response:
self.pop() self.pop()
self.elt = 0 self.elt = 0
return msg return msg
import hooks
class Hook:
def __init__(self, TYPE, call, name=None, data=None, regexp=None,
channels=list(), server=None, end=None, call_end=None,
SRC=None):
self.hook = hooks.Hook(call, name, data, regexp, channels,
server, end, call_end)
self.type = TYPE
self.src = SRC

View File

@ -144,7 +144,10 @@ class Server(threading.Thread):
self._receive_action = receive_action self._receive_action = receive_action
if not self.connected: if not self.connected:
self.stop = False self.stop = False
self.start() try:
self.start()
except RuntimeError:
pass
elif verb: elif verb:
print (" Already connected.") print (" Already connected.")