2018-10-30 14:58:48 +00:00
|
|
|
import time, typing, uuid
|
2019-10-08 11:20:08 +00:00
|
|
|
from src import Database, EventManager, Logging, PollHook
|
2018-09-28 15:51:36 +00:00
|
|
|
|
2019-10-08 12:49:43 +00:00
|
|
|
T_CALLBACK = typing.Callable[["Timer"], None]
|
|
|
|
|
2018-09-28 15:51:36 +00:00
|
|
|
class Timer(object):
|
2018-10-30 17:49:35 +00:00
|
|
|
def __init__(self, id: str, context: typing.Optional[str], name: str,
|
2019-10-08 12:49:43 +00:00
|
|
|
delay: float, next_due: typing.Optional[float], kwargs: dict,
|
|
|
|
callback: T_CALLBACK):
|
2018-09-28 15:51:36 +00:00
|
|
|
self.id = id
|
2018-10-13 00:13:14 +00:00
|
|
|
self.context = context
|
2018-09-28 15:51:36 +00:00
|
|
|
self.name = name
|
|
|
|
self.delay = delay
|
|
|
|
if next_due:
|
|
|
|
self.next_due = next_due
|
|
|
|
else:
|
|
|
|
self.set_next_due()
|
|
|
|
self.kwargs = kwargs
|
2019-10-08 12:49:43 +00:00
|
|
|
self.callback = callback
|
2018-09-28 15:51:36 +00:00
|
|
|
self._done = False
|
|
|
|
|
|
|
|
def set_next_due(self):
|
|
|
|
self.next_due = time.time()+self.delay
|
2018-10-30 14:58:48 +00:00
|
|
|
def due(self) -> bool:
|
2019-07-09 10:14:05 +00:00
|
|
|
return not self.done() and self.time_left() <= 0
|
2018-10-30 14:58:48 +00:00
|
|
|
def time_left(self) -> float:
|
2018-09-28 15:51:36 +00:00
|
|
|
return self.next_due-time.time()
|
|
|
|
|
|
|
|
def redo(self):
|
|
|
|
self._done = False
|
|
|
|
self.set_next_due()
|
2018-09-28 16:00:49 +00:00
|
|
|
def finish(self):
|
2018-09-28 15:51:36 +00:00
|
|
|
self._done = True
|
2019-07-09 10:14:05 +00:00
|
|
|
def cancel(self):
|
|
|
|
self.finish()
|
2018-10-30 14:58:48 +00:00
|
|
|
def done(self) -> bool:
|
2018-09-28 15:51:36 +00:00
|
|
|
return self._done
|
|
|
|
|
2019-10-08 11:20:08 +00:00
|
|
|
class Timers(PollHook.PollHook):
|
2018-10-30 14:58:48 +00:00
|
|
|
def __init__(self, database: Database.Database,
|
2019-06-26 10:01:09 +00:00
|
|
|
events: EventManager.Events,
|
2018-10-30 14:58:48 +00:00
|
|
|
log: Logging.Log):
|
2018-09-28 16:01:20 +00:00
|
|
|
self.database = database
|
2018-09-28 15:51:36 +00:00
|
|
|
self.events = events
|
|
|
|
self.log = log
|
2018-11-11 19:12:59 +00:00
|
|
|
self.timers = [] # type: typing.List[Timer]
|
|
|
|
self.context_timers = {} # type: typing.Dict[str, typing.List[Timer]]
|
2018-10-12 16:54:15 +00:00
|
|
|
|
2018-10-30 14:58:48 +00:00
|
|
|
def new_context(self, context: str) -> "TimersContext":
|
2018-10-12 16:54:15 +00:00
|
|
|
return TimersContext(self, context)
|
2018-09-28 15:51:36 +00:00
|
|
|
|
2018-10-30 14:58:48 +00:00
|
|
|
def setup(self, timers: typing.List[typing.Tuple[str, dict]]):
|
2018-09-28 15:51:36 +00:00
|
|
|
for name, timer in timers:
|
|
|
|
id = name.split("timer-", 1)[1]
|
2018-10-30 17:49:35 +00:00
|
|
|
self._add(None, timer["name"], timer["delay"], timer[
|
2018-09-28 15:51:36 +00:00
|
|
|
"next-due"], id, False, timer["kwargs"])
|
|
|
|
|
2018-10-30 14:58:48 +00:00
|
|
|
def _persist(self, timer: Timer):
|
2018-09-28 16:01:20 +00:00
|
|
|
self.database.bot_settings.set("timer-%s" % timer.id, {
|
2018-09-28 15:51:36 +00:00
|
|
|
"name": timer.name, "delay": timer.delay,
|
|
|
|
"next-due": timer.next_due, "kwargs": timer.kwargs})
|
2018-10-30 14:58:48 +00:00
|
|
|
def _remove(self, timer: Timer):
|
2018-10-13 08:10:26 +00:00
|
|
|
if timer.context:
|
2018-10-13 00:13:14 +00:00
|
|
|
self.context_timers[timer.context].remove(timer)
|
|
|
|
if not self.context_timers[timer.context]:
|
2018-10-13 08:16:53 +00:00
|
|
|
del self.context_timers[timer.context]
|
2018-10-13 00:13:14 +00:00
|
|
|
else:
|
|
|
|
self.timers.remove(timer)
|
2018-09-28 16:01:20 +00:00
|
|
|
self.database.bot_settings.delete("timer-%s" % timer.id)
|
2018-09-28 15:51:36 +00:00
|
|
|
|
2019-10-08 12:49:43 +00:00
|
|
|
def add(self, name: str, callback: T_CALLBACK, delay: float,
|
|
|
|
next_due: float=None, **kwargs) -> Timer:
|
|
|
|
return self._add(None, name, delay, next_due, None, False, kwargs,
|
|
|
|
callback=callback)
|
2018-10-30 14:58:48 +00:00
|
|
|
def add_persistent(self, name: str, delay: float, next_due: float=None,
|
2019-07-09 10:16:34 +00:00
|
|
|
**kwargs) -> Timer:
|
|
|
|
return self._add(None, name, delay, next_due, None, True, kwargs)
|
2018-10-30 17:49:35 +00:00
|
|
|
def _add(self, context: typing.Optional[str], name: str, delay: float,
|
|
|
|
next_due: typing.Optional[float], id: typing.Optional[str],
|
2019-10-08 12:49:43 +00:00
|
|
|
persist: bool, kwargs: dict, callback: T_CALLBACK=None) -> Timer:
|
2018-10-30 17:49:35 +00:00
|
|
|
id = id or str(uuid.uuid4())
|
2019-10-08 12:49:43 +00:00
|
|
|
|
|
|
|
if not callback:
|
|
|
|
callback = lambda timer: self.events.on("timer.%s" % name).call(
|
|
|
|
timer=timer, **kwargs)
|
|
|
|
|
|
|
|
timer = Timer(id, context, name, delay, next_due, kwargs,
|
|
|
|
callback=callback)
|
2018-09-28 15:51:36 +00:00
|
|
|
if persist:
|
|
|
|
self._persist(timer)
|
2018-10-12 16:54:15 +00:00
|
|
|
|
|
|
|
if context and not persist:
|
|
|
|
if not context in self.context_timers:
|
|
|
|
self.context_timers[context] = []
|
|
|
|
self.context_timers[context].append(timer)
|
|
|
|
else:
|
|
|
|
self.timers.append(timer)
|
2019-07-09 10:16:34 +00:00
|
|
|
return timer
|
2018-09-28 15:51:36 +00:00
|
|
|
|
2018-10-30 17:49:35 +00:00
|
|
|
def next(self) -> typing.Optional[float]:
|
2019-06-01 14:05:57 +00:00
|
|
|
times = list(filter(None,
|
|
|
|
[timer.time_left() for timer in self.get_timers()]))
|
2018-09-28 15:51:36 +00:00
|
|
|
if not times:
|
|
|
|
return None
|
|
|
|
return max(min(times), 0)
|
|
|
|
|
2018-10-30 14:58:48 +00:00
|
|
|
def get_timers(self) -> typing.List[Timer]:
|
2018-10-12 16:54:15 +00:00
|
|
|
return self.timers + sum(self.context_timers.values(), [])
|
|
|
|
|
2019-02-10 12:58:15 +00:00
|
|
|
def find_all(self, name: str) -> typing.List[Timer]:
|
|
|
|
name_lower = name.lower()
|
|
|
|
timers = self.get_timers()
|
|
|
|
found = [] # type: typing.List[Timer]
|
|
|
|
for timer in timers:
|
|
|
|
if timer.name.lower() == name_lower:
|
|
|
|
found.append(timer)
|
|
|
|
|
|
|
|
return found
|
|
|
|
|
2018-09-28 15:51:36 +00:00
|
|
|
def call(self):
|
2018-10-12 16:54:15 +00:00
|
|
|
for timer in self.get_timers():
|
2018-09-28 15:51:36 +00:00
|
|
|
if timer.due():
|
|
|
|
timer.finish()
|
2019-10-08 12:49:43 +00:00
|
|
|
timer.callback(timer)
|
2019-07-09 10:14:05 +00:00
|
|
|
if timer.done():
|
|
|
|
self._remove(timer)
|
2018-10-12 16:54:15 +00:00
|
|
|
|
2018-10-30 14:58:48 +00:00
|
|
|
def purge_context(self, context: str):
|
2018-10-12 16:54:15 +00:00
|
|
|
if context in self.context_timers:
|
|
|
|
del self.context_timers[context]
|
2018-10-30 14:58:48 +00:00
|
|
|
|
|
|
|
class TimersContext(object):
|
|
|
|
def __init__(self, parent: Timers, context: str):
|
|
|
|
self._parent = parent
|
|
|
|
self.context = context
|
2019-10-08 12:49:43 +00:00
|
|
|
def add(self, name: str, callback: T_CALLBACK, delay: float,
|
|
|
|
next_due: float=None, **kwargs) -> Timer:
|
2019-07-09 10:16:34 +00:00
|
|
|
return self._parent._add(self.context, name, delay, next_due, None,
|
2019-10-08 12:49:43 +00:00
|
|
|
False, kwargs, callback=callback)
|
2018-10-30 14:58:48 +00:00
|
|
|
def add_persistent(self, name: str, delay: float, next_due: float=None,
|
2019-07-09 10:16:34 +00:00
|
|
|
**kwargs) -> Timer:
|
|
|
|
return self._parent._add(None, name, delay, next_due, None, True,
|
2018-10-30 14:58:48 +00:00
|
|
|
kwargs)
|
2019-02-10 13:01:52 +00:00
|
|
|
def find_all(self, name: str) -> typing.List[Timer]:
|
|
|
|
return self._parent.find_all(name)
|