diff --git a/bot.py b/bot.py index 786be74..7c5e557 100644 --- a/bot.py +++ b/bot.py @@ -64,10 +64,6 @@ class Bot(threading.Thread): self.modules_paths = modules_paths self.data_path = data_path - # Save various informations - self.ctcp_capabilities = dict() - self.init_ctcp_capabilities() - # Keep global context: servers and modules self.servers = dict() self.modules = dict() @@ -147,53 +143,6 @@ class Bot(threading.Thread): self.receive_message(r, i) - def init_ctcp_capabilities(self): - """Reset existing CTCP capabilities to default one""" - - def _ctcp_clientinfo(srv, msg): - """Response to CLIENTINFO CTCP message""" - return _ctcp_response(msg.sender, - " ".join(self.ctcp_capabilities.keys())) - - def _ctcp_dcc(srv, msg): - """Response to DCC CTCP message""" - try: - ip = srv.toIP(int(msg.cmds[3])) - port = int(msg.cmds[4]) - conn = DCC(srv, msg.sender) - except: - return _ctcp_response(msg.sender, "ERRMSG invalid parameters provided as DCC CTCP request") - - logger.info("Receive DCC connection request from %s to %s:%d", conn.sender, ip, port) - - if conn.accept_user(ip, port): - srv.dcc_clients[conn.sender] = conn - conn.send_dcc("Hello %s!" % conn.nick) - else: - logger.error("DCC: unable to connect to %s:%d", ip, port) - return _ctcp_response(msg.sender, "ERRMSG unable to connect to %s:%d" % (ip, port)) - - self.ctcp_capabilities["ACTION"] = lambda srv, msg: print ("ACTION receive: %s" % msg.text) - self.ctcp_capabilities["CLIENTINFO"] = _ctcp_clientinfo - self.ctcp_capabilities["DCC"] = _ctcp_dcc - self.ctcp_capabilities["FINGER"] = lambda srv, msg: _ctcp_response( - msg.sender, "VERSION nemubot v%s" % __version__) - self.ctcp_capabilities["NEMUBOT"] = lambda srv, msg: _ctcp_response( - msg.sender, "NEMUBOT %s" % __version__) - self.ctcp_capabilities["PING"] = lambda srv, msg: _ctcp_response( - msg.sender, "PING %s" % " ".join(msg.cmds[1:])) - self.ctcp_capabilities["SOURCE"] = lambda srv, msg: _ctcp_response( - msg.sender, "SOURCE https://github.com/nemunaire/nemubot") - self.ctcp_capabilities["TIME"] = lambda srv, msg: _ctcp_response( - msg.sender, "TIME %s" % (datetime.now())) - self.ctcp_capabilities["USERINFO"] = lambda srv, msg: _ctcp_response( - msg.sender, "USERINFO %s" % srv.realname) - self.ctcp_capabilities["VERSION"] = lambda srv, msg: _ctcp_response( - msg.sender, "VERSION nemubot v%s" % __version__) - - logger.debug("CTCP capabilities setup: %s", ", ".join(self.ctcp_capabilities)) - - # Events methods def add_event(self, evt, eid=None, module_src=None): @@ -447,13 +396,9 @@ class Bot(threading.Thread): store.remove(hook) -def _ctcp_response(sndr, msg): - return response.Response(sndr, msg, ctcp=True) - def hotswap(bak): bak.stop = True new = Bot(str(bak.ip), bak.modules_paths, bak.data_path) - new.ctcp_capabilities = bak.ctcp_capabilities new.servers = bak.servers new.modules = bak.modules new.modules_configuration = bak.modules_configuration diff --git a/consumer.py b/consumer.py index 679b8cc..a2390d3 100644 --- a/consumer.py +++ b/consumer.py @@ -114,31 +114,13 @@ class MessageConsumer: self.responses = list() for msg in self.msgs: - # TODO: should be placed in server hooks - if msg.cmd == "001": - if hasattr(self.srv, "_on_connect"): - self.srv._on_connect() - - elif msg.cmd == "ERROR": - self.srv.close() - - elif (msg.cmd == "CAP" and - hasattr(self.srv, "_on_caps_ls") and - self.srv._on_caps_ls(msg)): - pass - - elif msg.cmd == "PING": - self.srv.write("%s :%s" % ("PONG", msg.params[0])) - - else: - for h in hm.get_hooks("in", msg.cmd, msg.qual): - - if h.match(message=msg, server=self.srv): - res = h.run(msg) - if isinstance(res, list): - self.responses += res - elif res is not None: - self.responses.append(res) + for h in hm.get_hooks("in", msg.cmd, msg.qual): + if h.match(message=msg, server=self.srv): + res = h.run(msg) + if isinstance(res, list): + self.responses += res + elif res is not None: + self.responses.append(res) def post_treat(self, hm): diff --git a/server/IRC.py b/server/IRC.py index 3c4e092..ee90eec 100644 --- a/server/IRC.py +++ b/server/IRC.py @@ -18,6 +18,7 @@ from datetime import datetime +import bot from message import Message import server from server.socket import SocketServer @@ -31,10 +32,11 @@ class IRCServer(SocketServer): node["password"], node.hasAttribute("ssl") and node["ssl"].lower() == "true") - self.nick = nick - self.owner = owner + self.nick = nick + self.owner = owner self.realname = realname - self.id = "TODO" + self.id = nick + "@" + node["host"] + ":" + node["port"] + if node.hasAttribute("caps"): if node["caps"].lower() == "no": @@ -44,18 +46,75 @@ class IRCServer(SocketServer): else: self.capabilities = list() - def _on_connect(): + # Register CTCP capabilities + self.ctcp_capabilities = dict() + + def _ctcp_clientinfo(msg): + """Response to CLIENTINFO CTCP message""" + return _ctcp_response(msg.sender, + " ".join(self.ctcp_capabilities.keys())) + + def _ctcp_dcc(msg): + """Response to DCC CTCP message""" + try: + ip = srv.toIP(int(msg.cmds[3])) + port = int(msg.cmds[4]) + conn = DCC(srv, msg.sender) + except: + return _ctcp_response(msg.sender, "ERRMSG invalid parameters provided as DCC CTCP request") + + self.logger.info("Receive DCC connection request from %s to %s:%d", conn.sender, ip, port) + + if conn.accept_user(ip, port): + srv.dcc_clients[conn.sender] = conn + conn.send_dcc("Hello %s!" % conn.nick) + else: + self.logger.error("DCC: unable to connect to %s:%d", ip, port) + return _ctcp_response(msg.sender, "ERRMSG unable to connect to %s:%d" % (ip, port)) + + self.ctcp_capabilities["ACTION"] = lambda msg: print ("ACTION receive: %s" % msg.text) + self.ctcp_capabilities["CLIENTINFO"] = _ctcp_clientinfo + #self.ctcp_capabilities["DCC"] = _ctcp_dcc + self.ctcp_capabilities["FINGER"] = lambda msg: _ctcp_response( + msg.sender, "VERSION nemubot v%s" % bot.__version__) + self.ctcp_capabilities["NEMUBOT"] = lambda msg: _ctcp_response( + msg.sender, "NEMUBOT %s" % bot.__version__) + self.ctcp_capabilities["PING"] = lambda msg: _ctcp_response( + msg.sender, "PING %s" % " ".join(msg.cmds[1:])) + self.ctcp_capabilities["SOURCE"] = lambda msg: _ctcp_response( + msg.sender, "SOURCE https://github.com/nemunaire/nemubot") + self.ctcp_capabilities["TIME"] = lambda msg: _ctcp_response( + msg.sender, "TIME %s" % (datetime.now())) + self.ctcp_capabilities["USERINFO"] = lambda msg: _ctcp_response( + msg.sender, "USERINFO %s" % self.realname) + self.ctcp_capabilities["VERSION"] = lambda msg: _ctcp_response( + msg.sender, "VERSION nemubot v%s" % bot.__version__) + + self.logger.debug("CTCP capabilities setup: %s", ", ".join(self.ctcp_capabilities)) + + # Register hooks on some IRC CMD + self.hookscmd = dict() + + def _on_ping(msg): + self.write("%s :%s" % ("PONG", msg.params[0])) + self.hookscmd["PING"] = _on_ping + + def _on_connect(msg): # First, JOIN some channels for chn in node.getNodes("channel"): if chn["password"] is not None: self.write("JOIN %s %s" % (chn["name"], chn["password"])) else: self.write("JOIN %s" % chn["name"]) - self._on_connect = _on_connect + self.hookscmd["001"] = _on_connect - def _on_caps_ls(msg): + def _on_error(msg): + self.close() + self.hookscmd["ERROR"] = _on_error + + def _on_cap(msg): if len(msg.params) != 3 or msg.params[1] != "LS": - return False + return server_caps = msg.params[2].split(" ") for cap in self.capabilities: if cap not in server_caps: @@ -63,7 +122,7 @@ class IRCServer(SocketServer): if len(self.capabilities) > 0: self.write("CAP REQ :" + " ".join(self.capabilities)) self.write("CAP END") - self._on_caps_ls = _on_caps_ls + self.hookscmd["CAP"] = _on_cap def _open(self): @@ -94,4 +153,24 @@ class IRCServer(SocketServer): def read(self): for line in SocketServer.read(self): - yield Message(line, datetime.now()) + msg = Message(line, datetime.now()) + + if msg.cmd in self.hookscmd: + self.hookscmd[msg.cmd](msg) + + elif msg.cmd == "PRIVMSG" and msg.is_ctcp: + if msg.cmds[0] in self.ctcp_capabilities: + res = self.ctcp_capabilities[msg.cmds[0]](msg) + else: + res = _ctcp_response(msg.sender, "ERRMSG Unknown or unimplemented CTCP request") + if res is not None: + self.send_response(res) + + else: + yield msg + + +from response import Response + +def _ctcp_response(sndr, msg): + return Response(sndr, msg, ctcp=True)