1
0
Fork 0
nemubot/modules/events.py

297 lines
12 KiB
Python
Raw Permalink Normal View History

2014-08-27 23:39:31 +00:00
"""Create countdowns and reminders"""
import calendar
2014-09-30 21:51:14 +00:00
from datetime import datetime, timedelta, timezone
from functools import partial
import re
2014-08-08 17:45:41 +00:00
2015-02-11 17:12:39 +00:00
from nemubot import context
from nemubot.exception import IMException
from nemubot.event import ModuleEvent
from nemubot.hooks import hook
2015-11-11 16:57:08 +00:00
from nemubot.message import Command
from nemubot.tools.countdown import countdown_format, countdown
2015-01-05 09:18:40 +00:00
from nemubot.tools.date import extractDate
from nemubot.tools.xmlparser.basic import DictNode
2014-08-08 17:45:41 +00:00
from nemubot.module.more import Response
2015-11-14 15:17:25 +00:00
class Event:
def __init__(self, server, channel, creator, start_time, end_time=None):
self._server = server
self._channel = channel
self._creator = creator
self._start = datetime.utcfromtimestamp(float(start_time)).replace(tzinfo=timezone.utc) if not isinstance(start_time, datetime) else start_time
self._end = datetime.utcfromtimestamp(float(end_time)).replace(tzinfo=timezone.utc) if end_time else None
self._evt = None
def __del__(self):
if self._evt is not None:
context.del_event(self._evt)
self._evt = None
def saveElement(self, store, tag="event"):
attrs = {
"server": str(self._server),
"channel": str(self._channel),
"creator": str(self._creator),
"start_time": str(calendar.timegm(self._start.timetuple())),
}
if self._end:
attrs["end_time"] = str(calendar.timegm(self._end.timetuple()))
store.startElement(tag, attrs)
store.endElement(tag)
@property
def creator(self):
return self._creator
@property
def start(self):
return self._start
@property
def end(self):
return self._end
@end.setter
def end(self, c):
self._end = c
@end.deleter
def end(self):
self._end = None
2014-08-08 17:45:41 +00:00
def help_full ():
return "This module store a lot of events: ny, we, " + (", ".join(context.datas.keys()) if hasattr(context, "datas") else "") + "\n!eventslist: gets list of timer\n!start /something/: launch a timer"
2014-08-08 17:45:41 +00:00
2015-11-14 15:17:25 +00:00
2014-08-08 17:45:41 +00:00
def load(context):
context.set_knodes({
"dict": DictNode,
"event": Event,
})
if context.data is None:
context.set_default(DictNode())
2015-02-11 17:12:39 +00:00
# Relaunch all timers
for kevt in context.data:
if context.data[kevt].end:
context.data[kevt]._evt = context.add_event(ModuleEvent(partial(fini, kevt, context.data[kevt]), offset=context.data[kevt].end - datetime.now(timezone.utc), interval=0))
2014-08-08 17:45:41 +00:00
def fini(name, evt):
context.send_response(evt._server, Response("%s arrivé à échéance." % name, channel=evt._channel, nick=evt.creator))
evt._evt = None
del context.data[name]
2015-02-11 17:12:39 +00:00
context.save()
2014-08-08 17:45:41 +00:00
2015-11-14 15:17:25 +00:00
2015-11-02 19:19:12 +00:00
@hook.command("goûter")
2014-08-08 17:45:41 +00:00
def cmd_gouter(msg):
2014-09-30 21:51:14 +00:00
ndate = datetime.now(timezone.utc)
ndate = datetime(ndate.year, ndate.month, ndate.day, 16, 42, 0, 0, timezone.utc)
return Response(countdown_format(ndate,
2014-08-08 17:45:41 +00:00
"Le goûter aura lieu dans %s, préparez vos biscuits !",
"Nous avons %s de retard pour le goûter :("),
channel=msg.channel)
2015-11-14 15:17:25 +00:00
2015-11-02 19:19:12 +00:00
@hook.command("week-end")
2014-08-08 17:45:41 +00:00
def cmd_we(msg):
2014-09-30 21:51:14 +00:00
ndate = datetime.now(timezone.utc) + timedelta(5 - datetime.today().weekday())
ndate = datetime(ndate.year, ndate.month, ndate.day, 0, 0, 1, 0, timezone.utc)
return Response(countdown_format(ndate,
2014-08-08 17:45:41 +00:00
"Il reste %s avant le week-end, courage ;)",
"Youhou, on est en week-end depuis %s."),
channel=msg.channel)
2015-11-14 15:17:25 +00:00
2015-11-02 19:19:12 +00:00
@hook.command("start")
2014-08-08 17:45:41 +00:00
def start_countdown(msg):
"""!start /something/: launch a timer"""
2015-07-10 21:09:54 +00:00
if len(msg.args) < 1:
raise IMException("indique le nom d'un événement à chronométrer")
if msg.args[0] in context.data:
raise IMException("%s existe déjà." % msg.args[0])
2014-08-08 17:45:41 +00:00
evt = Event(server=msg.server, channel=msg.channel, creator=msg.frm, start_time=msg.date)
2014-08-08 17:45:41 +00:00
2015-07-10 21:09:54 +00:00
if len(msg.args) > 1:
result1 = re.findall("([0-9]+)([smhdjwyaSMHDJWYA])?", msg.args[1])
result2 = re.match("(.*[^0-9])?([0-3]?[0-9])/([0-1]?[0-9])/((19|20)?[01239][0-9])", msg.args[1])
result3 = re.match("(.*[^0-9])?([0-2]?[0-9]):([0-5]?[0-9])(:([0-5]?[0-9]))?", msg.args[1])
2014-08-08 17:45:41 +00:00
if result2 is not None or result3 is not None:
try:
2014-10-05 16:19:20 +00:00
now = msg.date
2014-08-08 17:45:41 +00:00
if result3 is None or result3.group(5) is None: sec = 0
else: sec = int(result3.group(5))
if result3 is None or result3.group(3) is None: minu = 0
else: minu = int(result3.group(3))
if result3 is None or result3.group(2) is None: hou = 0
else: hou = int(result3.group(2))
if result2 is None or result2.group(4) is None: yea = now.year
else: yea = int(result2.group(4))
if result2 is not None and result3 is not None:
evt.end = datetime(yea, int(result2.group(3)), int(result2.group(2)), hou, minu, sec, timezone.utc)
2014-08-08 17:45:41 +00:00
elif result2 is not None:
evt.end = datetime(int(result2.group(4)), int(result2.group(3)), int(result2.group(2)), 0, 0, 0, timezone.utc)
2014-08-08 17:45:41 +00:00
elif result3 is not None:
if hou * 3600 + minu * 60 + sec > now.hour * 3600 + now.minute * 60 + now.second:
evt.end = datetime(now.year, now.month, now.day, hou, minu, sec, timezone.utc)
2014-08-08 17:45:41 +00:00
else:
evt.end = datetime(now.year, now.month, now.day + 1, hou, minu, sec, timezone.utc)
2014-08-08 17:45:41 +00:00
except:
raise IMException("Mauvais format de date pour l'événement %s. Il n'a pas été créé." % msg.args[0])
2014-08-08 17:45:41 +00:00
elif result1 is not None and len(result1) > 0:
evt.end = msg.date
2014-08-08 17:45:41 +00:00
for (t, g) in result1:
if g is None or g == "" or g == "m" or g == "M":
evt.end += timedelta(minutes=int(t))
2014-08-08 17:45:41 +00:00
elif g == "h" or g == "H":
evt.end += timedelta(hours=int(t))
2014-08-08 17:45:41 +00:00
elif g == "d" or g == "D" or g == "j" or g == "J":
evt.end += timedelta(days=int(t))
2014-08-08 17:45:41 +00:00
elif g == "w" or g == "W":
evt.end += timedelta(days=int(t)*7)
2014-08-08 17:45:41 +00:00
elif g == "y" or g == "Y" or g == "a" or g == "A":
evt.end += timedelta(days=int(t)*365)
2014-08-08 17:45:41 +00:00
else:
evt.end += timedelta(seconds=int(t))
2014-08-08 17:45:41 +00:00
else:
raise IMException("Mauvais format de date pour l'événement %s. Il n'a pas été créé." % msg.args[0])
context.data[msg.args[0]] = evt
2015-02-11 17:12:39 +00:00
context.save()
if evt.end is not None:
context.add_event(ModuleEvent(partial(fini, msg.args[0], evt),
offset=evt.end - datetime.now(timezone.utc),
interval=0))
return Response("%s commencé le %s et se terminera le %s." %
2015-07-10 21:09:54 +00:00
(msg.args[0], msg.date.strftime("%A %d %B %Y à %H:%M:%S"),
evt.end.strftime("%A %d %B %Y à %H:%M:%S")),
channel=msg.channel)
2014-08-08 17:45:41 +00:00
else:
2015-07-10 21:09:54 +00:00
return Response("%s commencé le %s"% (msg.args[0],
2014-10-05 16:19:20 +00:00
msg.date.strftime("%A %d %B %Y à %H:%M:%S")),
channel=msg.channel)
2014-08-08 17:45:41 +00:00
2015-11-14 15:17:25 +00:00
2015-11-02 19:19:12 +00:00
@hook.command("end")
@hook.command("forceend")
2014-08-08 17:45:41 +00:00
def end_countdown(msg):
2015-07-10 21:09:54 +00:00
if len(msg.args) < 1:
raise IMException("quel événement terminer ?")
2014-08-08 17:45:41 +00:00
if msg.args[0] in context.data:
if context.data[msg.args[0]].creator == msg.frm or (msg.cmd == "forceend" and msg.frm_owner):
duration = countdown(msg.date - context.data[msg.args[0]].start)
del context.data[msg.args[0]]
2015-02-11 17:12:39 +00:00
context.save()
2015-07-10 21:09:54 +00:00
return Response("%s a duré %s." % (msg.args[0], duration),
2017-07-18 04:32:48 +00:00
channel=msg.channel, nick=msg.frm)
2014-08-08 17:45:41 +00:00
else:
raise IMException("Vous ne pouvez pas terminer le compteur %s, créé par %s." % (msg.args[0], context.data[msg.args[0]].creator))
2014-08-08 17:45:41 +00:00
else:
2017-07-18 04:32:48 +00:00
return Response("%s n'est pas un compteur connu."% (msg.args[0]), channel=msg.channel, nick=msg.frm)
2014-08-08 17:45:41 +00:00
2015-11-14 15:17:25 +00:00
2015-11-02 19:19:12 +00:00
@hook.command("eventslist")
2014-08-08 17:45:41 +00:00
def liste(msg):
"""!eventslist: gets list of timer"""
2015-07-10 21:09:54 +00:00
if len(msg.args):
res = Response(channel=msg.channel)
2015-07-10 21:09:54 +00:00
for user in msg.args:
cmptr = [k for k in context.data if context.data[k].creator == user]
2014-08-08 17:45:41 +00:00
if len(cmptr) > 0:
res.append_message(cmptr, title="Events created by %s" % user)
2014-08-08 17:45:41 +00:00
else:
res.append_message("%s doesn't have any counting events" % user)
return res
2014-08-08 17:45:41 +00:00
else:
return Response(list(context.data.keys()), channel=msg.channel, title="Known events")
2014-08-08 17:45:41 +00:00
2015-11-14 15:17:25 +00:00
@hook.command(match=lambda msg: isinstance(msg, Command) and msg.cmd in context.data)
2014-08-08 17:45:41 +00:00
def parseanswer(msg):
2015-11-11 16:57:08 +00:00
res = Response(channel=msg.channel)
2014-08-08 17:45:41 +00:00
2015-11-11 16:57:08 +00:00
# Avoid message starting by ! which can be interpreted as command by other bots
if msg.cmd[0] == "!":
2017-07-18 04:32:48 +00:00
res.nick = msg.frm
2014-08-08 17:45:41 +00:00
if msg.cmd in context.data:
if context.data[msg.cmd].end:
res.append_message("%s commencé il y a %s et se terminera dans %s." % (msg.cmd, countdown(msg.date - context.data[msg.cmd].start), countdown(context.data[msg.cmd].end - msg.date)))
2014-08-08 17:45:41 +00:00
else:
res.append_message("%s commencé il y a %s." % (msg.cmd, countdown(msg.date - context.data[msg.cmd].start)))
2015-11-11 16:57:08 +00:00
else:
res.append_message(countdown_format(context.data[msg.cmd].start, context.data[msg.cmd]["msg_before"], context.data[msg.cmd]["msg_after"]))
2015-11-11 16:57:08 +00:00
return res
2014-08-08 17:45:41 +00:00
2015-11-14 15:17:25 +00:00
2014-08-08 17:45:41 +00:00
RGXP_ask = re.compile(r"^.*((create|new)\s+(a|an|a\s*new|an\s*other)?\s*(events?|commande?)|(nouvel(le)?|ajoute|cr[ée]{1,3})\s+(un)?\s*([eé]v[ée]nements?|commande?)).*$", re.I)
2017-07-18 04:48:15 +00:00
@hook.ask(match=lambda msg: RGXP_ask.match(msg.message))
2014-08-08 17:45:41 +00:00
def parseask(msg):
2017-07-18 04:48:15 +00:00
name = re.match("^.*!([^ \"'@!]+).*$", msg.message)
2015-11-14 15:17:25 +00:00
if name is None:
raise IMException("il faut que tu attribues une commande à l'événement.")
if name.group(1) in context.data:
2015-11-14 15:17:25 +00:00
raise IMException("un événement portant ce nom existe déjà.")
2017-07-18 04:48:15 +00:00
texts = re.match("^[^\"]*(avant|après|apres|before|after)?[^\"]*\"([^\"]+)\"[^\"]*((avant|après|apres|before|after)?.*\"([^\"]+)\".*)?$", msg.message, re.I)
2015-11-14 15:17:25 +00:00
if texts is not None and texts.group(3) is not None:
2017-07-18 04:48:15 +00:00
extDate = extractDate(msg.message)
2015-11-14 15:17:25 +00:00
if extDate is None or extDate == "":
raise IMException("la date de l'événement est invalide !")
if texts.group(1) is not None and (texts.group(1) == "après" or texts.group(1) == "apres" or texts.group(1) == "after"):
msg_after = texts.group(2)
msg_before = texts.group(5)
if (texts.group(4) is not None and (texts.group(4) == "après" or texts.group(4) == "apres" or texts.group(4) == "after")) or texts.group(1) is None:
msg_before = texts.group(2)
msg_after = texts.group(5)
if msg_before.find("%s") == -1 or msg_after.find("%s") == -1:
raise IMException("Pour que l'événement soit valide, ajouter %s à"
" l'endroit où vous voulez que soit ajouté le"
" compte à rebours.")
evt = ModuleState("event")
evt["server"] = msg.server
evt["channel"] = msg.channel
2017-07-18 04:32:48 +00:00
evt["proprio"] = msg.frm
2015-11-14 15:17:25 +00:00
evt["name"] = name.group(1)
evt["start"] = extDate
evt["msg_after"] = msg_after
evt["msg_before"] = msg_before
context.data.addChild(evt)
context.save()
return Response("Nouvel événement !%s ajouté avec succès." % name.group(1),
channel=msg.channel)
elif texts is not None and texts.group(2) is not None:
evt = ModuleState("event")
evt["server"] = msg.server
evt["channel"] = msg.channel
2017-07-18 04:32:48 +00:00
evt["proprio"] = msg.frm
2015-11-14 15:17:25 +00:00
evt["name"] = name.group(1)
evt["msg_before"] = texts.group (2)
context.data.addChild(evt)
context.save()
return Response("Nouvelle commande !%s ajoutée avec succès." % name.group(1),
channel=msg.channel)
2014-08-08 17:45:41 +00:00
2015-11-14 15:17:25 +00:00
else:
raise IMException("Veuillez indiquez les messages d'attente et d'après événement entre guillemets.")