diff --git a/bot.py b/bot.py
index b041e6c..1c16278 100644
--- a/bot.py
+++ b/bot.py
@@ -36,11 +36,13 @@ from networkbot import NetworkBot
logger = logging.getLogger("nemubot.bot")
+
class Bot(threading.Thread):
"""Class containing the bot context and ensuring key goals"""
- def __init__(self, ip="127.0.0.1", modules_paths=list(), data_path="./datas/"):
+ def __init__(self, ip="127.0.0.1", modules_paths=list(),
+ data_path="./datas/"):
"""Initialize the bot context
Keyword arguments:
@@ -71,6 +73,7 @@ class Bot(threading.Thread):
# Own hooks
self.hooks = HooksManager()
+
def in_ping(msg):
if re.match("^ *(m[' ]?entends?[ -]+tu|h?ear me|do you copy|ping)", msg.message, re.I) is not None:
return msg.respond("pong")
@@ -477,9 +480,9 @@ def reload():
prompt.reload()
import server
- rl,wl,xl = server._rlist,server._wlist,server._xlist
+ rl, wl, xl = server._rlist, server._wlist, server._xlist
imp.reload(server)
- server._rlist,server._wlist,server._xlist = rl,wl,xl
+ server._rlist, server._wlist, server._xlist = rl, wl, xl
server.reload()
diff --git a/channel.py b/channel.py
index fb570a2..4223370 100644
--- a/channel.py
+++ b/channel.py
@@ -18,8 +18,19 @@
import logging
+
class Channel:
+
+ """A chat room"""
+
def __init__(self, name, password=None):
+ """Initialize the channel
+
+ Arguments:
+ name -- the channel name
+ password -- the optional password use to join it
+ """
+
self.name = name
self.password = password
self.people = dict()
@@ -27,6 +38,13 @@ class Channel:
self.logger = logging.getLogger("nemubot.channel." + name)
def treat(self, cmd, msg):
+ """Treat a incoming IRC command
+
+ Arguments:
+ cmd -- the command
+ msg -- the whole message
+ """
+
if cmd == "353":
self.parse353(msg)
elif cmd == "332":
@@ -42,18 +60,35 @@ class Channel:
elif cmd == "TOPIC":
self.topic = self.text
- def join(self, nick, level = 0):
- """Someone join the channel"""
+ def join(self, nick, level=0):
+ """Someone join the channel
+
+ Argument:
+ nick -- nickname of the user joining the channel
+ level -- authorization level of the user
+ """
+
self.logger.debug("%s join", nick)
self.people[nick] = level
def chtopic(self, newtopic):
- """Send command to change the topic"""
+ """Send command to change the topic
+
+ Arguments:
+ newtopic -- the new topic of the channel
+ """
+
self.srv.send_msg(self.name, newtopic, "TOPIC")
self.topic = newtopic
def nick(self, oldnick, newnick):
- """Someone change his nick"""
+ """Someone change his nick
+
+ Arguments:
+ oldnick -- the previous nick of the user
+ newnick -- the new nick of the user
+ """
+
if oldnick in self.people:
self.logger.debug("%s switch nick to %s on", oldnick, newnick)
lvl = self.people[oldnick]
@@ -61,12 +96,22 @@ class Channel:
self.people[newnick] = lvl
def part(self, nick):
- """Someone leave the channel"""
+ """Someone leave the channel
+
+ Argument:
+ nick -- name of the user that leave
+ """
+
if nick in self.people:
self.logger.debug("%s has left", nick)
del self.people[nick]
def mode(self, msg):
+ """Channel or user mode change
+
+ Argument:
+ msg -- the whole message
+ """
if msg.text[0] == "-k":
self.password = ""
elif msg.text[0] == "+k":
@@ -88,9 +133,21 @@ class Channel:
self.people[msg.nick] &= ~1
def parse332(self, msg):
+ """Parse RPL_TOPIC message
+
+ Argument:
+ msg -- the whole message
+ """
+
self.topic = msg.text
def parse353(self, msg):
+ """Parse RPL_ENDOFWHO message
+
+ Argument:
+ msg -- the whole message
+ """
+
for p in msg.text:
p = p.decode()
if p[0] == "@":
diff --git a/consumer.py b/consumer.py
index 9cf2638..1aeb01c 100644
--- a/consumer.py
+++ b/consumer.py
@@ -23,6 +23,7 @@ import threading
logger = logging.getLogger("nemubot.consumer")
+
class MessageConsumer:
"""Store a message before treating"""
@@ -77,7 +78,7 @@ class MessageConsumer:
new_msg.append(res)
msg = None
break
- elif res is None or res == False:
+ elif res is None or res is False:
msg = None
break
if msg is not None:
@@ -134,7 +135,7 @@ class MessageConsumer:
new_msg.append(res)
msg = None
break
- elif res is None or res == False:
+ elif res is None or res is False:
msg = None
break
else:
@@ -160,7 +161,8 @@ class MessageConsumer:
if self.responses is not None and len(self.responses) > 0:
self.post_treat(context.hooks)
except:
- logger.exception("Error occurred during the processing of the %s: %s", type(self.msgs[0]).__name__, self.msgs[0])
+ logger.exception("Error occurred during the processing of the %s: "
+ "%s", type(self.msgs[0]).__name__, self.msgs[0])
for res in self.responses:
to_server = None
@@ -180,6 +182,7 @@ class MessageConsumer:
# Sent the message only if treat_post authorize it
to_server.send_response(res)
+
class EventConsumer:
"""Store a event before treating"""
def __init__(self, evt, timeout=20):
@@ -198,7 +201,8 @@ class EventConsumer:
context.add_event(self.evt, eid=self.evt.id)
# Or remove reference of this event
- elif hasattr(self.evt, "module_src") and self.evt.module_src is not None:
+ elif (hasattr(self.evt, "module_src") and
+ self.evt.module_src is not None):
self.evt.module_src.REGISTERED_EVENTS.remove(self.evt.id)
diff --git a/exception.py b/exception.py
index 92229bf..ccbe362 100644
--- a/exception.py
+++ b/exception.py
@@ -18,6 +18,7 @@
from message import TextMessage, DirectAsk
+
class IRCException(Exception):
def __init__(self, message, personnal=True):
@@ -27,6 +28,8 @@ class IRCException(Exception):
def fill_response(self, msg):
if self.personnal:
- return DirectAsk(msg.frm, self.message, server=msg.server, to=msg.to_response)
+ return DirectAsk(msg.frm, self.message,
+ server=msg.server, to=msg.to_response)
else:
- return TextMessage(self.message, server=msg.server, to=msg.to_response)
+ return TextMessage(self.message,
+ server=msg.server, to=msg.to_response)
diff --git a/hooks/__init__.py b/hooks/__init__.py
index 7c1e841..288ea3a 100644
--- a/hooks/__init__.py
+++ b/hooks/__init__.py
@@ -20,6 +20,7 @@ import imp
from exception import IRCException
+
def call_game(call, *args, **kargs):
"""TODO"""
l = list()
@@ -70,6 +71,7 @@ from hooks.messagehook import MessageHook
last_registered = []
+
def hook(store, *args, **kargs):
"""Function used as a decorator for module loading"""
def sec(call):
diff --git a/hooks/manager.py b/hooks/manager.py
index 6db0989..2331d68 100644
--- a/hooks/manager.py
+++ b/hooks/manager.py
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
+
class HooksManager:
"""Class to manage hooks"""
diff --git a/hooks/messagehook.py b/hooks/messagehook.py
index 1ab09c6..e0600c1 100644
--- a/hooks/messagehook.py
+++ b/hooks/messagehook.py
@@ -22,6 +22,7 @@ from exception import IRCException
import hooks
import message
+
class MessageHook(hooks.AbstractHook):
"""Class storing hook information, specialized for a generic Message"""
@@ -52,9 +53,9 @@ class MessageHook(hooks.AbstractHook):
def is_matching(self, strcmp, receivers=list(), server=None):
"""Test if the current hook correspond to the message"""
- if (server is None or self.server is None or self.server == server
- ) and ((self.name is None or strcmp == self.name) and (
- self.regexp is None or re.match(self.regexp, strcmp))):
+ if ((server is None or self.server is None or self.server == server)
+ and ((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:
diff --git a/message/__init__.py b/message/__init__.py
index f3a107a..67ce5f7 100644
--- a/message/__init__.py
+++ b/message/__init__.py
@@ -19,6 +19,7 @@
from datetime import datetime, timezone
import imp
+
class AbstractMessage:
"""This class represents an abstract message"""
@@ -37,10 +38,12 @@ class AbstractMessage:
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
+ 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
- self.frm_owner = False # Filled later, in consumer
+ self.frm_owner = False # Filled later, in consumer
@property
@@ -129,7 +132,6 @@ class DirectAsk(TextMessage):
self.designated = designated
-
def respond(self, message):
return DirectAsk(self.frm,
message,
diff --git a/message/printer/IRC.py b/message/printer/IRC.py
index 83155ac..1b4670f 100644
--- a/message/printer/IRC.py
+++ b/message/printer/IRC.py
@@ -19,6 +19,7 @@
from message import TextMessage
from message.visitor import AbstractVisitor
+
class IRC(AbstractVisitor):
def __init__(self):
diff --git a/message/printer/__init__.py b/message/printer/__init__.py
index e9bf5a5..215b464 100644
--- a/message/printer/__init__.py
+++ b/message/printer/__init__.py
@@ -18,6 +18,7 @@
import imp
+
def reload():
import message.printer.IRC
imp.reload(message.printer.IRC)
diff --git a/message/visitor.py b/message/visitor.py
index 7328254..1041a45 100644
--- a/message/visitor.py
+++ b/message/visitor.py
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
+
class AbstractVisitor:
def visit(self, obj):
diff --git a/nemubot.py b/nemubot.py
index f3a29cf..1b2121f 100755
--- a/nemubot.py
+++ b/nemubot.py
@@ -31,7 +31,8 @@ if __name__ == "__main__":
# Setup loggin interface
logger = logging.getLogger("nemubot")
- formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s %(message)s')
+ formatter = logging.Formatter(
+ '%(asctime)s %(name)s %(levelname)s %(message)s')
ch = logging.StreamHandler()
ch.setFormatter(formatter)
@@ -52,7 +53,7 @@ if __name__ == "__main__":
# Register the hook for futur import
sys.meta_path.append(importer.ModuleFinder(context, prmpt))
- #Add modules dir path
+ # Add modules dir path
if os.path.isdir("./modules/"):
context.add_modules_path(
os.path.realpath(os.path.abspath("./modules/")))
@@ -82,8 +83,9 @@ if __name__ == "__main__":
bot.__version__)
context.start()
except:
- logger.exception("\033[1;31mUnable to reload the prompt due to errors.\033[0"
- "m Fix them before trying to reload the prompt.")
+ logger.exception("\033[1;31mUnable to reload the prompt due to "
+ "errors.\033[0m Fix them before trying to reload "
+ "the prompt.")
context.quit()
print("\nWaiting for other threads shuts down...")
diff --git a/prompt/__init__.py b/prompt/__init__.py
index 0233e9c..d0ad4b8 100644
--- a/prompt/__init__.py
+++ b/prompt/__init__.py
@@ -25,7 +25,9 @@ import traceback
from . import builtins
+
class Prompt:
+
def __init__(self, hc=dict(), hl=dict()):
self.selectedServer = None
@@ -56,8 +58,8 @@ class Prompt:
return ret
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
- sys.stderr.write (traceback.format_exception_only(
- exc_type, exc_value)[0])
+ sys.stderr.write(traceback.format_exception_only(exc_type,
+ exc_value)[0])
return ret
def exec_cmd(self, toks, context):
@@ -65,7 +67,7 @@ class Prompt:
if toks[0] in builtins.CAPS:
return builtins.CAPS[toks[0]](toks, context, self)
elif toks[0] in self.HOOKS_CAPS:
- (f,d) = self.HOOKS_CAPS[toks[0]]
+ f, d = self.HOOKS_CAPS[toks[0]]
return f(d, toks, context, self)
else:
print("Unknown command: `%s'" % toks[0])
@@ -90,7 +92,8 @@ class Prompt:
ret = self.exec_cmd(toks, context)
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
- traceback.print_exception(exc_type, exc_value, exc_traceback)
+ traceback.print_exception(exc_type, exc_value,
+ exc_traceback)
except KeyboardInterrupt:
print("")
except EOFError:
@@ -102,6 +105,7 @@ class Prompt:
def hotswap(prompt):
return Prompt(prompt.HOOKS_CAPS, prompt.HOOKS_LIST)
+
def reload():
import prompt.builtins
imp.reload(prompt.builtins)
diff --git a/prompt/builtins.py b/prompt/builtins.py
index 2af2e18..f0e5c68 100644
--- a/prompt/builtins.py
+++ b/prompt/builtins.py
@@ -25,6 +25,13 @@ logger = logging.getLogger("nemubot.prompt.builtins")
from server.IRC import IRC as IRCServer
import xmlparser
+
+def get_boolean(d, k):
+ return (k in d and
+ mod["autoload"].lower() != "false" and
+ mod["autoload"].lower() != "off")
+
+
def end(toks, context, prompt):
"""Quit the prompt for reload or exit"""
if toks[0] == "refresh":
@@ -52,7 +59,7 @@ def liste(toks, context, prompt):
if len(context.modules) == 0:
print (" > No module loaded")
elif l in prompt.HOOKS_LIST:
- (f,d) = prompt.HOOKS_LIST[l]
+ f, d = prompt.HOOKS_LIST[l]
f(d, context, prompt)
else:
print (" Unknown list `%s'" % l)
@@ -65,8 +72,7 @@ def load_file(filename, context):
config = xmlparser.parse_file(filename)
# This is a true nemubot configuration file, load it!
- if (config.getName() == "botconfig"
- or config.getName() == "nemubotconfig"):
+ if config.getName() == "nemubotconfig":
# Preset each server in this file
for server in config.getNodes("server"):
opts = {
@@ -78,7 +84,8 @@ def load_file(filename, context):
}
# Optional keyword arguments
- for optional_opt in [ "port", "username", "realname", "password", "encoding", "caps" ]:
+ for optional_opt in [ "port", "username", "realname",
+ "password", "encoding", "caps" ]:
if server.hasAttribute(optional_opt):
opts[optional_opt] = server[optional_opt]
elif optional_opt in config:
@@ -94,11 +101,14 @@ def load_file(filename, context):
if server.hasNode("channel"):
opts["channels"] = list()
for chn in server.getNodes("channel"):
- opts["channels"].append((chn["name"], chn["password"]) if chn["password"] is not None else chn["name"])
+ opts["channels"].append((chn["name"], chn["password"])
+ if chn["password"] is not None
+ else chn["name"])
# Server/client capabilities
if "caps" in server or "caps" in config:
- capsl = (server["caps"] if server.hasAttribute("caps") else config["caps"]).lower()
+ capsl = (server["caps"] if server.hasAttribute("caps")
+ else config["caps"]).lower()
if capsl == "no" or capsl == "off" or capsl == "false":
opts["caps"] = None
else:
@@ -110,14 +120,14 @@ def load_file(filename, context):
if "protocol" not in server or server["protocol"] == "irc":
srvcls = IRCServer
else:
- raise Exception("Unhandled protocol '%s'" % server["protocol"])
+ raise Exception("Unhandled protocol '%s'" %
+ server["protocol"])
# Initialize the server
srv = srvcls(**opts)
# Add the server in the context
- if context.add_server(srv,
- "autoconnect" in server and server["autoconnect"].lower() != "false"):
+ if context.add_server(srv, get_boolean(server, "autoconnect")):
print("Server '%s' successfully added." % srv.id)
else:
print("Can't add server '%s'." % srv.id)
@@ -125,7 +135,7 @@ def load_file(filename, context):
# Load module and their configuration
for mod in config.getNodes("module"):
context.modules_configuration[mod["name"]] = mod
- if not mod.hasAttribute("autoload") or (mod["autoload"].lower() != "false" and mod["autoload"].lower() != "off"):
+ if get_boolean(mod, "autoload"):
__import__(mod["name"])
# Load files asked by the configuration file
@@ -155,8 +165,8 @@ def load(toks, context, prompt):
def select(toks, context, prompt):
"""Select the current server"""
- if (len(toks) == 2 and toks[1] != "None"
- and toks[1] != "nemubot" and toks[1] != "none"):
+ if (len(toks) == 2 and toks[1] != "None" and
+ toks[1] != "nemubot" and toks[1] != "none"):
if toks[1] in context.servers:
prompt.selectedServer = context.servers[toks[1]]
else:
@@ -197,15 +207,15 @@ def debug(toks, context, prompt):
print ("Not enough arguments. `debug' takes a module name.")
-#Register build-ins
+# Register build-ins
CAPS = {
- 'quit': end, #Disconnect all server and quit
- 'exit': end, #Alias for quit
- 'reset': end, #Reload the prompt
- 'refresh': end, #Reload the prompt but save modules
- 'load': load, #Load a servers or module configuration file
- 'unload': unload, #Unload a module and remove it from the list
- 'select': select, #Select a server
- 'list': liste, #Show lists
- 'debug': debug, #Pass a module in debug mode
+ 'quit': end, # Disconnect all server and quit
+ 'exit': end, # Alias for quit
+ 'reset': end, # Reload the prompt
+ 'refresh': end, # Reload the prompt but save modules
+ 'load': load, # Load a servers or module configuration file
+ 'unload': unload, # Unload a module and remove it from the list
+ 'select': select, # Select a server
+ 'list': liste, # Show lists
+ 'debug': debug, # Pass a module in debug mode
}
diff --git a/server/IRC.py b/server/IRC.py
index 9700a23..bbe6334 100644
--- a/server/IRC.py
+++ b/server/IRC.py
@@ -28,8 +28,11 @@ from message.printer.IRC import IRC as IRCPrinter
from server.socket import SocketServer
import tools
+
class IRC(SocketServer):
+ """Concrete implementation of a connexion to an IRC server"""
+
def __init__(self, owner, nick="nemubot", host="localhost", port=6667,
ssl=False, username=None, password=None, realname="Nemubot",
encoding="utf-8", caps=None, channels=list(),
@@ -155,7 +158,8 @@ class IRC(SocketServer):
# Respond to JOIN
def _on_join(msg):
- if len(msg.params) == 0: return
+ if len(msg.params) == 0:
+ return
for chname in msg.decode(msg.params[0]).split(","):
# Register the channel
@@ -164,7 +168,8 @@ class IRC(SocketServer):
self.hookscmd["JOIN"] = _on_join
# Respond to PART
def _on_part(msg):
- if len(msg.params) != 1 and len(msg.params) != 2: return
+ if len(msg.params) != 1 and len(msg.params) != 2:
+ return
for chname in msg.params[0].split(b","):
if chname in self.channels:
@@ -175,7 +180,8 @@ class IRC(SocketServer):
self.hookscmd["PART"] = _on_part
# Respond to 331/RPL_NOTOPIC,332/RPL_TOPIC,TOPIC
def _on_topic(msg):
- if len(msg.params) != 1 and len(msg.params) != 2: return
+ if len(msg.params) != 1 and len(msg.params) != 2:
+ return
if msg.params[0] in self.channels:
if len(msg.params) == 1 or len(msg.params[1]) == 0:
self.channels[msg.params[0]].topic = None
@@ -186,8 +192,10 @@ class IRC(SocketServer):
self.hookscmd["TOPIC"] = _on_topic
# Respond to 353/RPL_NAMREPLY
def _on_353(msg):
- if len(msg.params) == 3: msg.params.pop(0) # 353: like RFC 1459
- if len(msg.params) != 2: return
+ if len(msg.params) == 3:
+ msg.params.pop(0) # 353: like RFC 1459
+ if len(msg.params) != 2:
+ return
if msg.params[0] in self.channels:
for nk in msg.decode(msg.params[1]).split(" "):
res = re.match("^(?P[^a-zA-Z[\]\\`_^{|}])(?P[a-zA-Z[\]\\`_^{|}][a-zA-Z0-9[\]\\`_^{|}-]*)$")
@@ -196,7 +204,8 @@ class IRC(SocketServer):
# Respond to INVITE
def _on_invite(msg):
- if len(msg.params) != 2: return
+ if len(msg.params) != 2:
+ return
self.write("JOIN " + msg.decode(msg.params[1]))
self.hookscmd["INVITE"] = _on_invite
@@ -209,7 +218,8 @@ class IRC(SocketServer):
# Handle CTCP requests
def _on_ctcp(msg):
- if len(msg.params) != 2 or not msg.is_ctcp: return
+ 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)
@@ -353,9 +363,10 @@ class IRCMessage:
client -- export as a client-side string if true
"""
- res = ";".join(["@%s=%s" % (k,v if not isinstance(v, datetime) else v.strftime("%Y-%m-%dT%H:%M:%S.%fZ")) for k, v in self.tags.items()])
+ res = ";".join(["@%s=%s" % (k, v if not isinstance(v, datetime) else v.strftime("%Y-%m-%dT%H:%M:%S.%fZ")) for k, v in self.tags.items()])
- if not client: res += " :%s!%s@%s" % (self.nick, self.user, self.host)
+ if not client:
+ res += " :%s!%s@%s" % (self.nick, self.user, self.host)
res += " " + self.cmd
diff --git a/server/__init__.py b/server/__init__.py
index 11b9854..3ae04f2 100644
--- a/server/__init__.py
+++ b/server/__init__.py
@@ -27,6 +27,7 @@ _rlist = []
_wlist = []
_xlist = []
+
# Extends from IOBase in order to be compatible with select function
class AbstractServer(io.IOBase):
@@ -62,7 +63,8 @@ class AbstractServer(io.IOBase):
def open(self):
- """Generic open function that register the server un _rlist in case of successful _open"""
+ """Generic open function that register the server un _rlist in case
+ of successful _open"""
self.logger.info("Opening connection to %s", self.id)
if not hasattr(self, "_open") or self._open():
_rlist.append(self)
@@ -70,7 +72,8 @@ class AbstractServer(io.IOBase):
def close(self):
- """Generic close function that register the server un _{r,w,x}list in case of successful _close"""
+ """Generic close function that register the server un _{r,w,x}list in
+ case of successful _close"""
self.logger.info("Closing connection to %s", self.id)
if not hasattr(self, "_close") or self._close():
if self in _rlist:
diff --git a/server/socket.py b/server/socket.py
index 33f7944..bacfdac 100644
--- a/server/socket.py
+++ b/server/socket.py
@@ -21,8 +21,11 @@ import socket
from server import AbstractServer
+
class SocketServer(AbstractServer):
+ """Concrete implementation of a socket connexion (can be wrapped with TLS)"""
+
def __init__(self, host, port, ssl=False):
AbstractServer.__init__(self)
self.host = host
@@ -55,12 +58,13 @@ class SocketServer(AbstractServer):
self.socket = ctx.wrap_socket(self.socket)
try:
- self.socket.connect((self.host, self.port)) #Connect to server
+ self.socket.connect((self.host, self.port)) # Connect to server
self.logger.info("Connected to %s:%d", self.host, self.port)
except socket.error as e:
self.socket = None
self.logger.critical("Unable to connect to %s:%d: %s",
- self.host, self.port, os.strerror(e.errno))
+ self.host, self.port,
+ os.strerror(e.errno))
return False
return True
@@ -81,7 +85,8 @@ class SocketServer(AbstractServer):
# Write
def _write(self, cnt):
- if not self.connected: return
+ if not self.connected:
+ return
self.socket.send(cnt)
@@ -96,7 +101,8 @@ class SocketServer(AbstractServer):
# Read
def read(self):
- if not self.connected: return
+ if not self.connected:
+ return
raw = self.socket.recv(1024)
temp = (self.readbuffer + raw).split(b'\r\n')
diff --git a/tools/__init__.py b/tools/__init__.py
index fe39e49..78b9d0c 100644
--- a/tools/__init__.py
+++ b/tools/__init__.py
@@ -18,14 +18,16 @@
import imp
+
def intToIP(n):
ip = ""
- for i in range(0,4):
+ for i in range(0, 4):
mod = n % 256
ip = "%d.%s" % (mod, ip)
n = (n - mod) / 256
return ip[:len(ip) - 1]
+
def ipToInt(ip):
sum = 0
for b in ip.split("."):
diff --git a/tools/countdown.py b/tools/countdown.py
index b0c7c71..4f66b21 100644
--- a/tools/countdown.py
+++ b/tools/countdown.py
@@ -19,6 +19,7 @@
from datetime import datetime, timezone
import time
+
def countdown(delta, resolution=5):
sec = delta.seconds
hours, remainder = divmod(sec, 3600)
@@ -31,7 +32,7 @@ def countdown(delta, resolution=5):
if resolution > 0 and (force or an > 0):
force = True
- sentence += " %i an"%(an)
+ sentence += " %i an" % an
if an > 1:
sentence += "s"
@@ -42,7 +43,7 @@ def countdown(delta, resolution=5):
if resolution > 1 and (force or days > 0):
force = True
- sentence += " %i jour"%(days)
+ sentence += " %i jour" % days
if days > 1:
sentence += "s"
@@ -53,7 +54,7 @@ def countdown(delta, resolution=5):
if resolution > 2 and (force or hours > 0):
force = True
- sentence += " %i heure"%(hours)
+ sentence += " %i heure" % hours
if hours > 1:
sentence += "s"
if resolution > 4:
@@ -63,7 +64,7 @@ def countdown(delta, resolution=5):
if resolution > 3 and (force or minutes > 0):
force = True
- sentence += " %i minute"%(minutes)
+ sentence += " %i minute" % minutes
if minutes > 1:
sentence += "s"
if resolution > 4:
@@ -71,20 +72,21 @@ def countdown(delta, resolution=5):
if resolution > 4 and (force or seconds > 0):
force = True
- sentence += " %i seconde"%(seconds)
+ sentence += " %i seconde" % seconds
if seconds > 1:
sentence += "s"
return sentence[1:]
def countdown_format(date, msg_before, msg_after, tz=None):
- """Replace in a text %s by a sentence incidated the remaining time before/after an event"""
- if tz != None:
+ """Replace in a text %s by a sentence incidated the remaining time
+ before/after an event"""
+ if tz is not None:
oldtz = os.environ['TZ']
os.environ['TZ'] = tz
time.tzset()
- #Calculate time before the date
+ # Calculate time before the date
try:
if datetime.now(timezone.utc) > date:
sentence_c = msg_after
@@ -100,8 +102,7 @@ def countdown_format(date, msg_before, msg_after, tz=None):
sentence_c = msg_before
delta = date - datetime.now()
-
- if tz != None:
+ if tz is not None:
os.environ['TZ'] = oldtz
return sentence_c % countdown(delta)
diff --git a/tools/date.py b/tools/date.py
index 15b694e..f15ed95 100644
--- a/tools/date.py
+++ b/tools/date.py
@@ -29,24 +29,37 @@ xtrdt = re.compile(r'''^.*? (?P[0-9]{1,4}) .+?
(?:[^0-9]*[m\":][^0-9]*(?P[0-9]{1,2}))?)?)?.*?
$''', re.X)
+
def extractDate(msg):
"""Parse a message to extract a time and date"""
result = xtrdt.match(msg.lower())
if result is not None:
day = result.group("day")
month = result.group("month")
- if month == "janvier" or month == "january" or month == "januar": month = 1
- elif month == "fevrier" or month == "février" or month == "february": month = 2
- elif month == "mars" or month == "march": month = 3
- elif month == "avril" or month == "april": month = 4
- elif month == "mai" or month == "may" or month == "maï": month = 5
- elif month == "juin" or month == "juni" or month == "junni": month = 6
- elif month == "juillet" or month == "jully" or month == "july": month = 7
- elif month == "aout" or month == "août" or month == "august": month = 8
- elif month == "september" or month == "septembre": month = 9
- elif month == "october" or month == "october" or month == "oktober": month = 10
- elif month == "november" or month == "novembre": month = 11
- elif month == "december" or month == "decembre" or month == "décembre": month = 12
+ if month == "janvier" or month == "january" or month == "januar":
+ month = 1
+ elif month == "fevrier" or month == "février" or month == "february":
+ month = 2
+ elif month == "mars" or month == "march":
+ month = 3
+ elif month == "avril" or month == "april":
+ month = 4
+ elif month == "mai" or month == "may" or month == "maï":
+ month = 5
+ elif month == "juin" or month == "juni" or month == "junni":
+ month = 6
+ elif month == "juillet" or month == "jully" or month == "july":
+ month = 7
+ elif month == "aout" or month == "août" or month == "august":
+ month = 8
+ elif month == "september" or month == "septembre":
+ month = 9
+ elif month == "october" or month == "october" or month == "oktober":
+ month = 10
+ elif month == "november" or month == "novembre":
+ month = 11
+ elif month == "december" or month == "decembre" or month == "décembre":
+ month = 12
year = result.group("year")
@@ -57,9 +70,12 @@ def extractDate(msg):
minute = result.group("minute")
second = result.group("second")
- if year is None: year = date.today().year
- if hour is None: hour = 0
- if minute is None: minute = 0
+ if year is None:
+ year = date.today().year
+ if hour is None:
+ hour = 0
+ if minute is None:
+ minute = 0
if second is None:
second = 1
else:
@@ -68,6 +84,7 @@ def extractDate(msg):
minute = int(minute) + 1
second = 0
- return datetime(int(year), int(month), int(day), int(hour), int(minute), int(second))
+ return datetime(int(year), int(month), int(day),
+ int(hour), int(minute), int(second))
else:
return None
diff --git a/tools/web.py b/tools/web.py
index 020a616..a9e59fa 100644
--- a/tools/web.py
+++ b/tools/web.py
@@ -28,31 +28,39 @@ from urllib.request import urlopen
from exception import IRCException
import xmlparser
+
def isURL(url):
"""Return True if the URL can be parsed"""
o = urlparse(url)
return o.scheme == "" and o.netloc == "" and o.path == ""
+
def getScheme(url):
"""Return the protocol of a given URL"""
o = urlparse(url)
return o.scheme
+
def getHost(url):
"""Return the domain of a given URL"""
return urlparse(url).netloc
+
def getPort(url):
"""Return the port of a given URL"""
return urlparse(url).port
+
def getPath(url):
"""Return the page request of a given URL"""
return urlparse(url).path
+
def getUser(url):
"""Return the page request of a given URL"""
return urlparse(url).username
+
+
def getPassword(url):
"""Return the page request of a given URL"""
return urlparse(url).password
@@ -67,16 +75,19 @@ def getURLContent(url, timeout=15):
o = urlparse("http://" + url)
if o.scheme == "http":
- conn = http.client.HTTPConnection(o.netloc, port=o.port, timeout=timeout)
+ conn = http.client.HTTPConnection(o.netloc, port=o.port,
+ timeout=timeout)
elif o.scheme == "https":
- conn = http.client.HTTPSConnection(o.netloc, port=o.port, timeout=timeout)
+ conn = http.client.HTTPSConnection(o.netloc, port=o.port,
+ timeout=timeout)
elif o.scheme is None or o.scheme == "":
conn = http.client.HTTPConnection(o.netloc, port=80, timeout=timeout)
else:
return None
try:
if o.query != '':
- conn.request("GET", o.path + "?" + o.query, None, {"User-agent": "Nemubot v3"})
+ conn.request("GET", o.path + "?" + o.query,
+ None, {"User-agent": "Nemubot v3"})
else:
conn.request("GET", o.path, None, {"User-agent": "Nemubot v3"})
except socket.timeout:
@@ -115,10 +126,14 @@ def getURLContent(url, timeout=15):
if res.status == http.client.OK or res.status == http.client.SEE_OTHER:
return data.decode(charset)
- elif (res.status == http.client.FOUND or res.status == http.client.MOVED_PERMANENTLY) and res.getheader("Location") != url:
+ elif ((res.status == http.client.FOUND or
+ res.status == http.client.MOVED_PERMANENTLY) and
+ res.getheader("Location") != url):
return getURLContent(res.getheader("Location"), timeout)
else:
- raise IRCException("A HTTP error occurs: %d - %s" % (res.status, http.client.responses[res.status]))
+ raise IRCException("A HTTP error occurs: %d - %s" %
+ (res.status, http.client.responses[res.status]))
+
def getXML(url, timeout=15):
"""Get content page and return XML parsed content"""
@@ -128,6 +143,7 @@ def getXML(url, timeout=15):
else:
return xmlparser.parse_string(cnt.encode())
+
def getJSON(url, timeout=15):
"""Get content page and return JSON content"""
cnt = getURLContent(url, timeout)
@@ -136,6 +152,7 @@ def getJSON(url, timeout=15):
else:
return json.loads(cnt.decode())
+
# Other utils
def htmlentitydecode(s):
@@ -143,7 +160,11 @@ def htmlentitydecode(s):
return re.sub('&(%s);' % '|'.join(name2codepoint),
lambda m: chr(name2codepoint[m.group(1)]), s)
+
def striphtml(data):
"""Remove HTML tags from text"""
p = re.compile(r'<.*?>')
- return htmlentitydecode(p.sub('', data).replace("(", "/(").replace(")", ")/").replace(""", "\""))
+ return htmlentitydecode(p.sub('', data)
+ .replace("(", "/(")
+ .replace(")", ")/")
+ .replace(""", "\""))
diff --git a/xmlparser/__init__.py b/xmlparser/__init__.py
index 8dd33d6..722c6bf 100644
--- a/xmlparser/__init__.py
+++ b/xmlparser/__init__.py
@@ -25,56 +25,59 @@ from . import node as module_state
logger = logging.getLogger("nemubot.xmlparser")
+
class ModuleStatesFile(xml.sax.ContentHandler):
- def startDocument(self):
- self.root = None
- self.stack = list()
+ def startDocument(self):
+ self.root = None
+ self.stack = list()
- def startElement(self, name, attrs):
- cur = module_state.ModuleState(name)
+ def startElement(self, name, attrs):
+ cur = module_state.ModuleState(name)
- for name in attrs.keys():
- cur.setAttribute(name, attrs.getValue(name))
+ for name in attrs.keys():
+ cur.setAttribute(name, attrs.getValue(name))
- self.stack.append(cur)
+ self.stack.append(cur)
- def characters(self, content):
- self.stack[len(self.stack)-1].content += content
+ def characters(self, content):
+ self.stack[len(self.stack)-1].content += content
+
+ def endElement(self, name):
+ child = self.stack.pop()
+ size = len(self.stack)
+ if size > 0:
+ self.stack[size - 1].content = self.stack[size - 1].content.strip()
+ self.stack[size - 1].addChild(child)
+ else:
+ self.root = child
- def endElement(self, name):
- child = self.stack.pop()
- size = len(self.stack)
- if size > 0:
- self.stack[size - 1].content = self.stack[size - 1].content.strip()
- self.stack[size - 1].addChild(child)
- else:
- self.root = child
def parse_file(filename):
- parser = xml.sax.make_parser()
- mod = ModuleStatesFile()
- parser.setContentHandler(mod)
- try:
- parser.parse(open(filename, "r"))
- return mod.root
- except IOError:
- logger.exception("error occurs during XML parsing of %s", filename)
- return module_state.ModuleState("nemubotstate")
- except:
- logger.exception("error occurs during XML parsing of %s", filename)
- if mod.root is None:
- return module_state.ModuleState("nemubotstate")
- else:
- return mod.root
+ parser = xml.sax.make_parser()
+ mod = ModuleStatesFile()
+ parser.setContentHandler(mod)
+ try:
+ parser.parse(open(filename, "r"))
+ return mod.root
+ except IOError:
+ logger.exception("error occurs during XML parsing of %s", filename)
+ return module_state.ModuleState("nemubotstate")
+ except:
+ logger.exception("error occurs during XML parsing of %s", filename)
+ if mod.root is None:
+ return module_state.ModuleState("nemubotstate")
+ else:
+ return mod.root
+
def parse_string(string):
- mod = ModuleStatesFile()
- try:
- xml.sax.parseString(string, mod)
- return mod.root
- except:
- logger.exception("error occurs during XML parsing")
- if mod.root is None:
- return module_state.ModuleState("nemubotstate")
- else:
- return mod.root
+ mod = ModuleStatesFile()
+ try:
+ xml.sax.parseString(string, mod)
+ return mod.root
+ except:
+ logger.exception("error occurs during XML parsing")
+ if mod.root is None:
+ return module_state.ModuleState("nemubotstate")
+ else:
+ return mod.root
diff --git a/xmlparser/node.py b/xmlparser/node.py
index 34e7d0e..6f7f91c 100644
--- a/xmlparser/node.py
+++ b/xmlparser/node.py
@@ -9,197 +9,206 @@ import traceback
logger = logging.getLogger("nemubot.xmlparser.node")
+
class ModuleState:
- """Tiny tree representation of an XML file"""
+ """Tiny tree representation of an XML file"""
- def __init__(self, name):
- self.name = name
- self.content = ""
- self.attributes = dict()
- self.childs = list()
- self.index = dict()
- self.index_fieldname = None
- self.index_tagname = None
+ def __init__(self, name):
+ self.name = name
+ self.content = ""
+ self.attributes = dict()
+ self.childs = list()
+ self.index = dict()
+ self.index_fieldname = None
+ self.index_tagname = None
- def getName(self):
- """Get the name of the current node"""
- return self.name
+ def getName(self):
+ """Get the name of the current node"""
+ return self.name
- def display(self, level = 0):
- ret = ""
- out = list()
- for k in self.attributes:
- out.append("%s : %s" % (k, self.attributes[k]))
- ret += "%s%s { %s } = '%s'\n" % (' ' * level, self.name, ' ; '.join(out), self.content)
- for c in self.childs:
- ret += c.display(level + 2)
- return ret
+ def display(self, level = 0):
+ ret = ""
+ out = list()
+ for k in self.attributes:
+ out.append("%s : %s" % (k, self.attributes[k]))
+ ret += "%s%s { %s } = '%s'\n" % (' ' * level, self.name,
+ ' ; '.join(out), self.content)
+ for c in self.childs:
+ ret += c.display(level + 2)
+ return ret
- def __str__(self):
- return self.display()
+ def __str__(self):
+ return self.display()
- def __getitem__(self, i):
- """Return the attribute asked"""
- return self.getAttribute(i)
+ def __getitem__(self, i):
+ """Return the attribute asked"""
+ return self.getAttribute(i)
- def __setitem__(self, i, c):
- """Set the attribute"""
- return self.setAttribute(i, c)
+ def __setitem__(self, i, c):
+ """Set the attribute"""
+ return self.setAttribute(i, c)
- def getAttribute(self, name):
- """Get the asked argument or return None if doesn't exist"""
- if name in self.attributes:
- return self.attributes[name]
- else:
- return None
+ def getAttribute(self, name):
+ """Get the asked argument or return None if doesn't exist"""
+ if name in self.attributes:
+ return self.attributes[name]
+ else:
+ return None
- def getDate(self, name=None):
- """Get the asked argument and return it as a date"""
- if name is None:
- source = self.content
- elif name in self.attributes.keys():
- source = self.attributes[name]
- else:
+ def getDate(self, name=None):
+ """Get the asked argument and return it as a date"""
+ if name is None:
+ source = self.content
+ elif name in self.attributes.keys():
+ source = self.attributes[name]
+ else:
+ return None
+
+ if isinstance(source, datetime):
+ return source
+ else:
+ try:
+ return datetime.fromtimestamp(float(source), timezone.utc)
+ except ValueError:
+ while True:
+ try:
+ return datetime.fromtimestamp(time.mktime(
+ time.strptime(source[:19], "%Y-%m-%d %H:%M:%S")),
+ timezone.utc)
+ except ImportError:
+ pass
+
+ def getInt(self, name=None):
+ """Get the asked argument and return it as an integer"""
+ if name is None:
+ source = self.content
+ elif name in self.attributes.keys():
+ source = self.attributes[name]
+ else:
+ return None
+
+ return int(float(source))
+
+ def getBool(self, name=None):
+ """Get the asked argument and return it as an integer"""
+ if name is None:
+ source = self.content
+ elif name in self.attributes.keys():
+ source = self.attributes[name]
+ else:
+ return False
+
+ return (isinstance(source, bool) and source) or source == "True"
+
+ def tmpIndex(self, fieldname="name", tagname=None):
+ index = dict()
+ for child in self.childs:
+ if ((tagname is None or tagname == child.name) and
+ child.hasAttribute(fieldname)):
+ index[child[fieldname]] = child
+ return index
+
+ def setIndex(self, fieldname="name", tagname=None):
+ """Defines an hash table to accelerate childs search.
+ You have just to define a common attribute"""
+ self.index = self.tmpIndex(fieldname, tagname)
+ self.index_fieldname = fieldname
+ self.index_tagname = tagname
+
+ def __contains__(self, i):
+ """Return true if i is found in the index"""
+ if self.index:
+ return i in self.index
+ else:
+ return self.hasAttribute(i)
+
+ def hasAttribute(self, name):
+ """DOM like method"""
+ return (name in self.attributes)
+
+ def setAttribute(self, name, value):
+ """DOM like method"""
+ if (isinstance(value, datetime) or isinstance(value, str) or
+ isinstance(value, int) or isinstance(value, float)):
+ self.attributes[name] = value
+ else:
+ raise TypeError("attributes must be primary type "
+ "or datetime (here %s)" % type(value))
+
+ def getContent(self):
+ return self.content
+
+ def getChilds(self):
+ """Return a full list of direct child of this node"""
+ return self.childs
+
+ def getNode(self, tagname):
+ """Get a unique node (or the last one) with the given tagname"""
+ ret = None
+ for child in self.childs:
+ if tagname is None or tagname == child.name:
+ ret = child
+ return ret
+
+ def getFirstNode(self, tagname):
+ """Get a unique node (or the last one) with the given tagname"""
+ for child in self.childs:
+ if tagname is None or tagname == child.name:
+ return child
return None
- if isinstance(source, datetime):
- return source
- else:
- try:
- return datetime.fromtimestamp(float(source), timezone.utc)
- except ValueError:
- while True:
- try:
- return datetime.fromtimestamp(time.mktime(
- time.strptime(source[:19], "%Y-%m-%d %H:%M:%S")), timezone.utc)
- except ImportError:
- pass
+ def getNodes(self, tagname):
+ """Get all direct childs that have the given tagname"""
+ for child in self.childs:
+ if tagname is None or tagname == child.name:
+ yield child
- def getInt(self, name=None):
- """Get the asked argument and return it as an integer"""
- if name is None:
- source = self.content
- elif name in self.attributes.keys():
- source = self.attributes[name]
- else:
- return None
-
- return int(float(source))
-
- def getBool(self, name=None):
- """Get the asked argument and return it as an integer"""
- if name is None:
- source = self.content
- elif name in self.attributes.keys():
- source = self.attributes[name]
- else:
+ def hasNode(self, tagname):
+ """Return True if at least one node with the given tagname exists"""
+ for child in self.childs:
+ if tagname is None or tagname == child.name:
+ return True
return False
- return (isinstance(source, bool) and source) or source == "True"
+ def addChild(self, child):
+ """Add a child to this node"""
+ self.childs.append(child)
+ if self.index_fieldname is not None:
+ self.setIndex(self.index_fieldname, self.index_tagname)
- def tmpIndex(self, fieldname="name", tagname=None):
- index = dict()
- for child in self.childs:
- if (tagname is None or tagname == child.name) and child.hasAttribute(fieldname):
- index[child[fieldname]] = child
- return index
+ def delChild(self, child):
+ """Remove the given child from this node"""
+ self.childs.remove(child)
+ if self.index_fieldname is not None:
+ self.setIndex(self.index_fieldname, self.index_tagname)
- def setIndex(self, fieldname="name", tagname=None):
- """Defines an hash table to accelerate childs search. You have just to define a common attribute"""
- self.index = self.tmpIndex(fieldname, tagname)
- self.index_fieldname = fieldname
- self.index_tagname = tagname
+ def save_node(self, gen):
+ """Serialize this node as a XML node"""
+ attribs = {}
+ for att in self.attributes.keys():
+ if att[0] != "_": # Don't save attribute starting by _
+ if isinstance(self.attributes[att], datetime):
+ attribs[att] = str(time.mktime(
+ self.attributes[att].timetuple()))
+ else:
+ attribs[att] = str(self.attributes[att])
+ attrs = xml.sax.xmlreader.AttributesImpl(attribs)
- def __contains__(self, i):
- """Return true if i is found in the index"""
- if self.index:
- return i in self.index
- else:
- return self.hasAttribute(i)
+ try:
+ gen.startElement(self.name, attrs)
- def hasAttribute(self, name):
- """DOM like method"""
- return (name in self.attributes)
+ for child in self.childs:
+ child.save_node(gen)
- def setAttribute(self, name, value):
- """DOM like method"""
- if isinstance(value, datetime) or isinstance(value, str) or isinstance(value, int) or isinstance(value, float):
- self.attributes[name] = value
- else:
- raise TypeError("attributes must be primary type or datetime (here %s)" % type(value))
+ gen.endElement(self.name)
+ except:
+ logger.exception("Error occured when saving the following "
+ "XML node: %s with %s", self.name, attrs)
- def getContent(self):
- return self.content
-
- def getChilds(self):
- """Return a full list of direct child of this node"""
- return self.childs
-
- def getNode(self, tagname):
- """Get a unique node (or the last one) with the given tagname"""
- ret = None
- for child in self.childs:
- if tagname is None or tagname == child.name:
- ret = child
- return ret
-
- def getFirstNode(self, tagname):
- """Get a unique node (or the last one) with the given tagname"""
- for child in self.childs:
- if tagname is None or tagname == child.name:
- return child
- return None
-
- def getNodes(self, tagname):
- """Get all direct childs that have the given tagname"""
- for child in self.childs:
- if tagname is None or tagname == child.name:
- yield child
-
- def hasNode(self, tagname):
- """Return True if at least one node with the given tagname exists"""
- for child in self.childs:
- if tagname is None or tagname == child.name:
- return True
- return False
-
- def addChild(self, child):
- """Add a child to this node"""
- self.childs.append(child)
- if self.index_fieldname is not None:
- self.setIndex(self.index_fieldname, self.index_tagname)
-
- def delChild(self, child):
- """Remove the given child from this node"""
- self.childs.remove(child)
- if self.index_fieldname is not None:
- self.setIndex(self.index_fieldname, self.index_tagname)
-
- def save_node(self, gen):
- """Serialize this node as a XML node"""
- attribs = {}
- for att in self.attributes.keys():
- if att[0] != "_": # Don't save attribute starting by _
- if isinstance(self.attributes[att], datetime):
- attribs[att] = str(time.mktime(self.attributes[att].timetuple()))
- else:
- attribs[att] = str(self.attributes[att])
- attrs = xml.sax.xmlreader.AttributesImpl(attribs)
-
- try:
- gen.startElement(self.name, attrs)
-
- for child in self.childs:
- child.save_node(gen)
-
- gen.endElement(self.name)
- except:
- logger.exception("Error occured when saving the following XML node: %s with %s", self.name, attrs)
-
- def save(self, filename):
- """Save the current node as root node in a XML file"""
- with open(filename,"w") as f:
- gen = xml.sax.saxutils.XMLGenerator(f, "utf-8")
- gen.startDocument()
- self.save_node(gen)
- gen.endDocument()
+ def save(self, filename):
+ """Save the current node as root node in a XML file"""
+ with open(filename, "w") as f:
+ gen = xml.sax.saxutils.XMLGenerator(f, "utf-8")
+ gen.startDocument()
+ self.save_node(gen)
+ gen.endDocument()