New message processing
This commit is contained in:
parent
981025610e
commit
dfde4c5f49
43
bot.py
43
bot.py
@ -25,7 +25,7 @@ import threading
|
|||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
__version__ = '3.4.dev0'
|
__version__ = '3.4.dev1'
|
||||||
__author__ = 'nemunaire'
|
__author__ = 'nemunaire'
|
||||||
|
|
||||||
from consumer import Consumer, EventConsumer, MessageConsumer
|
from consumer import Consumer, EventConsumer, MessageConsumer
|
||||||
@ -33,7 +33,7 @@ from event import ModuleEvent
|
|||||||
from hooks.messagehook import MessageHook
|
from hooks.messagehook import MessageHook
|
||||||
from hooks.manager import HooksManager
|
from hooks.manager import HooksManager
|
||||||
from networkbot import NetworkBot
|
from networkbot import NetworkBot
|
||||||
from server.IRC import IRCServer
|
from server.IRC import IRC as IRCServer
|
||||||
from server.DCC import DCC
|
from server.DCC import DCC
|
||||||
|
|
||||||
logger = logging.getLogger("nemubot.bot")
|
logger = logging.getLogger("nemubot.bot")
|
||||||
@ -74,28 +74,27 @@ class Bot(threading.Thread):
|
|||||||
# Own hooks
|
# Own hooks
|
||||||
self.hooks = HooksManager()
|
self.hooks = HooksManager()
|
||||||
def in_ping(msg):
|
def in_ping(msg):
|
||||||
if re.match("^ *(m[' ]?entends?[ -]+tu|h?ear me|do you copy|ping)", msg.text, re.I) is not None:
|
if re.match("^ *(m[' ]?entends?[ -]+tu|h?ear me|do you copy|ping)", msg.message, re.I) is not None:
|
||||||
return "PRIVMSG %s :%s: pong" % (",".join(msg.receivers), msg.nick)
|
return msg.respond("pong")
|
||||||
self.hooks.add_hook(MessageHook(in_ping), "in", "PRIVMSG", "ask")
|
self.hooks.add_hook(MessageHook(in_ping), "in", "DirectAsk")
|
||||||
|
|
||||||
def _help_msg(msg):
|
def _help_msg(msg):
|
||||||
"""Parse and response to help messages"""
|
"""Parse and response to help messages"""
|
||||||
cmd = msg.cmds
|
|
||||||
from more import Response
|
from more import Response
|
||||||
res = Response()
|
res = Response(channel=msg.frm)
|
||||||
if len(cmd) > 1:
|
if len(msg.args) > 1:
|
||||||
if cmd[1] in self.modules:
|
if msg.args[0] in self.modules:
|
||||||
if len(cmd) > 2:
|
if len(msg.args) > 2:
|
||||||
if hasattr(self.modules[cmd[1]], "HELP_cmd"):
|
if hasattr(self.modules[msg.args[0]], "HELP_cmd"):
|
||||||
res.append_message(self.modules[cmd[1]].HELP_cmd(cmd[2]))
|
res.append_message(self.modules[msg.args[0]].HELP_cmd(msg.args[1]))
|
||||||
else:
|
else:
|
||||||
res.append_message("No help for command %s in module %s" % (cmd[2], cmd[1]))
|
res.append_message("No help for command %s in module %s" % (msg.args[1], msg.args[0]))
|
||||||
elif hasattr(self.modules[cmd[1]], "help_full"):
|
elif hasattr(self.modules[msg.args[0]], "help_full"):
|
||||||
res.append_message(self.modules[cmd[1]].help_full())
|
res.append_message(self.modules[msg.args[0]].help_full())
|
||||||
else:
|
else:
|
||||||
res.append_message("No help for module %s" % cmd[1])
|
res.append_message("No help for module %s" % msg.args[0])
|
||||||
else:
|
else:
|
||||||
res.append_message("No module named %s" % cmd[1])
|
res.append_message("No module named %s" % msg.args[0])
|
||||||
else:
|
else:
|
||||||
res.append_message("Pour me demander quelque chose, commencez "
|
res.append_message("Pour me demander quelque chose, commencez "
|
||||||
"votre message par mon nom ; je réagis "
|
"votre message par mon nom ; je réagis "
|
||||||
@ -113,7 +112,7 @@ class Bot(threading.Thread):
|
|||||||
" de tous les modules disponibles localement",
|
" de tous les modules disponibles localement",
|
||||||
message=["\x03\x02%s\x03\x02 (%s)" % (im, self.modules[im].__doc__) for im in self.modules if self.modules[im].__doc__])
|
message=["\x03\x02%s\x03\x02 (%s)" % (im, self.modules[im].__doc__) for im in self.modules if self.modules[im].__doc__])
|
||||||
return res
|
return res
|
||||||
self.hooks.add_hook(MessageHook(_help_msg, "help"), "in", "PRIVMSG", "cmd")
|
self.hooks.add_hook(MessageHook(_help_msg, "help"), "in", "Command")
|
||||||
|
|
||||||
# Other known bots, making a bots network
|
# Other known bots, making a bots network
|
||||||
self.network = dict()
|
self.network = dict()
|
||||||
@ -343,12 +342,10 @@ class Bot(threading.Thread):
|
|||||||
"""Add a module to the context, if already exists, unload the
|
"""Add a module to the context, if already exists, unload the
|
||||||
old one before"""
|
old one before"""
|
||||||
# Check if the module already exists
|
# Check if the module already exists
|
||||||
for mod in self.modules.keys():
|
if module.__name__ in self.modules:
|
||||||
if self.modules[mod].name == module.name:
|
self.unload_module(module.__name__)
|
||||||
self.unload_module(self.modules[mod].name)
|
|
||||||
break
|
|
||||||
|
|
||||||
self.modules[module.name] = module
|
self.modules[module.__name__] = module
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
45
consumer.py
45
consumer.py
@ -20,13 +20,6 @@ import logging
|
|||||||
import queue
|
import queue
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
import traceback
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import bot
|
|
||||||
from server.DCC import DCC
|
|
||||||
from message import Message
|
|
||||||
import server
|
|
||||||
|
|
||||||
logger = logging.getLogger("nemubot.consumer")
|
logger = logging.getLogger("nemubot.consumer")
|
||||||
|
|
||||||
@ -48,26 +41,10 @@ class MessageConsumer:
|
|||||||
msg -- The Message or Response to qualify
|
msg -- The Message or Response to qualify
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not hasattr(msg, "qual") or msg.qual is None:
|
|
||||||
# Assume this is a message with no particulariry
|
|
||||||
msg.qual = "def"
|
|
||||||
|
|
||||||
# Define the source server if not already done
|
# Define the source server if not already done
|
||||||
if not hasattr(msg, "server") or msg.server is None:
|
if not hasattr(msg, "server") or msg.server is None:
|
||||||
msg.server = self.srv.id
|
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
|
return msg
|
||||||
|
|
||||||
|
|
||||||
@ -84,8 +61,8 @@ class MessageConsumer:
|
|||||||
|
|
||||||
while len(new_msg) > 0:
|
while len(new_msg) > 0:
|
||||||
msg = new_msg.pop(0)
|
msg = new_msg.pop(0)
|
||||||
for h in hm.get_hooks("pre", msg.cmd, msg.qual):
|
for h in hm.get_hooks("pre", type(msg).__name__):
|
||||||
if h.match(message=msg, server=self.srv):
|
if h.match(msg, server=self.srv):
|
||||||
res = h.run(msg)
|
res = h.run(msg)
|
||||||
if isinstance(res, list):
|
if isinstance(res, list):
|
||||||
for i in range(len(res)):
|
for i in range(len(res)):
|
||||||
@ -113,17 +90,12 @@ class MessageConsumer:
|
|||||||
|
|
||||||
self.responses = list()
|
self.responses = list()
|
||||||
for msg in self.msgs:
|
for msg in self.msgs:
|
||||||
for h in hm.get_hooks("in", msg.cmd, msg.qual):
|
for h in hm.get_hooks("in", type(msg).__name__):
|
||||||
if h.match(message=msg, server=self.srv):
|
if h.match(msg, server=self.srv):
|
||||||
res = h.run(msg)
|
res = h.run(msg)
|
||||||
if isinstance(res, list):
|
if isinstance(res, list):
|
||||||
for r in res:
|
|
||||||
if hasattr(r, "set_sender"):
|
|
||||||
r.set_sender(msg.sender)
|
|
||||||
self.responses += res
|
self.responses += res
|
||||||
elif res is not None:
|
elif res is not None:
|
||||||
if hasattr(res, "set_sender"):
|
|
||||||
res.set_sender(msg.sender)
|
|
||||||
self.responses.append(res)
|
self.responses.append(res)
|
||||||
|
|
||||||
|
|
||||||
@ -145,7 +117,7 @@ class MessageConsumer:
|
|||||||
continue
|
continue
|
||||||
msg = self.first_treat(ff)
|
msg = self.first_treat(ff)
|
||||||
for h in hm.get_hooks("post"):
|
for h in hm.get_hooks("post"):
|
||||||
if h.match(message=msg, server=self.srv):
|
if h.match(msg, server=self.srv):
|
||||||
res = h.run(msg)
|
res = h.run(msg)
|
||||||
if isinstance(res, list):
|
if isinstance(res, list):
|
||||||
for i in range(len(res)):
|
for i in range(len(res)):
|
||||||
@ -154,6 +126,7 @@ class MessageConsumer:
|
|||||||
break
|
break
|
||||||
msg = None
|
msg = None
|
||||||
new_msg += res
|
new_msg += res
|
||||||
|
break
|
||||||
elif res is not None and res != msg:
|
elif res is not None and res != msg:
|
||||||
new_msg.append(res)
|
new_msg.append(res)
|
||||||
msg = None
|
msg = None
|
||||||
@ -161,6 +134,8 @@ class MessageConsumer:
|
|||||||
elif res is None or res == False:
|
elif res is None or res == False:
|
||||||
msg = None
|
msg = None
|
||||||
break
|
break
|
||||||
|
else:
|
||||||
|
msg = res
|
||||||
if msg is not None:
|
if msg is not None:
|
||||||
self.responses.append(msg)
|
self.responses.append(msg)
|
||||||
|
|
||||||
@ -182,7 +157,7 @@ class MessageConsumer:
|
|||||||
if self.responses is not None and len(self.responses) > 0:
|
if self.responses is not None and len(self.responses) > 0:
|
||||||
self.post_treat(context.hooks)
|
self.post_treat(context.hooks)
|
||||||
except:
|
except:
|
||||||
logger.exception("Error occurred during the processing of the message: %s", self.msgs[0].raw)
|
logger.exception("Error occurred during the processing of the %s: %s", type(self.msgs[0]).__name__, self.msgs[0])
|
||||||
|
|
||||||
for res in self.responses:
|
for res in self.responses:
|
||||||
to_server = None
|
to_server = None
|
||||||
@ -200,7 +175,7 @@ class MessageConsumer:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Sent the message only if treat_post authorize it
|
# Sent the message only if treat_post authorize it
|
||||||
to_server.write(res)
|
to_server.send_response(res)
|
||||||
|
|
||||||
class EventConsumer:
|
class EventConsumer:
|
||||||
"""Store a event before treating"""
|
"""Store a event before treating"""
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from message import TextMessage, DirectAsk
|
||||||
|
|
||||||
class IRCException(Exception):
|
class IRCException(Exception):
|
||||||
|
|
||||||
def __init__(self, message, personnal=True):
|
def __init__(self, message, personnal=True):
|
||||||
@ -24,6 +26,7 @@ class IRCException(Exception):
|
|||||||
self.personnal = personnal
|
self.personnal = personnal
|
||||||
|
|
||||||
def fill_response(self, msg):
|
def fill_response(self, msg):
|
||||||
# TODO: no more Response usable here
|
if self.personnal:
|
||||||
from more import Response
|
return DirectAsk(msg.frm, self.message, server=msg.server, to=msg.to_response)
|
||||||
return Response(self.message, channel=msg.receivers, nick=(msg.nick if self.personnal else None))
|
else:
|
||||||
|
return TextMessage(self.message, server=msg.server, to=msg.to_response)
|
||||||
|
@ -20,8 +20,7 @@ import re
|
|||||||
|
|
||||||
from exception import IRCException
|
from exception import IRCException
|
||||||
import hooks
|
import hooks
|
||||||
|
import message
|
||||||
from message import Message
|
|
||||||
|
|
||||||
class MessageHook(hooks.AbstractHook):
|
class MessageHook(hooks.AbstractHook):
|
||||||
|
|
||||||
@ -39,24 +38,28 @@ class MessageHook(hooks.AbstractHook):
|
|||||||
self.channels = channels
|
self.channels = channels
|
||||||
|
|
||||||
|
|
||||||
def match(self, message, server=None):
|
def match(self, msg, server=None):
|
||||||
if not isinstance(message, Message):
|
if not isinstance(msg, message.AbstractMessage):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
elif message.qual == "cmd":
|
elif isinstance(msg, message.Command):
|
||||||
return self.is_matching(message.cmds[0], message.channel, server)
|
return self.is_matching(msg.cmd, msg.to, server)
|
||||||
elif hasattr(message, "text"):
|
elif isinstance(msg, message.TextMessage):
|
||||||
return self.is_matching(message.text, message.channel, server)
|
return self.is_matching(msg.message, msg.to, server)
|
||||||
elif len(message.params) > 0:
|
|
||||||
return self.is_matching(message.params[0], message.channel, server)
|
|
||||||
else:
|
else:
|
||||||
return self.is_matching(message.cmd, message.channel, server)
|
return False
|
||||||
|
|
||||||
|
|
||||||
def is_matching(self, strcmp, channel=None, server=None):
|
def is_matching(self, strcmp, receivers=list(), 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
|
if (server is None or self.server is None or self.server == server
|
||||||
channel in self.channels) and (server is None or
|
) and ((self.name is None or strcmp == self.name) and (
|
||||||
self.server is None or self.server == server) and (
|
self.regexp is None or re.match(self.regexp, strcmp))):
|
||||||
(self.name is None or strcmp == self.name) and (
|
|
||||||
self.regexp is None or re.match(self.regexp, strcmp)))
|
if receivers and self.channels:
|
||||||
|
for receiver in receivers:
|
||||||
|
if receiver in self.channels:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
39
importer.py
39
importer.py
@ -28,6 +28,7 @@ from bot import __version__
|
|||||||
import event
|
import event
|
||||||
import exception
|
import exception
|
||||||
import hooks
|
import hooks
|
||||||
|
from message import TextMessage
|
||||||
import xmlparser
|
import xmlparser
|
||||||
|
|
||||||
logger = logging.getLogger("nemubot.importer")
|
logger = logging.getLogger("nemubot.importer")
|
||||||
@ -124,13 +125,6 @@ class ModuleLoader(SourceLoader):
|
|||||||
def load_module(self, fullname):
|
def load_module(self, fullname):
|
||||||
module = self._load_module(fullname, sourceless=True)
|
module = self._load_module(fullname, sourceless=True)
|
||||||
|
|
||||||
# Remove the module from sys list
|
|
||||||
del sys.modules[fullname]
|
|
||||||
|
|
||||||
# If the module was already loaded, then reload it
|
|
||||||
if hasattr(module, '__LOADED__'):
|
|
||||||
reload(module)
|
|
||||||
|
|
||||||
# Check that is a valid nemubot module
|
# Check that is a valid nemubot module
|
||||||
if not hasattr(module, "nemubotversion"):
|
if not hasattr(module, "nemubotversion"):
|
||||||
raise ImportError("Module `%s' is not a nemubot module."%self.name)
|
raise ImportError("Module `%s' is not a nemubot module."%self.name)
|
||||||
@ -144,21 +138,25 @@ class ModuleLoader(SourceLoader):
|
|||||||
module.logger = logging.getLogger("nemubot.module." + fullname)
|
module.logger = logging.getLogger("nemubot.module." + fullname)
|
||||||
|
|
||||||
def prnt(*args):
|
def prnt(*args):
|
||||||
print("[%s]" % module.name, *args)
|
print("[%s]" % module.__name__, *args)
|
||||||
module.logger.info(*args)
|
module.logger.info(*args)
|
||||||
def prnt_dbg(*args):
|
def prnt_dbg(*args):
|
||||||
if module.DEBUG:
|
if module.DEBUG:
|
||||||
print("{%s}" % module.name, *args)
|
print("{%s}" % module.__name__, *args)
|
||||||
module.logger.debug(*args)
|
module.logger.debug(*args)
|
||||||
|
|
||||||
def mod_save():
|
def mod_save():
|
||||||
fpath = self.context.data_path + "/" + module.name + ".xml"
|
fpath = self.context.data_path + "/" + module.__name__ + ".xml"
|
||||||
module.print_debug("Saving DATAS to " + fpath)
|
module.print_debug("Saving DATAS to " + fpath)
|
||||||
module.DATAS.save(fpath)
|
module.DATAS.save(fpath)
|
||||||
|
|
||||||
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].write("PRIVMSG %s :%s" % (",".join(res.receivers), res.get_message()))
|
r = res.next_response()
|
||||||
|
if r.server is not None:
|
||||||
|
return self.context.servers[r.server].send_response(r)
|
||||||
|
else:
|
||||||
|
return self.context.servers[server].send_response(r)
|
||||||
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
|
||||||
@ -183,7 +181,6 @@ class ModuleLoader(SourceLoader):
|
|||||||
module.REGISTERED_EVENTS = list()
|
module.REGISTERED_EVENTS = list()
|
||||||
module.DEBUG = False
|
module.DEBUG = False
|
||||||
module.DIR = self.mpath
|
module.DIR = self.mpath
|
||||||
module.name = fullname
|
|
||||||
module.print = prnt
|
module.print = prnt
|
||||||
module.print_debug = prnt_dbg
|
module.print_debug = prnt_dbg
|
||||||
module.send_response = send_response
|
module.send_response = send_response
|
||||||
@ -195,7 +192,7 @@ class ModuleLoader(SourceLoader):
|
|||||||
|
|
||||||
if not hasattr(module, "NODATA"):
|
if not hasattr(module, "NODATA"):
|
||||||
module.DATAS = xmlparser.parse_file(self.context.data_path
|
module.DATAS = xmlparser.parse_file(self.context.data_path
|
||||||
+ module.name + ".xml")
|
+ module.__name__ + ".xml")
|
||||||
module.save = mod_save
|
module.save = mod_save
|
||||||
else:
|
else:
|
||||||
module.DATAS = None
|
module.DATAS = None
|
||||||
@ -216,7 +213,7 @@ class ModuleLoader(SourceLoader):
|
|||||||
break
|
break
|
||||||
if depend["name"] not in module.MODS:
|
if depend["name"] not in module.MODS:
|
||||||
logger.error("In module `%s', module `%s' require by this "
|
logger.error("In module `%s', module `%s' require by this "
|
||||||
"module but is not loaded.", module.name,
|
"module but is not loaded.", module.__name__,
|
||||||
depend["name"])
|
depend["name"])
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -230,21 +227,21 @@ class ModuleLoader(SourceLoader):
|
|||||||
# Register hooks
|
# Register hooks
|
||||||
register_hooks(module, self.context, self.prompt)
|
register_hooks(module, self.context, self.prompt)
|
||||||
|
|
||||||
logger.info("Module '%s' successfully loaded.", module.name)
|
logger.info("Module '%s' successfully loaded.", module.__name__)
|
||||||
else:
|
else:
|
||||||
logger.error("An error occurs while importing `%s'.", module.name)
|
logger.error("An error occurs while importing `%s'.", module.__name__)
|
||||||
raise ImportError("An error occurs while importing `%s'."
|
raise ImportError("An error occurs while importing `%s'."
|
||||||
% module.name)
|
% module.__name__)
|
||||||
return module
|
return module
|
||||||
|
|
||||||
|
|
||||||
def convert_legacy_store(old):
|
def convert_legacy_store(old):
|
||||||
if old == "cmd_hook" or old == "cmd_rgxp" or old == "cmd_default":
|
if old == "cmd_hook" or old == "cmd_rgxp" or old == "cmd_default":
|
||||||
return "in_PRIVMSG_cmd"
|
return "in_Command"
|
||||||
elif old == "ask_hook" or old == "ask_rgxp" or old == "ask_default":
|
elif old == "ask_hook" or old == "ask_rgxp" or old == "ask_default":
|
||||||
return "in_PRIVMSG_ask"
|
return "in_DirectAsk"
|
||||||
elif old == "msg_hook" or old == "msg_rgxp" or old == "msg_default":
|
elif old == "msg_hook" or old == "msg_rgxp" or old == "msg_default":
|
||||||
return "in_PRIVMSG_def"
|
return "in_TextMessage"
|
||||||
elif old == "all_post":
|
elif old == "all_post":
|
||||||
return "post"
|
return "post"
|
||||||
elif old == "all_pre":
|
elif old == "all_pre":
|
||||||
@ -258,7 +255,7 @@ def add_cap_hook(prompt, module, cmd):
|
|||||||
prompt.add_cap_hook(cmd["name"], getattr(module, cmd["call"]))
|
prompt.add_cap_hook(cmd["name"], getattr(module, cmd["call"]))
|
||||||
else:
|
else:
|
||||||
logger.warn("In module `%s', no function `%s' defined for `%s' "
|
logger.warn("In module `%s', no function `%s' defined for `%s' "
|
||||||
"command hook.", module.name, cmd["call"], cmd["name"])
|
"command hook.", module.__name__, cmd["call"], cmd["name"])
|
||||||
|
|
||||||
def register_hooks(module, context, prompt):
|
def register_hooks(module, context, prompt):
|
||||||
"""Register all available hooks"""
|
"""Register all available hooks"""
|
||||||
|
71
message.py
71
message.py
@ -1,71 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Nemubot is a modulable IRC bot, built around XML configuration files.
|
|
||||||
# Copyright (C) 2012 Mercier Pierre-Olivier
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Affero General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Affero General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
import shlex
|
|
||||||
|
|
||||||
class Message:
|
|
||||||
def __init__ (self, orig, private=False):
|
|
||||||
self.cmd = orig.cmd
|
|
||||||
self.tags = orig.tags
|
|
||||||
self.params = orig.params
|
|
||||||
self.private = private
|
|
||||||
self.prefix = orig.prefix
|
|
||||||
self.nick = orig.nick
|
|
||||||
|
|
||||||
# Special commands
|
|
||||||
if self.cmd == 'PRIVMSG' or self.cmd == 'NOTICE':
|
|
||||||
self.receivers = orig.decode(self.params[0]).split(',')
|
|
||||||
|
|
||||||
# If CTCP, remove 0x01
|
|
||||||
if len(self.params[1]) > 1 and (self.params[1][0] == 0x01 or self.params[1][1] == 0x01):
|
|
||||||
self.is_ctcp = True
|
|
||||||
self.text = orig.decode(self.params[1][1:len(self.params[1])-1])
|
|
||||||
else:
|
|
||||||
self.is_ctcp = False
|
|
||||||
self.text = orig.decode(self.params[1])
|
|
||||||
|
|
||||||
# Split content by words
|
|
||||||
self.parse_content()
|
|
||||||
|
|
||||||
else:
|
|
||||||
for i in range(0, len(self.params)):
|
|
||||||
self.params[i] = orig.decode(self.params[i])
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: here for legacy content
|
|
||||||
@property
|
|
||||||
def sender(self):
|
|
||||||
return self.prefix
|
|
||||||
@property
|
|
||||||
def channel(self):
|
|
||||||
return self.receivers[0]
|
|
||||||
|
|
||||||
|
|
||||||
def parse_content(self):
|
|
||||||
"""Parse or reparse the message content"""
|
|
||||||
# Remove !
|
|
||||||
if len(self.text) > 1 and self.text[0] == '!':
|
|
||||||
self.qual = "cmd"
|
|
||||||
self.text = self.text[1:].strip()
|
|
||||||
|
|
||||||
# Split content by words
|
|
||||||
try:
|
|
||||||
self.cmds = shlex.split(self.text)
|
|
||||||
except ValueError:
|
|
||||||
self.cmds = self.text.split(' ')
|
|
160
message/__init__.py
Normal file
160
message/__init__.py
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Nemubot is a smart and modulable IM bot.
|
||||||
|
# Copyright (C) 2012-2014 nemunaire
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
class AbstractMessage:
|
||||||
|
|
||||||
|
"""This class represents an abstract message"""
|
||||||
|
|
||||||
|
def __init__(self, server, date=None, to=None, to_response=None, frm=None):
|
||||||
|
"""Initialize an abstract message
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
server -- the servir identifier
|
||||||
|
date -- time of the message reception, default: now
|
||||||
|
to -- list of recipients
|
||||||
|
to_response -- if channel(s) where send the response differ
|
||||||
|
frm -- the sender
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.server = server
|
||||||
|
self.date = datetime.now(timezone.utc) if date is None else date
|
||||||
|
self.to = to if to is not None else list()
|
||||||
|
self._to_response = to_response if to_response is None or isinstance(to_response, list) else [ to_response ]
|
||||||
|
self.frm = frm # None allowed when it designate this bot
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def to_response(self):
|
||||||
|
if self._to_response is not None:
|
||||||
|
return self._to_response
|
||||||
|
else:
|
||||||
|
return self.to
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def receivers(self):
|
||||||
|
# TODO: this is for legacy modules
|
||||||
|
return self.to_response
|
||||||
|
|
||||||
|
@property
|
||||||
|
def channel(self):
|
||||||
|
# TODO: this is for legacy modules
|
||||||
|
return self.to_response[0]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nick(self):
|
||||||
|
# TODO: this is for legacy modules
|
||||||
|
return self.frm
|
||||||
|
|
||||||
|
|
||||||
|
def accept(self, visitor):
|
||||||
|
visitor.visit(self)
|
||||||
|
|
||||||
|
|
||||||
|
def export_args(self, without=list()):
|
||||||
|
if not isinstance(without, list):
|
||||||
|
without = [ without ]
|
||||||
|
|
||||||
|
ret = {
|
||||||
|
"server": self.server,
|
||||||
|
"date": self.date,
|
||||||
|
"to": self.to,
|
||||||
|
"to_response": self._to_response,
|
||||||
|
"frm": self.frm
|
||||||
|
}
|
||||||
|
|
||||||
|
for w in without:
|
||||||
|
if w in ret:
|
||||||
|
del ret[w]
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
class TextMessage(AbstractMessage):
|
||||||
|
|
||||||
|
"""This class represent a simple message send to someone"""
|
||||||
|
|
||||||
|
def __init__(self, message, *args, **kargs):
|
||||||
|
"""Initialize a message with no particular specificity
|
||||||
|
|
||||||
|
Argument:
|
||||||
|
message -- the parsed message
|
||||||
|
"""
|
||||||
|
|
||||||
|
AbstractMessage.__init__(self, *args, **kargs)
|
||||||
|
|
||||||
|
self.message = message
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.message
|
||||||
|
|
||||||
|
@property
|
||||||
|
def text(self):
|
||||||
|
# TODO: this is for legacy modules
|
||||||
|
return self.message
|
||||||
|
|
||||||
|
|
||||||
|
class DirectAsk(TextMessage):
|
||||||
|
|
||||||
|
"""This class represents a message to this bot"""
|
||||||
|
|
||||||
|
def __init__(self, designated, *args, **kargs):
|
||||||
|
"""Initialize a message to a specific person
|
||||||
|
|
||||||
|
Argument:
|
||||||
|
designated -- the user designated by the message
|
||||||
|
"""
|
||||||
|
|
||||||
|
TextMessage.__init__(self, *args, **kargs)
|
||||||
|
|
||||||
|
self.designated = designated
|
||||||
|
|
||||||
|
|
||||||
|
def respond(self, message):
|
||||||
|
return DirectAsk(self.frm,
|
||||||
|
message,
|
||||||
|
server=self.server,
|
||||||
|
to=self.to_response)
|
||||||
|
|
||||||
|
|
||||||
|
class Command(AbstractMessage):
|
||||||
|
|
||||||
|
"""This class represents a specialized TextMessage"""
|
||||||
|
|
||||||
|
def __init__(self, cmd, args=None, *nargs, **kargs):
|
||||||
|
AbstractMessage.__init__(self, *nargs, **kargs)
|
||||||
|
|
||||||
|
self.cmd = cmd
|
||||||
|
self.args = args if args is not None else list()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.cmd + " @" + ",@".join(self.args)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cmds(self):
|
||||||
|
# TODO: this is for legacy modules
|
||||||
|
return [self.cmd] + self.args
|
||||||
|
|
||||||
|
|
||||||
|
class OwnerCommand(Command):
|
||||||
|
|
||||||
|
"""This class represents a special command incomming from the owner"""
|
||||||
|
|
||||||
|
pass
|
69
message/printer/IRC.py
Normal file
69
message/printer/IRC.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Nemubot is a smart and modulable IM bot.
|
||||||
|
# Copyright (C) 2012-2014 nemunaire
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from message import TextMessage
|
||||||
|
from message.visitor import AbstractVisitor
|
||||||
|
|
||||||
|
class IRC(AbstractVisitor):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.pp = ""
|
||||||
|
|
||||||
|
|
||||||
|
def visit_TextMessage(self, msg):
|
||||||
|
self.pp += "PRIVMSG %s :" % ",".join(msg.to)
|
||||||
|
if isinstance(msg.message, str):
|
||||||
|
self.pp += msg.message
|
||||||
|
else:
|
||||||
|
msg.message.accept(self)
|
||||||
|
self.pp += "\r\n"
|
||||||
|
|
||||||
|
|
||||||
|
def visit_DirectAsk(self, msg):
|
||||||
|
others = [to for to in msg.to if to != msg.designated]
|
||||||
|
|
||||||
|
# Avoid nick starting message when discussing on user channel
|
||||||
|
if len(others) != len(msg.to):
|
||||||
|
res = TextMessage(msg.message,
|
||||||
|
server=msg.server, date=msg.date,
|
||||||
|
to=msg.to, frm=msg.frm)
|
||||||
|
res.accept(self)
|
||||||
|
|
||||||
|
if len(others):
|
||||||
|
res = TextMessage("%s: %s" % (msg.designated, msg.message),
|
||||||
|
server=msg.server, date=msg.date,
|
||||||
|
to=others, frm=msg.frm)
|
||||||
|
res.accept(self)
|
||||||
|
|
||||||
|
|
||||||
|
def visit_Command(self, msg):
|
||||||
|
res = TextMessage("!%s%s%s" % (msg.cmd,
|
||||||
|
" " if len(msg.args) else "",
|
||||||
|
" ".join(msg.args)),
|
||||||
|
server=msg.server, date=msg.date,
|
||||||
|
to=msg.to, frm=msg.frm)
|
||||||
|
res.accept(self)
|
||||||
|
|
||||||
|
|
||||||
|
def visit_OwnerCommand(self, msg):
|
||||||
|
res = TextMessage("`%s%s%s" % (msg.cmd,
|
||||||
|
" " if len(msg.args) else "",
|
||||||
|
" ".join(msg.args)),
|
||||||
|
server=msg.server, date=msg.date,
|
||||||
|
to=msg.to, frm=msg.frm)
|
||||||
|
res.accept(self)
|
0
message/printer/__init__.py
Normal file
0
message/printer/__init__.py
Normal file
25
message/visitor.py
Normal file
25
message/visitor.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# -*- 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 AbstractVisitor:
|
||||||
|
|
||||||
|
def visit(self, obj):
|
||||||
|
"""Visit a node"""
|
||||||
|
method_name = "visit_%s" % obj.__class__.__name__
|
||||||
|
method = getattr(self, method_name)
|
||||||
|
return method(obj)
|
@ -5,8 +5,10 @@
|
|||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
import shlex
|
||||||
|
|
||||||
from hooks import hook
|
from hooks import hook
|
||||||
|
from message import TextMessage, Command
|
||||||
|
|
||||||
nemubotversion = 3.4
|
nemubotversion = 3.4
|
||||||
|
|
||||||
@ -34,10 +36,8 @@ def set_variable(name, value, creator):
|
|||||||
DATAS.getNode("variables").addChild(var)
|
DATAS.getNode("variables").addChild(var)
|
||||||
|
|
||||||
def get_variable(name, msg=None):
|
def get_variable(name, msg=None):
|
||||||
if name == "sender":
|
if name == "sender" or name == "from" or name == "nick":
|
||||||
return msg.sender
|
return msg.frm
|
||||||
elif name == "nick":
|
|
||||||
return msg.nick
|
|
||||||
elif name == "chan" or name == "channel":
|
elif name == "chan" or name == "channel":
|
||||||
return msg.channel
|
return msg.channel
|
||||||
elif name == "date":
|
elif name == "date":
|
||||||
@ -141,35 +141,25 @@ def replace_variables(cnt, msg=None):
|
|||||||
return " ".join(cnt)
|
return " ".join(cnt)
|
||||||
|
|
||||||
|
|
||||||
@hook("all_post")
|
@hook("pre_Command")
|
||||||
def treat_variables(res):
|
|
||||||
for i in range(0, len(res.messages)):
|
|
||||||
if isinstance(res.messages[i], list):
|
|
||||||
res.messages[i] = replace_variables(", ".join(res.messages[i]), res)
|
|
||||||
else:
|
|
||||||
res.messages[i] = replace_variables(res.messages[i], res)
|
|
||||||
return res
|
|
||||||
|
|
||||||
@hook("pre_PRIVMSG_cmd")
|
|
||||||
def treat_alias(msg):
|
def treat_alias(msg):
|
||||||
if msg.cmds[0] in DATAS.getNode("aliases").index:
|
if msg.cmd in DATAS.getNode("aliases").index:
|
||||||
oldcmd = msg.cmds[0]
|
txt = DATAS.getNode("aliases").index[msg.cmd]["origin"]
|
||||||
msg.text = msg.text.replace(msg.cmds[0],
|
# TODO: for legacy compatibility
|
||||||
DATAS.getNode("aliases").index[msg.cmds[0]]["origin"], 1)
|
if txt[0] == "!":
|
||||||
|
txt = txt[1:]
|
||||||
msg.qual = "def"
|
try:
|
||||||
msg.parse_content()
|
args = shlex.split(txt)
|
||||||
|
except ValueError:
|
||||||
|
args = txt.split(' ')
|
||||||
|
nmsg = Command(args[0], args[1:] + msg.args, **msg.export_args())
|
||||||
|
|
||||||
# Avoid infinite recursion
|
# Avoid infinite recursion
|
||||||
if oldcmd == msg.cmds[0]:
|
if msg.cmd != nmsg.cmd:
|
||||||
return msg
|
return nmsg
|
||||||
else:
|
|
||||||
return treat_alias(msg)
|
return msg
|
||||||
|
|
||||||
else:
|
|
||||||
msg.text = replace_variables(msg.text, msg)
|
|
||||||
msg.parse_content()
|
|
||||||
return msg
|
|
||||||
|
|
||||||
@hook("ask_default")
|
@hook("ask_default")
|
||||||
def parseask(msg):
|
def parseask(msg):
|
||||||
|
@ -71,8 +71,7 @@ def start_countdown(msg):
|
|||||||
strnd["server"] = msg.server
|
strnd["server"] = msg.server
|
||||||
strnd["channel"] = msg.channel
|
strnd["channel"] = msg.channel
|
||||||
strnd["proprio"] = msg.nick
|
strnd["proprio"] = msg.nick
|
||||||
strnd["sender"] = msg.sender
|
strnd["start"] = msg.date
|
||||||
strnd["start"] = msg.tags["time"]
|
|
||||||
strnd["name"] = msg.cmds[1]
|
strnd["name"] = msg.cmds[1]
|
||||||
DATAS.addChild(strnd)
|
DATAS.addChild(strnd)
|
||||||
|
|
||||||
@ -84,7 +83,7 @@ def start_countdown(msg):
|
|||||||
result3 = re.match("(.*[^0-9])?([0-2]?[0-9]):([0-5]?[0-9])(:([0-5]?[0-9]))?", msg.cmds[2])
|
result3 = re.match("(.*[^0-9])?([0-2]?[0-9]):([0-5]?[0-9])(:([0-5]?[0-9]))?", msg.cmds[2])
|
||||||
if result2 is not None or result3 is not None:
|
if result2 is not None or result3 is not None:
|
||||||
try:
|
try:
|
||||||
now = msg.tags["time"]
|
now = msg.date
|
||||||
if result3 is None or result3.group(5) is None: sec = 0
|
if result3 is None or result3.group(5) is None: sec = 0
|
||||||
else: sec = int(result3.group(5))
|
else: sec = int(result3.group(5))
|
||||||
if result3 is None or result3.group(3) is None: minu = 0
|
if result3 is None or result3.group(3) is None: minu = 0
|
||||||
@ -109,7 +108,7 @@ def start_countdown(msg):
|
|||||||
raise IRCException("Mauvais format de date pour l'événement %s. Il n'a pas été créé." % msg.cmds[1])
|
raise IRCException("Mauvais format de date pour l'événement %s. Il n'a pas été créé." % msg.cmds[1])
|
||||||
|
|
||||||
elif result1 is not None and len(result1) > 0:
|
elif result1 is not None and len(result1) > 0:
|
||||||
strnd["end"] = msg.tags["time"]
|
strnd["end"] = msg.date
|
||||||
for (t, g) in result1:
|
for (t, g) in result1:
|
||||||
if g is None or g == "" or g == "m" or g == "M":
|
if g is None or g == "" or g == "m" or g == "M":
|
||||||
strnd["end"] += timedelta(minutes=int(t))
|
strnd["end"] += timedelta(minutes=int(t))
|
||||||
@ -131,11 +130,13 @@ def start_countdown(msg):
|
|||||||
save()
|
save()
|
||||||
if "end" in strnd:
|
if "end" in strnd:
|
||||||
return Response("%s commencé le %s et se terminera le %s." %
|
return Response("%s commencé le %s et se terminera le %s." %
|
||||||
(msg.cmds[1], msg.tags["time"].strftime("%A %d %B %Y à %H:%M:%S"),
|
(msg.cmds[1], msg.date.strftime("%A %d %B %Y à %H:%M:%S"),
|
||||||
strnd.getDate("end").strftime("%A %d %B %Y à %H:%M:%S")))
|
strnd.getDate("end").strftime("%A %d %B %Y à %H:%M:%S")),
|
||||||
|
nick=msg.frm)
|
||||||
else:
|
else:
|
||||||
return Response("%s commencé le %s"% (msg.cmds[1],
|
return Response("%s commencé le %s"% (msg.cmds[1],
|
||||||
msg.tags["time"].strftime("%A %d %B %Y à %H:%M:%S")))
|
msg.date.strftime("%A %d %B %Y à %H:%M:%S")),
|
||||||
|
nick=msg.frm)
|
||||||
|
|
||||||
@hook("cmd_hook", "end")
|
@hook("cmd_hook", "end")
|
||||||
@hook("cmd_hook", "forceend")
|
@hook("cmd_hook", "forceend")
|
||||||
@ -145,7 +146,7 @@ def end_countdown(msg):
|
|||||||
|
|
||||||
if msg.cmds[1] in DATAS.index:
|
if msg.cmds[1] in DATAS.index:
|
||||||
if DATAS.index[msg.cmds[1]]["proprio"] == msg.nick or (msg.cmds[0] == "forceend" and msg.is_owner):
|
if DATAS.index[msg.cmds[1]]["proprio"] == msg.nick or (msg.cmds[0] == "forceend" and msg.is_owner):
|
||||||
duration = countdown(msg.tags["time"] - DATAS.index[msg.cmds[1]].getDate("start"))
|
duration = countdown(msg.date - DATAS.index[msg.cmds[1]].getDate("start"))
|
||||||
del_event(DATAS.index[msg.cmds[1]]["id"])
|
del_event(DATAS.index[msg.cmds[1]]["id"])
|
||||||
DATAS.delChild(DATAS.index[msg.cmds[1]])
|
DATAS.delChild(DATAS.index[msg.cmds[1]])
|
||||||
save()
|
save()
|
||||||
@ -182,9 +183,9 @@ def parseanswer(msg):
|
|||||||
|
|
||||||
if DATAS.index[msg.cmds[0]].name == "strend":
|
if DATAS.index[msg.cmds[0]].name == "strend":
|
||||||
if DATAS.index[msg.cmds[0]].hasAttribute("end"):
|
if DATAS.index[msg.cmds[0]].hasAttribute("end"):
|
||||||
res.append_message("%s commencé il y a %s et se terminera dans %s." % (msg.cmds[0], countdown(msg.tags["time"] - DATAS.index[msg.cmds[0]].getDate("start")), countdown(DATAS.index[msg.cmds[0]].getDate("end") - msg.tags["time"])))
|
res.append_message("%s commencé il y a %s et se terminera dans %s." % (msg.cmds[0], countdown(msg.date - DATAS.index[msg.cmds[0]].getDate("start")), countdown(DATAS.index[msg.cmds[0]].getDate("end") - msg.date)))
|
||||||
else:
|
else:
|
||||||
res.append_message("%s commencé il y a %s." % (msg.cmds[0], countdown(msg.tags["time"] - DATAS.index[msg.cmds[0]].getDate("start"))))
|
res.append_message("%s commencé il y a %s." % (msg.cmds[0], countdown(msg.date - DATAS.index[msg.cmds[0]].getDate("start"))))
|
||||||
else:
|
else:
|
||||||
res.append_message(countdown_format(DATAS.index[msg.cmds[0]].getDate("start"), DATAS.index[msg.cmds[0]]["msg_before"], DATAS.index[msg.cmds[0]]["msg_after"]))
|
res.append_message(countdown_format(DATAS.index[msg.cmds[0]].getDate("start"), DATAS.index[msg.cmds[0]]["msg_before"], DATAS.index[msg.cmds[0]]["msg_after"]))
|
||||||
return res
|
return res
|
||||||
@ -222,7 +223,6 @@ def parseask(msg):
|
|||||||
evt["server"] = msg.server
|
evt["server"] = msg.server
|
||||||
evt["channel"] = msg.channel
|
evt["channel"] = msg.channel
|
||||||
evt["proprio"] = msg.nick
|
evt["proprio"] = msg.nick
|
||||||
evt["sender"] = msg.sender
|
|
||||||
evt["name"] = name.group(1)
|
evt["name"] = name.group(1)
|
||||||
evt["start"] = extDate
|
evt["start"] = extDate
|
||||||
evt["msg_after"] = msg_after
|
evt["msg_after"] = msg_after
|
||||||
@ -237,12 +237,12 @@ def parseask(msg):
|
|||||||
evt["server"] = msg.server
|
evt["server"] = msg.server
|
||||||
evt["channel"] = msg.channel
|
evt["channel"] = msg.channel
|
||||||
evt["proprio"] = msg.nick
|
evt["proprio"] = msg.nick
|
||||||
evt["sender"] = msg.sender
|
|
||||||
evt["name"] = name.group(1)
|
evt["name"] = name.group(1)
|
||||||
evt["msg_before"] = texts.group (2)
|
evt["msg_before"] = texts.group (2)
|
||||||
DATAS.addChild(evt)
|
DATAS.addChild(evt)
|
||||||
save()
|
save()
|
||||||
return Response("Nouvelle commande !%s ajoutée avec succès." % name.group(1))
|
return Response("Nouvelle commande !%s ajoutée avec succès." % name.group(1),
|
||||||
|
channel=msg.channel)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise IRCException("Veuillez indiquez les messages d'attente et d'après événement entre guillemets.")
|
raise IRCException("Veuillez indiquez les messages d'attente et d'après événement entre guillemets.")
|
||||||
|
@ -107,7 +107,7 @@ def search(site, term, ssl=False):
|
|||||||
web.striphtml(itm["snippet"].replace("<span class='searchmatch'>", "\x03\x02").replace("</span>", "\x03\x02")))
|
web.striphtml(itm["snippet"].replace("<span class='searchmatch'>", "\x03\x02").replace("</span>", "\x03\x02")))
|
||||||
|
|
||||||
|
|
||||||
@hook("in_PRIVMSG_cmd", "mediawiki")
|
@hook("cmd_hook", "mediawiki")
|
||||||
def cmd_mediawiki(msg):
|
def cmd_mediawiki(msg):
|
||||||
"""Read an article on a MediaWiki"""
|
"""Read an article on a MediaWiki"""
|
||||||
if len(msg.cmds) < 3:
|
if len(msg.cmds) < 3:
|
||||||
@ -118,7 +118,7 @@ def cmd_mediawiki(msg):
|
|||||||
channel=msg.receivers)
|
channel=msg.receivers)
|
||||||
|
|
||||||
|
|
||||||
@hook("in_PRIVMSG_cmd", "search_mediawiki")
|
@hook("cmd_hook", "search_mediawiki")
|
||||||
def cmd_srchmediawiki(msg):
|
def cmd_srchmediawiki(msg):
|
||||||
"""Search an article on a MediaWiki"""
|
"""Search an article on a MediaWiki"""
|
||||||
if len(msg.cmds) < 3:
|
if len(msg.cmds) < 3:
|
||||||
@ -132,7 +132,7 @@ def cmd_srchmediawiki(msg):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
@hook("in_PRIVMSG_cmd", "wikipedia")
|
@hook("cmd_hook", "wikipedia")
|
||||||
def cmd_wikipedia(msg):
|
def cmd_wikipedia(msg):
|
||||||
if len(msg.cmds) < 3:
|
if len(msg.cmds) < 3:
|
||||||
raise IRCException("indicate a lang and a term to search")
|
raise IRCException("indicate a lang and a term to search")
|
||||||
|
100
modules/more.py
100
modules/more.py
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from message import TextMessage, DirectAsk
|
||||||
from hooks import hook
|
from hooks import hook
|
||||||
|
|
||||||
nemubotversion = 3.4
|
nemubotversion = 3.4
|
||||||
@ -29,8 +30,7 @@ logger = logging.getLogger("nemubot.response")
|
|||||||
class Response:
|
class Response:
|
||||||
def __init__(self, message=None, channel=None, nick=None, server=None,
|
def __init__(self, message=None, channel=None, nick=None, server=None,
|
||||||
nomore="No more message", title=None, more="(suite) ",
|
nomore="No more message", title=None, more="(suite) ",
|
||||||
count=None, ctcp=False, shown_first_count=-1,
|
count=None, shown_first_count=-1, line_treat=None):
|
||||||
line_treat=None):
|
|
||||||
self.nomore = nomore
|
self.nomore = nomore
|
||||||
self.more = more
|
self.more = more
|
||||||
self.line_treat = line_treat
|
self.line_treat = line_treat
|
||||||
@ -38,12 +38,10 @@ class Response:
|
|||||||
self.server = server
|
self.server = server
|
||||||
self.messages = list()
|
self.messages = list()
|
||||||
self.alone = True
|
self.alone = True
|
||||||
self.is_ctcp = ctcp
|
|
||||||
if message is not None:
|
if message is not None:
|
||||||
self.append_message(message, shown_first_count=shown_first_count)
|
self.append_message(message, shown_first_count=shown_first_count)
|
||||||
self.elt = 0 # Next element to display
|
self.elt = 0 # Next element to display
|
||||||
|
|
||||||
self.sender = None
|
|
||||||
self.channel = channel
|
self.channel = channel
|
||||||
self.nick = nick
|
self.nick = nick
|
||||||
self.count = count
|
self.count = count
|
||||||
@ -59,14 +57,6 @@ class Response:
|
|||||||
else:
|
else:
|
||||||
return [ self.channel ]
|
return [ self.channel ]
|
||||||
|
|
||||||
def set_sender(self, sender):
|
|
||||||
if sender is None or sender.find("!") < 0:
|
|
||||||
if sender is not None:
|
|
||||||
logger.warn("Bad sender provided in Response, it will be ignored.", stack_info=True)
|
|
||||||
self.sender = None
|
|
||||||
else:
|
|
||||||
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:
|
if type(message) is str:
|
||||||
message = message.split('\n')
|
message = message.split('\n')
|
||||||
@ -120,13 +110,19 @@ class Response:
|
|||||||
if len(self.rawtitle) <= 0:
|
if len(self.rawtitle) <= 0:
|
||||||
self.rawtitle = None
|
self.rawtitle = None
|
||||||
|
|
||||||
def treat_ctcp(self, content):
|
|
||||||
if self.is_ctcp:
|
|
||||||
return "\x01" + content + "\x01"
|
|
||||||
else:
|
|
||||||
return content
|
|
||||||
|
|
||||||
def get_message(self):
|
def accept(self, visitor):
|
||||||
|
visitor.visit(self.next_response())
|
||||||
|
|
||||||
|
|
||||||
|
def next_response(self, maxlen=440):
|
||||||
|
if self.nick:
|
||||||
|
return DirectAsk(self.nick, self.get_message(maxlen - len(self.nick) - 2), server=None, to=self.receivers)
|
||||||
|
else:
|
||||||
|
return TextMessage(self.get_message(maxlen), server=None, to=self.receivers)
|
||||||
|
|
||||||
|
|
||||||
|
def get_message(self, maxlen):
|
||||||
if self.alone and len(self.messages) > 1:
|
if self.alone and len(self.messages) > 1:
|
||||||
self.alone = False
|
self.alone = False
|
||||||
|
|
||||||
@ -134,7 +130,7 @@ class Response:
|
|||||||
if hasattr(self.nomore, '__call__'):
|
if hasattr(self.nomore, '__call__'):
|
||||||
res = self.nomore(self)
|
res = self.nomore(self)
|
||||||
if res is None:
|
if res is None:
|
||||||
return self.treat_ctcp("No more message")
|
return "No more message"
|
||||||
elif isinstance(res, Response):
|
elif isinstance(res, Response):
|
||||||
self.__dict__ = res.__dict__
|
self.__dict__ = res.__dict__
|
||||||
elif isinstance(res, list):
|
elif isinstance(res, list):
|
||||||
@ -145,62 +141,59 @@ class Response:
|
|||||||
raise Exception("Type returned by nomore (%s) is not handled here." % type(res))
|
raise Exception("Type returned by nomore (%s) is not handled here." % type(res))
|
||||||
return self.get_message()
|
return self.get_message()
|
||||||
else:
|
else:
|
||||||
return self.treat_ctcp(self.nomore)
|
return self.nomore
|
||||||
|
|
||||||
if self.line_treat is not None and self.elt == 0:
|
if self.line_treat is not None and self.elt == 0:
|
||||||
self.messages[0] = self.line_treat(self.messages[0]).replace("\n", " ").strip()
|
self.messages[0] = self.line_treat(self.messages[0]).replace("\n", " ").strip()
|
||||||
|
|
||||||
msg = ""
|
msg = ""
|
||||||
if self.channel is not None and self.nick is not None:
|
|
||||||
msg += self.nick + ": "
|
|
||||||
|
|
||||||
if self.title is not None:
|
if self.title is not None:
|
||||||
if self.elt > 0:
|
if self.elt > 0:
|
||||||
msg += self.title + " " + self.more + ": "
|
msg += self.title + " " + self.more + ": "
|
||||||
else:
|
else:
|
||||||
msg += self.title + ": "
|
msg += self.title + ": "
|
||||||
|
|
||||||
if self.elt > 0:
|
elif self.elt > 0:
|
||||||
msg += "[…] "
|
msg += "[…] "
|
||||||
|
|
||||||
elts = self.messages[0][self.elt:]
|
elts = self.messages[0][self.elt:]
|
||||||
if isinstance(elts, list):
|
if isinstance(elts, list):
|
||||||
for e in elts:
|
for e in elts:
|
||||||
if len(msg) + len(e) > 430:
|
if len(msg) + len(e) > maxlen - 3:
|
||||||
msg += "[…]"
|
msg += "[…]"
|
||||||
self.alone = False
|
self.alone = False
|
||||||
return self.treat_ctcp(msg)
|
return msg
|
||||||
else:
|
else:
|
||||||
msg += e + ", "
|
msg += e + ", "
|
||||||
self.elt += 1
|
self.elt += 1
|
||||||
self.pop()
|
self.pop()
|
||||||
return self.treat_ctcp(msg[:len(msg)-2])
|
return msg[:len(msg)-2]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if len(elts.encode()) <= 432:
|
if len(elts.encode()) <= maxlen:
|
||||||
self.pop()
|
self.pop()
|
||||||
if self.count is not None:
|
if self.count is not None:
|
||||||
return self.treat_ctcp(msg + elts + (self.count % len(self.messages)))
|
return msg + elts + (self.count % len(self.messages))
|
||||||
else:
|
else:
|
||||||
return self.treat_ctcp(msg + elts)
|
return msg + elts
|
||||||
|
|
||||||
else:
|
else:
|
||||||
words = elts.split(' ')
|
words = elts.split(' ')
|
||||||
|
|
||||||
if len(words[0].encode()) > 432 - len(msg.encode()):
|
if len(words[0].encode()) > maxlen - len(msg.encode()):
|
||||||
self.elt += 432 - len(msg.encode())
|
self.elt += maxlen - len(msg.encode())
|
||||||
return self.treat_ctcp(msg + elts[:self.elt] + "[…]")
|
return msg + elts[:self.elt] + "[…]"
|
||||||
|
|
||||||
for w in words:
|
for w in words:
|
||||||
if len(msg.encode()) + len(w.encode()) > 431:
|
if len(msg.encode()) + len(w.encode()) >= maxlen:
|
||||||
msg += "[…]"
|
msg += "[…]"
|
||||||
self.alone = False
|
self.alone = False
|
||||||
return self.treat_ctcp(msg)
|
return msg
|
||||||
else:
|
else:
|
||||||
msg += w + " "
|
msg += w + " "
|
||||||
self.elt += len(w) + 1
|
self.elt += len(w) + 1
|
||||||
self.pop()
|
self.pop()
|
||||||
return self.treat_ctcp(msg)
|
return msg
|
||||||
|
|
||||||
|
|
||||||
SERVERS = dict()
|
SERVERS = dict()
|
||||||
@ -209,18 +202,17 @@ SERVERS = dict()
|
|||||||
def parseresponse(res):
|
def parseresponse(res):
|
||||||
# TODO: handle inter-bot communication NOMORE
|
# TODO: handle inter-bot communication NOMORE
|
||||||
# TODO: check that the response is not the one already saved
|
# TODO: check that the response is not the one already saved
|
||||||
rstr = res.get_message()
|
if isinstance(res, Response):
|
||||||
|
|
||||||
if not res.alone:
|
|
||||||
if res.server not in SERVERS:
|
if res.server not in SERVERS:
|
||||||
SERVERS[res.server] = dict()
|
SERVERS[res.server] = dict()
|
||||||
for receiver in res.receivers:
|
for receiver in res.receivers:
|
||||||
SERVERS[res.server][receiver] = res
|
if receiver in SERVERS[res.server]:
|
||||||
|
nw, bk = SERVERS[res.server][receiver]
|
||||||
ret = list()
|
else:
|
||||||
for channel in res.receivers:
|
nw, bk = None, None
|
||||||
ret.append("%s %s :%s" % ("NOTICE" if res.is_ctcp else "PRIVMSG", channel, rstr))
|
if nw != res:
|
||||||
return ret
|
SERVERS[res.server][receiver] = (res, bk)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
@hook("cmd_hook", "more")
|
@hook("cmd_hook", "more")
|
||||||
@ -228,9 +220,13 @@ def cmd_more(msg):
|
|||||||
"""Display next chunck of the message"""
|
"""Display next chunck of the message"""
|
||||||
res = list()
|
res = list()
|
||||||
if msg.server in SERVERS:
|
if msg.server in SERVERS:
|
||||||
for receiver in msg.receivers:
|
for receiver in msg.to_response:
|
||||||
if receiver in SERVERS[msg.server]:
|
if receiver in SERVERS[msg.server]:
|
||||||
res.append(SERVERS[msg.server][receiver])
|
nw, bk = SERVERS[msg.server][receiver]
|
||||||
|
if nw is not None and not nw.alone:
|
||||||
|
bk = nw
|
||||||
|
SERVERS[msg.server][receiver] = None, bk
|
||||||
|
res.append(bk)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
@ -239,8 +235,12 @@ def cmd_next(msg):
|
|||||||
"""Display the next information include in the message"""
|
"""Display the next information include in the message"""
|
||||||
res = list()
|
res = list()
|
||||||
if msg.server in SERVERS:
|
if msg.server in SERVERS:
|
||||||
for receiver in msg.receivers:
|
for receiver in msg.to_response:
|
||||||
if receiver in SERVERS[msg.server]:
|
if receiver in SERVERS[msg.server]:
|
||||||
SERVERS[msg.server][receiver].pop()
|
nw, bk = SERVERS[msg.server][receiver]
|
||||||
res.append(SERVERS[msg.server][receiver])
|
if nw is not None and not nw.alone:
|
||||||
|
bk = nw
|
||||||
|
SERVERS[msg.server][receiver] = None, bk
|
||||||
|
bk.pop()
|
||||||
|
res.append(bk)
|
||||||
return res
|
return res
|
||||||
|
@ -56,7 +56,7 @@ def del_site(msg):
|
|||||||
site = DATAS.index[url]
|
site = DATAS.index[url]
|
||||||
for a in site.getNodes("alert"):
|
for a in site.getNodes("alert"):
|
||||||
if a["channel"] == msg.channel:
|
if a["channel"] == msg.channel:
|
||||||
if not (msg.sender == a["sender"] or msg.is_owner):
|
if not (msg.frm == a["nick"] or msg.is_owner):
|
||||||
raise IRCException("vous ne pouvez pas supprimer cette URL.")
|
raise IRCException("vous ne pouvez pas supprimer cette URL.")
|
||||||
site.delChild(a)
|
site.delChild(a)
|
||||||
if not site.hasNode("alert"):
|
if not site.hasNode("alert"):
|
||||||
@ -82,7 +82,7 @@ def add_site(msg, diffType="diff"):
|
|||||||
raise IRCException("je ne peux pas surveiller cette URL")
|
raise IRCException("je ne peux pas surveiller cette URL")
|
||||||
|
|
||||||
alert = ModuleState("alert")
|
alert = ModuleState("alert")
|
||||||
alert["sender"] = msg.sender
|
alert["nick"] = msg.nick
|
||||||
alert["server"] = msg.server
|
alert["server"] = msg.server
|
||||||
alert["channel"] = msg.channel
|
alert["channel"] = msg.channel
|
||||||
alert["message"] = "{url} a changé !"
|
alert["message"] = "{url} a changé !"
|
||||||
|
@ -31,7 +31,6 @@ def start_watch(msg):
|
|||||||
w["server"] = msg.server
|
w["server"] = msg.server
|
||||||
w["channel"] = msg.channel
|
w["channel"] = msg.channel
|
||||||
w["proprio"] = msg.nick
|
w["proprio"] = msg.nick
|
||||||
w["sender"] = msg.sender
|
|
||||||
w["start"] = datetime.now(timezone.utc)
|
w["start"] = datetime.now(timezone.utc)
|
||||||
DATAS.addChild(w)
|
DATAS.addChild(w)
|
||||||
save()
|
save()
|
||||||
|
@ -8,11 +8,10 @@ from urllib.parse import quote
|
|||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
|
|
||||||
from hooks import hook
|
from hooks import hook
|
||||||
|
from message import TextMessage
|
||||||
|
|
||||||
nemubotversion = 3.4
|
nemubotversion = 3.4
|
||||||
|
|
||||||
from more import Response
|
|
||||||
|
|
||||||
def help_full():
|
def help_full():
|
||||||
return "!ycc [<url>]: with an argument, reduce the given <url> thanks to ycc.fr; without argument, reduce the last URL said on the current channel."
|
return "!ycc [<url>]: with an argument, reduce the given <url> thanks to ycc.fr; without argument, reduce the last URL said on the current channel."
|
||||||
|
|
||||||
@ -22,24 +21,28 @@ def gen_response(res, msg, srv):
|
|||||||
if res is None:
|
if res is None:
|
||||||
raise IRCException("la situation est embarassante, il semblerait que YCC soit down :(")
|
raise IRCException("la situation est embarassante, il semblerait que YCC soit down :(")
|
||||||
elif isinstance(res, str):
|
elif isinstance(res, str):
|
||||||
return Response("URL pour %s : %s" % (srv, res), msg.channel)
|
return TextMessage("URL pour %s : %s" % (srv, res), server=None, to=msg.to_response)
|
||||||
else:
|
else:
|
||||||
raise IRCException("mauvaise URL : %s" % srv)
|
raise IRCException("mauvaise URL : %s" % srv)
|
||||||
|
|
||||||
@hook("cmd_hook", "ycc")
|
@hook("cmd_hook", "ycc")
|
||||||
def cmd_ycc(msg):
|
def cmd_ycc(msg):
|
||||||
|
minify = list()
|
||||||
|
|
||||||
if len(msg.cmds) == 1:
|
if len(msg.cmds) == 1:
|
||||||
global LAST_URLS
|
global LAST_URLS
|
||||||
if msg.channel in LAST_URLS and len(LAST_URLS[msg.channel]) > 0:
|
if msg.channel in LAST_URLS and len(LAST_URLS[msg.channel]) > 0:
|
||||||
msg.cmds.append(LAST_URLS[msg.channel].pop())
|
minify.append(LAST_URLS[msg.channel].pop())
|
||||||
else:
|
else:
|
||||||
raise IRCException("je n'ai pas d'autre URL à réduire.")
|
raise IRCException("je n'ai pas d'autre URL à réduire.")
|
||||||
|
|
||||||
if len(msg.cmds) > 5:
|
if len(msg.cmds) > 5:
|
||||||
raise IRCException("je ne peux pas réduire autant d'URL d'un seul coup.")
|
raise IRCException("je ne peux pas réduire autant d'URL d'un seul coup.")
|
||||||
|
else:
|
||||||
|
minify += msg.cmds[1:]
|
||||||
|
|
||||||
res = list()
|
res = list()
|
||||||
for url in msg.cmds[1:]:
|
for url in minify:
|
||||||
o = urlparse(url, "http")
|
o = urlparse(url, "http")
|
||||||
if o.scheme != "":
|
if o.scheme != "":
|
||||||
snd_url = "http://ycc.fr/redirection/create/" + quote(url, "/:%@&=?")
|
snd_url = "http://ycc.fr/redirection/create/" + quote(url, "/:%@&=?")
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import imp
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import xmlparser
|
import xmlparser
|
||||||
@ -94,7 +95,8 @@ def load_file(filename, context):
|
|||||||
|
|
||||||
# Unexisting file, assume a name was passed, import the module!
|
# Unexisting file, assume a name was passed, import the module!
|
||||||
else:
|
else:
|
||||||
__import__(filename)
|
tt = __import__(filename)
|
||||||
|
imp.reload(tt)
|
||||||
|
|
||||||
|
|
||||||
def load(toks, context, prompt):
|
def load(toks, context, prompt):
|
||||||
|
131
server/IRC.py
131
server/IRC.py
@ -23,14 +23,15 @@ import time
|
|||||||
import shlex
|
import shlex
|
||||||
|
|
||||||
from channel import Channel
|
from channel import Channel
|
||||||
from message import Message
|
import message
|
||||||
import server
|
from message.printer.IRC import IRC as IRCPrinter
|
||||||
from server.socket import SocketServer
|
from server.socket import SocketServer
|
||||||
|
|
||||||
class IRCServer(SocketServer):
|
class IRC(SocketServer):
|
||||||
|
|
||||||
def __init__(self, node, nick, owner, realname):
|
def __init__(self, node, nick, owner, realname):
|
||||||
self.id = nick + "@" + node["host"] + ":" + node["port"]
|
self.id = nick + "@" + node["host"] + ":" + node["port"]
|
||||||
|
self.printer = IRCPrinter
|
||||||
SocketServer.__init__(self,
|
SocketServer.__init__(self,
|
||||||
node["host"],
|
node["host"],
|
||||||
node["port"],
|
node["port"],
|
||||||
@ -60,18 +61,18 @@ class IRCServer(SocketServer):
|
|||||||
# Register CTCP capabilities
|
# Register CTCP capabilities
|
||||||
self.ctcp_capabilities = dict()
|
self.ctcp_capabilities = dict()
|
||||||
|
|
||||||
def _ctcp_clientinfo(msg):
|
def _ctcp_clientinfo(msg, cmds):
|
||||||
"""Response to CLIENTINFO CTCP message"""
|
"""Response to CLIENTINFO CTCP message"""
|
||||||
return _ctcp_response(" ".join(self.ctcp_capabilities.keys()))
|
return " ".join(self.ctcp_capabilities.keys())
|
||||||
|
|
||||||
def _ctcp_dcc(msg):
|
def _ctcp_dcc(msg, cmds):
|
||||||
"""Response to DCC CTCP message"""
|
"""Response to DCC CTCP message"""
|
||||||
try:
|
try:
|
||||||
ip = srv.toIP(int(msg.cmds[3]))
|
ip = srv.toIP(int(cmds[3]))
|
||||||
port = int(msg.cmds[4])
|
port = int(cmds[4])
|
||||||
conn = DCC(srv, msg.sender)
|
conn = DCC(srv, msg.sender)
|
||||||
except:
|
except:
|
||||||
return _ctcp_response("ERRMSG invalid parameters provided as DCC CTCP request")
|
return "ERRMSG invalid parameters provided as DCC CTCP request"
|
||||||
|
|
||||||
self.logger.info("Receive DCC connection request from %s to %s:%d", conn.sender, ip, port)
|
self.logger.info("Receive DCC connection request from %s to %s:%d", conn.sender, ip, port)
|
||||||
|
|
||||||
@ -80,27 +81,20 @@ class IRCServer(SocketServer):
|
|||||||
conn.send_dcc("Hello %s!" % conn.nick)
|
conn.send_dcc("Hello %s!" % conn.nick)
|
||||||
else:
|
else:
|
||||||
self.logger.error("DCC: unable to connect to %s:%d", ip, port)
|
self.logger.error("DCC: unable to connect to %s:%d", ip, port)
|
||||||
return _ctcp_response("ERRMSG unable to connect to %s:%d" % (ip, port))
|
return "ERRMSG unable to connect to %s:%d" % (ip, port)
|
||||||
|
|
||||||
import bot
|
import bot
|
||||||
|
|
||||||
self.ctcp_capabilities["ACTION"] = lambda msg: print ("ACTION receive: %s" % msg.text)
|
self.ctcp_capabilities["ACTION"] = lambda msg, cmds: print ("ACTION receive: %s" % cmds)
|
||||||
self.ctcp_capabilities["CLIENTINFO"] = _ctcp_clientinfo
|
self.ctcp_capabilities["CLIENTINFO"] = _ctcp_clientinfo
|
||||||
#self.ctcp_capabilities["DCC"] = _ctcp_dcc
|
#self.ctcp_capabilities["DCC"] = _ctcp_dcc
|
||||||
self.ctcp_capabilities["FINGER"] = lambda msg: _ctcp_response(
|
self.ctcp_capabilities["FINGER"] = lambda msg, cmds: "VERSION nemubot v%s" % bot.__version__
|
||||||
"VERSION nemubot v%s" % bot.__version__)
|
self.ctcp_capabilities["NEMUBOT"] = lambda msg, cmds: "NEMUBOT %s" % bot.__version__
|
||||||
self.ctcp_capabilities["NEMUBOT"] = lambda msg: _ctcp_response(
|
self.ctcp_capabilities["PING"] = lambda msg, cmds: "PING %s" % " ".join(cmds[1:])
|
||||||
"NEMUBOT %s" % bot.__version__)
|
self.ctcp_capabilities["SOURCE"] = lambda msg, cmds: "SOURCE https://github.com/nemunaire/nemubot"
|
||||||
self.ctcp_capabilities["PING"] = lambda msg: _ctcp_response(
|
self.ctcp_capabilities["TIME"] = lambda msg, cmds: "TIME %s" % (datetime.now())
|
||||||
"PING %s" % " ".join(msg.cmds[1:]))
|
self.ctcp_capabilities["USERINFO"] = lambda msg, cmds: "USERINFO %s" % self.realname
|
||||||
self.ctcp_capabilities["SOURCE"] = lambda msg: _ctcp_response(
|
self.ctcp_capabilities["VERSION"] = lambda msg, cmds: "VERSION nemubot v%s" % bot.__version__
|
||||||
"SOURCE https://github.com/nemunaire/nemubot")
|
|
||||||
self.ctcp_capabilities["TIME"] = lambda msg: _ctcp_response(
|
|
||||||
"TIME %s" % (datetime.now()))
|
|
||||||
self.ctcp_capabilities["USERINFO"] = lambda msg: _ctcp_response(
|
|
||||||
"USERINFO %s" % self.realname)
|
|
||||||
self.ctcp_capabilities["VERSION"] = lambda msg: _ctcp_response(
|
|
||||||
"VERSION nemubot v%s" % bot.__version__)
|
|
||||||
|
|
||||||
self.logger.debug("CTCP capabilities setup: %s", ", ".join(self.ctcp_capabilities))
|
self.logger.debug("CTCP capabilities setup: %s", ", ".join(self.ctcp_capabilities))
|
||||||
|
|
||||||
@ -190,6 +184,18 @@ class IRCServer(SocketServer):
|
|||||||
self.write("JOIN " + msg.decode(msg.params[1]))
|
self.write("JOIN " + msg.decode(msg.params[1]))
|
||||||
self.hookscmd["INVITE"] = _on_invite
|
self.hookscmd["INVITE"] = _on_invite
|
||||||
|
|
||||||
|
# Handle CTCP requests
|
||||||
|
def _on_ctcp(msg):
|
||||||
|
if len(msg.params) != 2 or not msg.is_ctcp: return
|
||||||
|
cmds = msg.decode(msg.params[1][1:len(msg.params[1])-1]).split(' ')
|
||||||
|
if cmds[0] in self.ctcp_capabilities:
|
||||||
|
res = self.ctcp_capabilities[cmds[0]](msg, cmds)
|
||||||
|
else:
|
||||||
|
res = "ERRMSG Unknown or unimplemented CTCP request"
|
||||||
|
if res is not None:
|
||||||
|
self.write("NOTICE %s :\x01%s\x01" % (msg.nick, res))
|
||||||
|
self.hookscmd["PRIVMSG"] = _on_ctcp
|
||||||
|
|
||||||
|
|
||||||
def _open(self):
|
def _open(self):
|
||||||
if SocketServer._open(self):
|
if SocketServer._open(self):
|
||||||
@ -215,31 +221,9 @@ class IRCServer(SocketServer):
|
|||||||
if msg.cmd in self.hookscmd:
|
if msg.cmd in self.hookscmd:
|
||||||
self.hookscmd[msg.cmd](msg)
|
self.hookscmd[msg.cmd](msg)
|
||||||
|
|
||||||
else:
|
mes = msg.to_message(self)
|
||||||
mes = msg.to_message()
|
if mes is not None:
|
||||||
mes.raw = msg.raw
|
yield mes
|
||||||
|
|
||||||
if hasattr(mes, "receivers"):
|
|
||||||
# Private message: prepare response
|
|
||||||
for i in range(len(mes.receivers)):
|
|
||||||
if mes.receivers[i] == self.nick:
|
|
||||||
mes.receivers[i] = mes.nick
|
|
||||||
|
|
||||||
if (mes.cmd == "PRIVMSG" or mes.cmd == "NOTICE") and mes.is_ctcp:
|
|
||||||
if mes.cmds[0] in self.ctcp_capabilities:
|
|
||||||
res = self.ctcp_capabilities[mes.cmds[0]](mes)
|
|
||||||
else:
|
|
||||||
res = _ctcp_response("ERRMSG Unknown or unimplemented CTCP request")
|
|
||||||
if res is not None:
|
|
||||||
res = res % mes.nick
|
|
||||||
self.write(res)
|
|
||||||
|
|
||||||
else:
|
|
||||||
yield mes
|
|
||||||
|
|
||||||
|
|
||||||
def _ctcp_response(msg):
|
|
||||||
return "NOTICE %%s :\x01%s\x01" % msg
|
|
||||||
|
|
||||||
|
|
||||||
mgx = re.compile(b'''^(?:@(?P<tags>[^ ]+)\ )?
|
mgx = re.compile(b'''^(?:@(?P<tags>[^ ]+)\ )?
|
||||||
@ -303,6 +287,11 @@ class IRCMessage:
|
|||||||
self.tags[key] = value
|
self.tags[key] = value
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_ctcp(self):
|
||||||
|
return self.cmd == "PRIVMSG" and len(self.params) == 2 and len(self.params[1]) > 1 and (self.params[1][0] == 0x01 or self.params[1][1] == 0x01)
|
||||||
|
|
||||||
|
|
||||||
def decode(self, s):
|
def decode(self, s):
|
||||||
"""Decode the content string usign a specific encoding"""
|
"""Decode the content string usign a specific encoding"""
|
||||||
if isinstance(s, bytes):
|
if isinstance(s, bytes):
|
||||||
@ -313,8 +302,6 @@ class IRCMessage:
|
|||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def to_message(self):
|
|
||||||
return Message(self)
|
|
||||||
|
|
||||||
def to_irc_string(self, client=True):
|
def to_irc_string(self, client=True):
|
||||||
"""Pretty print the message to close to original input string
|
"""Pretty print the message to close to original input string
|
||||||
@ -336,3 +323,43 @@ class IRCMessage:
|
|||||||
res += " :" + self.decode(self.params[-1])
|
res += " :" + self.decode(self.params[-1])
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def to_message(self, srv):
|
||||||
|
if self.cmd == "PRIVMSG" or self.cmd == "NOTICE":
|
||||||
|
|
||||||
|
receivers = self.decode(self.params[0]).split(',')
|
||||||
|
|
||||||
|
common_args = {
|
||||||
|
"server": srv.id,
|
||||||
|
"date": self.tags["time"],
|
||||||
|
"to": receivers,
|
||||||
|
"to_response": [r if r != srv.nick else self.nick for r in receivers],
|
||||||
|
"frm": self.nick
|
||||||
|
}
|
||||||
|
|
||||||
|
# If CTCP, remove 0x01
|
||||||
|
if self.is_ctcp:
|
||||||
|
text = self.decode(self.params[1][1:len(self.params[1])-1])
|
||||||
|
else:
|
||||||
|
text = self.decode(self.params[1])
|
||||||
|
|
||||||
|
if len(text) > 1 and text[0] == '!':
|
||||||
|
text = text[1:].strip()
|
||||||
|
|
||||||
|
# Split content by words
|
||||||
|
try:
|
||||||
|
args = shlex.split(text)
|
||||||
|
except ValueError:
|
||||||
|
args = text.split(' ')
|
||||||
|
|
||||||
|
return message.Command(cmd=args[0], args=args[1:], **common_args)
|
||||||
|
|
||||||
|
elif text.find(srv.nick) == 0 and len(text) > len(srv.nick) + 2 and text[len(srv.nick)] == ":":
|
||||||
|
text = text[len(srv.nick) + 1:].strip()
|
||||||
|
return message.DirectAsk(designated=srv.nick, message=text, **common_args)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return message.TextMessage(message=text, **common_args)
|
||||||
|
|
||||||
|
return None
|
||||||
|
@ -89,3 +89,18 @@ class AbstractServer(io.IOBase):
|
|||||||
def exception(self):
|
def exception(self):
|
||||||
"""Exception occurs in fd"""
|
"""Exception occurs in fd"""
|
||||||
print("Unhandle file descriptor exception on server " + self.id)
|
print("Unhandle file descriptor exception on server " + self.id)
|
||||||
|
|
||||||
|
|
||||||
|
def send_response(self, response):
|
||||||
|
"""Send a formated Message class"""
|
||||||
|
if response is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
elif isinstance(response, list):
|
||||||
|
for r in response:
|
||||||
|
self.send_response(r)
|
||||||
|
|
||||||
|
else:
|
||||||
|
vprnt = self.printer()
|
||||||
|
response.accept(vprnt)
|
||||||
|
self.write(vprnt.pp)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user