PEP8 clean

This commit is contained in:
nemunaire 2014-11-09 14:11:54 +01:00
parent 95deafe7af
commit e17996d858
23 changed files with 481 additions and 316 deletions

9
bot.py
View File

@ -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()

View File

@ -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] == "@":

View File

@ -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)

View File

@ -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)

View File

@ -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):

View File

@ -16,6 +16,7 @@
# 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 HooksManager:
"""Class to manage hooks"""

View File

@ -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:

View File

@ -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,

View File

@ -19,6 +19,7 @@
from message import TextMessage
from message.visitor import AbstractVisitor
class IRC(AbstractVisitor):
def __init__(self):

View File

@ -18,6 +18,7 @@
import imp
def reload():
import message.printer.IRC
imp.reload(message.printer.IRC)

View File

@ -16,6 +16,7 @@
# 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):

View File

@ -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...")

View File

@ -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)

View File

@ -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
}

View File

@ -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<level>[^a-zA-Z[\]\\`_^{|}])(?P<nickname>[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

View File

@ -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:

View File

@ -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')

View File

@ -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("."):

View File

@ -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)

View File

@ -29,24 +29,37 @@ xtrdt = re.compile(r'''^.*? (?P<day>[0-9]{1,4}) .+?
(?:[^0-9]*[m\":][^0-9]*(?P<second>[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

View File

@ -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("&#x28;", "/(").replace("&#x29;", ")/").replace("&#x22;", "\""))
return htmlentitydecode(p.sub('', data)
.replace("&#x28;", "/(")
.replace("&#x29;", ")/")
.replace("&#x22;", "\""))

View File

@ -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

View File

@ -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()