2015-12-31 14:55:15 +01:00
|
|
|
#!/usr/bin/env python
|
|
|
|
#
|
|
|
|
# A library that provides a Python interface to the Telegram Bot API
|
|
|
|
# Copyright (C) 2015 Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU Lesser Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU Lesser Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Lesser Public License
|
|
|
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
|
|
|
|
|
|
|
"""
|
|
|
|
This module contains the class JobQueue
|
|
|
|
"""
|
|
|
|
|
|
|
|
import logging
|
|
|
|
import time
|
2016-01-04 00:01:00 +01:00
|
|
|
from threading import Thread, Lock
|
2015-12-31 14:55:15 +01:00
|
|
|
|
|
|
|
try:
|
2016-01-04 02:05:39 +01:00
|
|
|
from queue import PriorityQueue
|
2015-12-31 14:55:15 +01:00
|
|
|
except ImportError:
|
2016-01-04 02:05:39 +01:00
|
|
|
from Queue import PriorityQueue
|
2015-12-31 14:55:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
class JobQueue(object):
|
2016-01-04 01:56:22 +01:00
|
|
|
"""
|
|
|
|
This class allows you to periodically perform tasks with the bot.
|
2015-12-31 14:55:15 +01:00
|
|
|
|
|
|
|
Attributes:
|
|
|
|
tick_interval (float):
|
|
|
|
queue (PriorityQueue):
|
|
|
|
bot (Bot):
|
|
|
|
running (bool):
|
|
|
|
|
|
|
|
Args:
|
|
|
|
bot (Bot): The bot instance that should be passed to the jobs
|
|
|
|
|
|
|
|
Keyword Args:
|
|
|
|
tick_interval (Optional[float]): The interval this queue should check
|
|
|
|
the newest task in seconds. Defaults to 1.0
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, bot, tick_interval=1.0):
|
|
|
|
self.tick_interval = tick_interval
|
|
|
|
self.queue = PriorityQueue()
|
|
|
|
self.bot = bot
|
|
|
|
self.logger = logging.getLogger(__name__)
|
|
|
|
self.__lock = Lock()
|
|
|
|
self.running = False
|
|
|
|
|
2016-01-05 13:32:19 +01:00
|
|
|
def put(self,
|
|
|
|
run,
|
|
|
|
interval,
|
|
|
|
repeat=True,
|
|
|
|
next_t=None,
|
|
|
|
prevent_autostart=False):
|
2015-12-31 14:55:15 +01:00
|
|
|
"""
|
2016-01-05 13:32:19 +01:00
|
|
|
Queue a new job. If the JobQueue is not running, it will be started.
|
2015-12-31 14:55:15 +01:00
|
|
|
|
|
|
|
Args:
|
|
|
|
run (function): A function that takes the parameter `bot`
|
|
|
|
interval (float): The interval in seconds in which `run` should be
|
|
|
|
executed
|
2016-01-05 13:32:19 +01:00
|
|
|
repeat (Optional[bool]): If `False`, job will only be executed once
|
2015-12-31 14:55:15 +01:00
|
|
|
next_t (Optional[float]): Time in seconds in which run should be
|
|
|
|
executed first. Defaults to `interval`
|
2016-01-05 13:32:19 +01:00
|
|
|
prevent_autostart (Optional[bool]): If `True`, the job queue will
|
|
|
|
not be started automatically if it is not running.
|
2015-12-31 14:55:15 +01:00
|
|
|
"""
|
|
|
|
name = run.__name__
|
|
|
|
|
|
|
|
job = JobQueue.Job()
|
|
|
|
job.run = run
|
|
|
|
job.interval = interval
|
|
|
|
job.name = name
|
2016-01-04 01:56:22 +01:00
|
|
|
job.repeat = repeat
|
2015-12-31 14:55:15 +01:00
|
|
|
|
|
|
|
if next_t is None:
|
|
|
|
next_t = interval
|
|
|
|
|
2016-01-04 00:01:00 +01:00
|
|
|
next_t += time.time()
|
|
|
|
|
2016-01-04 02:05:39 +01:00
|
|
|
self.logger.debug("Putting a %s with t=%f" % (job.name, next_t))
|
2015-12-31 14:55:15 +01:00
|
|
|
self.queue.put((next_t, job))
|
|
|
|
|
2016-01-05 13:32:19 +01:00
|
|
|
if not self.running and not prevent_autostart:
|
|
|
|
self.logger.info("Auto-starting JobQueue")
|
|
|
|
self.start()
|
|
|
|
|
2015-12-31 14:55:15 +01:00
|
|
|
def tick(self):
|
|
|
|
"""
|
|
|
|
Run all jobs that are due and re-enqueue them with their interval
|
|
|
|
"""
|
|
|
|
now = time.time()
|
|
|
|
|
2016-01-04 02:05:39 +01:00
|
|
|
self.logger.debug("Ticking jobs with t=%f" % now)
|
2015-12-31 14:55:15 +01:00
|
|
|
while not self.queue.empty():
|
|
|
|
t, j = self.queue.queue[0]
|
2016-01-04 02:05:39 +01:00
|
|
|
self.logger.debug("Peeked at %s with t=%f" % (j.name, t))
|
2015-12-31 14:55:15 +01:00
|
|
|
|
|
|
|
if t < now:
|
|
|
|
self.queue.get()
|
2016-01-04 02:05:39 +01:00
|
|
|
self.logger.info("Running job %s" % j.name)
|
2016-01-04 01:56:22 +01:00
|
|
|
try:
|
|
|
|
j.run(self.bot)
|
|
|
|
except:
|
|
|
|
self.logger.exception("An uncaught error was raised while "
|
2016-01-04 02:05:39 +01:00
|
|
|
"executing job %s" % j.name)
|
2016-01-04 01:56:22 +01:00
|
|
|
if j.repeat:
|
|
|
|
self.put(j.run, j.interval)
|
2015-12-31 14:55:15 +01:00
|
|
|
continue
|
|
|
|
|
|
|
|
self.logger.debug("Next task isn't due yet. Finished!")
|
|
|
|
break
|
|
|
|
|
|
|
|
def start(self):
|
|
|
|
"""
|
2016-01-04 00:01:00 +01:00
|
|
|
Starts the job_queue thread.
|
2015-12-31 14:55:15 +01:00
|
|
|
"""
|
|
|
|
self.__lock.acquire()
|
|
|
|
if not self.running:
|
|
|
|
self.running = True
|
|
|
|
self.__lock.release()
|
2016-01-04 00:01:00 +01:00
|
|
|
job_queue_thread = Thread(target=self._start,
|
|
|
|
name="job_queue")
|
|
|
|
job_queue_thread.start()
|
2016-01-04 01:56:22 +01:00
|
|
|
self.logger.info('Job Queue thread started')
|
2015-12-31 14:55:15 +01:00
|
|
|
else:
|
|
|
|
self.__lock.release()
|
|
|
|
|
2016-01-04 00:01:00 +01:00
|
|
|
def _start(self):
|
|
|
|
"""
|
|
|
|
Thread target of thread 'job_queue'. Runs in background and performs
|
|
|
|
ticks on the job queue.
|
|
|
|
"""
|
|
|
|
while self.running:
|
|
|
|
self.tick()
|
|
|
|
time.sleep(self.tick_interval)
|
|
|
|
|
2016-01-04 01:56:22 +01:00
|
|
|
self.logger.info('Job Queue thread stopped')
|
|
|
|
|
2015-12-31 14:55:15 +01:00
|
|
|
def stop(self):
|
|
|
|
"""
|
|
|
|
Stops the thread
|
|
|
|
"""
|
|
|
|
with self.__lock:
|
|
|
|
self.running = False
|
|
|
|
|
|
|
|
class Job(object):
|
|
|
|
""" Inner class that represents a job """
|
|
|
|
interval = None
|
|
|
|
name = None
|
2016-01-04 01:56:22 +01:00
|
|
|
repeat = None
|
2015-12-31 14:55:15 +01:00
|
|
|
|
|
|
|
def run(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def __lt__(self, other):
|
|
|
|
return False
|