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:
parent
21f2af1cad
commit
dc52593953
11
DCC.py
11
DCC.py
@ -35,7 +35,6 @@ class DCC(server.Server):
|
||||
def __init__(self, srv, dest, socket=None):
|
||||
server.Server.__init__(self)
|
||||
|
||||
self.DCC = False
|
||||
self.error = False # An error has occur, closing the connection?
|
||||
self.messages = list() # Message queued before connexion
|
||||
|
||||
@ -132,9 +131,10 @@ class DCC(server.Server):
|
||||
if self.error:
|
||||
self.srv.send_msg_final(self.nick, msg)
|
||||
elif not self.connected or self.s is None:
|
||||
if not self.DCC:
|
||||
self.DCC = True
|
||||
try:
|
||||
self.start()
|
||||
except RuntimeError:
|
||||
pass
|
||||
self.messages.append(msg)
|
||||
else:
|
||||
for line in msg.split("\n"):
|
||||
@ -146,9 +146,10 @@ class DCC(server.Server):
|
||||
"""Send a file over DCC"""
|
||||
if os.path.isfile(filename):
|
||||
self.messages = filename
|
||||
if not self.DCC:
|
||||
try:
|
||||
self.start()
|
||||
self.DCC = True
|
||||
except RuntimeError:
|
||||
pass
|
||||
else:
|
||||
print("File not found `%s'" % filename)
|
||||
|
||||
|
30
IRCServer.py
30
IRCServer.py
@ -22,8 +22,9 @@ import socket
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
import channel
|
||||
from channel import Channel
|
||||
from DCC import DCC
|
||||
from hooks import Hook
|
||||
import message
|
||||
import server
|
||||
import xmlparser
|
||||
@ -56,7 +57,7 @@ class IRCServer(server.Server):
|
||||
|
||||
self.channels = dict()
|
||||
for chn in self.node.getNodes("channel"):
|
||||
chan = channel.Channel(chn, self)
|
||||
chan = Channel(chn["name"], chn["password"])
|
||||
self.channels[chan.name] = chan
|
||||
|
||||
|
||||
@ -104,6 +105,23 @@ class IRCServer(server.Server):
|
||||
"""Gives the server identifiant"""
|
||||
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):
|
||||
"""Return True if the channel (or the user) is authorized"""
|
||||
if self.allow_all:
|
||||
@ -120,11 +138,7 @@ class IRCServer(server.Server):
|
||||
"""Join a channel"""
|
||||
if force or (chan is not None and
|
||||
self.connected and chan not in self.channels):
|
||||
chn = xmlparser.module_state.ModuleState("channel")
|
||||
chn["name"] = chan
|
||||
chn["password"] = password
|
||||
self.node.addChild(chn)
|
||||
self.channels[chan] = channel.Channel(chn, self)
|
||||
self.channels[chan] = Channel(chan, password)
|
||||
if password is not None:
|
||||
self.s.send(("JOIN %s %s\r\n" % (chan, password)).encode())
|
||||
else:
|
||||
@ -136,7 +150,7 @@ class IRCServer(server.Server):
|
||||
def leave(self, chan):
|
||||
"""Leave a channel"""
|
||||
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:
|
||||
self.leave(c)
|
||||
else:
|
||||
|
165
bot.py
165
bot.py
@ -21,6 +21,7 @@ from datetime import timedelta
|
||||
from queue import Queue
|
||||
import threading
|
||||
import time
|
||||
import re
|
||||
|
||||
import consumer
|
||||
import event
|
||||
@ -67,6 +68,10 @@ class Bot:
|
||||
self.cnsr_thrd = list()
|
||||
self.cnsr_thrd_size = -1
|
||||
|
||||
self.hooks.add_hook("irc_hook",
|
||||
hooks.Hook(self.treat_prvmsg, "PRIVMSG"),
|
||||
self)
|
||||
|
||||
|
||||
def init_ctcp_capabilities(self):
|
||||
"""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]))
|
||||
|
||||
|
||||
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"""
|
||||
if eid is None:
|
||||
# Find an ID
|
||||
@ -123,18 +128,26 @@ class Bot:
|
||||
if len(self.events) <= 0 or self.events[i+1] != evt:
|
||||
return None
|
||||
|
||||
if module_src is not None:
|
||||
module_src.REGISTERED_EVENTS.append(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"""
|
||||
if len(self.events) > 0 and id == self.events[0].id:
|
||||
self.events.remove(self.events[0])
|
||||
self.update_timer()
|
||||
if module_src is not None:
|
||||
module_src.REGISTERED_EVENTS.remove(evt.id)
|
||||
return True
|
||||
|
||||
for evt in self.events:
|
||||
if evt.id == id:
|
||||
self.events.remove(evt)
|
||||
|
||||
if module_src is not None:
|
||||
module_src.REGISTERED_EVENTS.remove(evt.id)
|
||||
return True
|
||||
return False
|
||||
|
||||
@ -172,6 +185,8 @@ class Bot:
|
||||
def addServer(self, node, nick, owner, realname):
|
||||
"""Add a new server to the context"""
|
||||
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:
|
||||
self.servers[srv.id] = srv
|
||||
if srv.autoconnect:
|
||||
@ -326,24 +341,111 @@ class Bot:
|
||||
elif isinstance(store, list):
|
||||
store.remove(hook)
|
||||
|
||||
def treat_pre(self, msg):
|
||||
def treat_pre(self, msg, srv):
|
||||
"""Treat a message before all other treatment"""
|
||||
for h, lvl, store in self.create_cache("all_pre"):
|
||||
h.run(self.create_cache, msg)
|
||||
self.check_rest_times(store, h)
|
||||
if h.is_matching(None, server=srv):
|
||||
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"""
|
||||
for h, lvl, store in self.create_cache("all_post"):
|
||||
c = h.run(msg)
|
||||
self.check_rest_times(store, h)
|
||||
if not c:
|
||||
return False
|
||||
if h.is_matching(None, channel=res.channel, server=res.server):
|
||||
c = h.run(msg)
|
||||
self.check_rest_times(store, h)
|
||||
if not c:
|
||||
return False
|
||||
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"""
|
||||
treated = list()
|
||||
|
||||
@ -352,15 +454,16 @@ class Bot:
|
||||
if msg.cmds[0] in cmd_hook:
|
||||
(hks, lvl, store) = cmd_hook[msg.cmds[0]]
|
||||
for h in hks:
|
||||
res = h.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(store, h)
|
||||
if h.is_matching(msg.cmds[0], channel=msg.channel, server=srv):
|
||||
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 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)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
@ -378,7 +481,7 @@ class Bot:
|
||||
|
||||
return treated
|
||||
|
||||
def treat_ask(self, msg):
|
||||
def treat_ask(self, msg, srv):
|
||||
"""Treat an ask message"""
|
||||
treated = list()
|
||||
|
||||
@ -387,16 +490,17 @@ class Bot:
|
||||
if msg.content in ask_hook:
|
||||
hks, lvl, store = ask_hook[msg.content]
|
||||
for h in hks:
|
||||
res = h.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(store, h)
|
||||
if h.is_matching(msg.content, channel=msg.channel, server=srv):
|
||||
res = h.run(msg, strcmp=msg.content)
|
||||
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 in ask_rgxp:
|
||||
if hook.is_matching(msg.content, msg.channel):
|
||||
res = hook.run(msg)
|
||||
if hook.is_matching(msg.content, channel=msg.channel, server=srv):
|
||||
res = hook.run(msg, strcmp=msg.content)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(store, hook)
|
||||
@ -413,7 +517,7 @@ class Bot:
|
||||
|
||||
return treated
|
||||
|
||||
def treat_answer(self, msg):
|
||||
def treat_answer(self, msg, srv):
|
||||
"""Treat a normal message"""
|
||||
treated = list()
|
||||
|
||||
@ -422,16 +526,17 @@ class Bot:
|
||||
if msg.content in msg_hook:
|
||||
hks, lvl, store = msg_hook[msg.content]
|
||||
for h in hks:
|
||||
res = h.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(store, h)
|
||||
if h.is_matching(msg.content, channel=msg.channel, server=srv):
|
||||
res = h.run(msg, strcmp=msg.content)
|
||||
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 in msg_rgxp:
|
||||
if hook.is_matching(msg.content, msg.channel):
|
||||
res = hook.run(msg)
|
||||
if hook.is_matching(msg.content, channel=msg.channel, server=srv):
|
||||
res = hook.run(msg, strcmp=msg.content)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(store, hook)
|
||||
|
153
channel.py
153
channel.py
@ -1,71 +1,102 @@
|
||||
# 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:
|
||||
def __init__(self, node, srv):
|
||||
self.node = node
|
||||
self.name = node["name"]
|
||||
self.password = node["password"]
|
||||
self.people = dict()
|
||||
self.srv = srv
|
||||
self.topic = ""
|
||||
def __init__(self, name, password=None):
|
||||
self.name = name
|
||||
self.password = password
|
||||
self.people = dict()
|
||||
self.topic = ""
|
||||
|
||||
def join(self, nick, level = 0):
|
||||
#print ("%s arrive sur %s" % (nick, self.name))
|
||||
self.people[nick] = level
|
||||
def treat(self, cmd, msg):
|
||||
if cmd == "353":
|
||||
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):
|
||||
"""Send command to change the topic"""
|
||||
self.srv.send_msg(self.name, newtopic, "TOPIC")
|
||||
self.topic = newtopic
|
||||
def join(self, nick, level = 0):
|
||||
"""Someone join the channel"""
|
||||
#print ("%s arrive sur %s" % (nick, self.name))
|
||||
self.people[nick] = level
|
||||
|
||||
def nick(self, oldnick, newnick):
|
||||
#print ("%s change de nom pour %s" % (oldnick, newnick))
|
||||
if oldnick in self.people:
|
||||
lvl = self.people[oldnick]
|
||||
del self.people[oldnick]
|
||||
else:
|
||||
lvl = 0
|
||||
self.people[newnick] = lvl
|
||||
def chtopic(self, newtopic):
|
||||
"""Send command to change the topic"""
|
||||
self.srv.send_msg(self.name, newtopic, "TOPIC")
|
||||
self.topic = newtopic
|
||||
|
||||
def part(self, nick):
|
||||
#print ("%s vient de quitter %s" % (self.sender, self.channel))
|
||||
if nick in self.people:
|
||||
del self.people[nick]
|
||||
def nick(self, oldnick, newnick):
|
||||
"""Someone change his nick"""
|
||||
if oldnick in self.people:
|
||||
#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):
|
||||
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 part(self, nick):
|
||||
"""Someone leave the channel"""
|
||||
if nick in self.people:
|
||||
#print ("%s vient de quitter %s" % (nick, self.name))
|
||||
del self.people[nick]
|
||||
|
||||
def parse332(self, msg):
|
||||
self.topic = msg.content
|
||||
def mode(self, msg):
|
||||
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):
|
||||
for p in msg.content:
|
||||
p = p.decode()
|
||||
if p[0] == "@":
|
||||
level = 4
|
||||
elif p[0] == "%":
|
||||
level = 2
|
||||
elif p[0] == "+":
|
||||
level = 1
|
||||
else:
|
||||
self.people[p] = 0
|
||||
continue
|
||||
self.people[p[1:]] = level
|
||||
def parse332(self, msg):
|
||||
self.topic = msg.content
|
||||
|
||||
def parse353(self, msg):
|
||||
for p in msg.content:
|
||||
p = p.decode()
|
||||
if p[0] == "@":
|
||||
level = 4
|
||||
elif p[0] == "%":
|
||||
level = 2
|
||||
elif p[0] == "+":
|
||||
level = 1
|
||||
else:
|
||||
self.join(p, 0)
|
||||
continue
|
||||
self.join(p[1:], level)
|
||||
|
132
consumer.py
132
consumer.py
@ -25,7 +25,7 @@ import sys
|
||||
import bot
|
||||
from DCC import DCC
|
||||
from message import Message
|
||||
from response import Response
|
||||
import response
|
||||
import server
|
||||
|
||||
class MessageConsumer:
|
||||
@ -43,104 +43,48 @@ class MessageConsumer:
|
||||
if msg.cmd == "PING":
|
||||
self.srv.send_pong(msg.content)
|
||||
else:
|
||||
# TODO: Manage credits here
|
||||
context.treat_pre(msg)
|
||||
|
||||
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
|
||||
# All messages
|
||||
context.treat_pre(msg, self.srv)
|
||||
|
||||
# TODO: Manage credits
|
||||
return context.treat_irc(msg, self.srv)
|
||||
|
||||
def treat_out(self, context, res):
|
||||
"""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
|
||||
if (res.server is not None and
|
||||
res.server.instanceof(string) and res.server in context.servers):
|
||||
res.server = context.servers[res.server]
|
||||
if (res.server is not None and
|
||||
not res.server.instanceof(server.Server)):
|
||||
print ("\033[1;35mWarning:\033[0m the server defined in this "
|
||||
"response doesn't exist: %s" % (res.server))
|
||||
res.server = None
|
||||
if res.server is None:
|
||||
res.server = self.srv
|
||||
if (res.server is not None and
|
||||
isinstance(res.server, str) and res.server in context.servers):
|
||||
res.server = context.servers[res.server]
|
||||
if (res.server is not None and
|
||||
not isinstance(res.server, server.Server)):
|
||||
print ("\033[1;35mWarning:\033[0m the server defined in this "
|
||||
"response doesn't exist: %s" % (res.server))
|
||||
res.server = None
|
||||
if res.server is None:
|
||||
res.server = self.srv
|
||||
|
||||
# Sent the message only if treat_post authorize it
|
||||
if context.treat_post(res):
|
||||
res.server.send_response(res, self.data)
|
||||
# Sent the message only if treat_post authorize it
|
||||
if context.treat_post(res):
|
||||
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):
|
||||
"""Create, parse and treat the message"""
|
||||
try:
|
||||
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)
|
||||
except:
|
||||
print ("\033[1;31mERROR:\033[0m occurred during the "
|
||||
@ -151,17 +95,7 @@ class MessageConsumer:
|
||||
return
|
||||
|
||||
# Send message
|
||||
if res is not None:
|
||||
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)
|
||||
self.treat_out(context, res)
|
||||
|
||||
# Inform that the message has been treated
|
||||
self.srv.msg_treated(self.data)
|
||||
@ -210,7 +144,7 @@ class Consumer(threading.Thread):
|
||||
|
||||
def _help_msg(modules, sndr, cmd):
|
||||
"""Parse and response to help messages"""
|
||||
res = Response(sndr)
|
||||
res = response.Response(sndr)
|
||||
if len(cmd) > 1:
|
||||
if cmd[1] in modules:
|
||||
if len(cmd) > 2:
|
||||
|
70
hooks.py
70
hooks.py
@ -22,16 +22,19 @@ class MessagesHook:
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
# Store specials hook
|
||||
# Store specials hooks
|
||||
self.all_pre = list() # Treated before any parse
|
||||
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.ask_hook = dict()
|
||||
self.msg_hook = dict()
|
||||
|
||||
# Store regexp hook
|
||||
# Store regexp hooks
|
||||
self.cmd_rgxp = list()
|
||||
self.ask_rgxp = list()
|
||||
self.msg_rgxp = list()
|
||||
@ -61,12 +64,16 @@ class MessagesHook:
|
||||
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:
|
||||
print ("\033[1;32mWarning:\033[0m unrecognized hook store type")
|
||||
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))
|
||||
|
||||
def register_hook_attributes(self, store, module, node):
|
||||
@ -92,7 +99,7 @@ class MessagesHook:
|
||||
node["type"] == "all"):
|
||||
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"""
|
||||
if store in self.context.hooks_cache:
|
||||
del self.context.hooks_cache[store]
|
||||
@ -105,33 +112,68 @@ class MessagesHook:
|
||||
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 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.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
|
||||
|
||||
def is_matching(self, strcmp, channel):
|
||||
def is_matching(self, strcmp, channel=None, server=None):
|
||||
"""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.end is not None and strcmp == self.end) or (
|
||||
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"""
|
||||
if self.times != 0:
|
||||
self.times -= 1
|
||||
|
||||
if self.data is None:
|
||||
return self.call(msg)
|
||||
elif isinstance(self.data, dict):
|
||||
return self.call(msg, **self.data)
|
||||
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:
|
||||
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)
|
||||
|
@ -145,6 +145,7 @@ class ModuleLoader(SourceLoader):
|
||||
|
||||
# Set module common functions and datas
|
||||
module.REGISTERED_HOOKS = list()
|
||||
module.REGISTERED_EVENTS = list()
|
||||
module.DEBUG = False
|
||||
module.DIR = self.mpath
|
||||
module.name = fullname
|
||||
@ -152,6 +153,10 @@ class ModuleLoader(SourceLoader):
|
||||
module.print_debug = lambda msg: mod_print_dbg(module, msg)
|
||||
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.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"):
|
||||
module.DATAS = xmlparser.parse_file(self.context.datas_path
|
||||
|
27
message.py
27
message.py
@ -105,8 +105,8 @@ class Message:
|
||||
def parse_content(self):
|
||||
"""Parse or reparse the message content"""
|
||||
# If CTCP, remove 0x01
|
||||
#if self.ctcp:
|
||||
# self.content = self.content[1:len(self.content)-1]
|
||||
if self.ctcp:
|
||||
self.content = self.content[1:len(self.content)-1]
|
||||
|
||||
# Split content by words
|
||||
try:
|
||||
@ -146,29 +146,6 @@ class Message:
|
||||
return False
|
||||
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 #
|
||||
|
10
response.py
10
response.py
@ -154,3 +154,13 @@ class Response:
|
||||
self.pop()
|
||||
self.elt = 0
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user