2017-09-07 19:49:08 +03:00
|
|
|
from collections import deque
|
2017-09-07 21:17:40 +03:00
|
|
|
from datetime import datetime
|
|
|
|
from threading import RLock, Event
|
|
|
|
|
|
|
|
from .tl import types as tl
|
2017-09-07 19:49:08 +03:00
|
|
|
|
|
|
|
|
|
|
|
class UpdateState:
|
|
|
|
"""Used to hold the current state of processed updates.
|
2017-09-08 13:54:38 +03:00
|
|
|
To retrieve an update, .poll() should be called.
|
2017-09-07 19:49:08 +03:00
|
|
|
"""
|
2017-09-08 13:54:38 +03:00
|
|
|
def __init__(self, polling):
|
|
|
|
self._polling = polling
|
2017-09-07 19:58:54 +03:00
|
|
|
self.handlers = []
|
2017-09-07 21:17:40 +03:00
|
|
|
self._updates_lock = RLock()
|
2017-09-07 19:49:08 +03:00
|
|
|
self._updates_available = Event()
|
|
|
|
self._updates = deque()
|
|
|
|
|
2017-09-07 21:17:40 +03:00
|
|
|
# https://core.telegram.org/api/updates
|
|
|
|
self._state = tl.updates.State(0, 0, datetime.now(), 0, 0)
|
|
|
|
|
2017-09-08 13:54:38 +03:00
|
|
|
def can_poll(self):
|
|
|
|
"""Returns True if a call to .poll() won't lock"""
|
2017-09-07 19:49:08 +03:00
|
|
|
return self._updates_available.is_set()
|
|
|
|
|
2017-09-07 21:29:51 +03:00
|
|
|
def poll(self):
|
|
|
|
"""Polls an update or blocks until an update object is available"""
|
2017-09-08 13:54:38 +03:00
|
|
|
if not self._polling:
|
|
|
|
raise ValueError('Updates are not being polled hence not saved.')
|
2017-09-07 21:29:51 +03:00
|
|
|
|
2017-09-07 19:49:08 +03:00
|
|
|
self._updates_available.wait()
|
|
|
|
with self._updates_lock:
|
|
|
|
update = self._updates.popleft()
|
|
|
|
if not self._updates:
|
|
|
|
self._updates_available.clear()
|
|
|
|
|
2017-09-18 11:59:54 +03:00
|
|
|
if isinstance(update, Exception):
|
|
|
|
raise update # Some error was set through .set_error()
|
|
|
|
|
|
|
|
return update
|
2017-09-07 19:49:08 +03:00
|
|
|
|
2017-09-08 13:54:38 +03:00
|
|
|
def get_polling(self):
|
|
|
|
return self._polling
|
|
|
|
|
|
|
|
def set_polling(self, polling):
|
|
|
|
self._polling = polling
|
|
|
|
if not polling:
|
2017-09-07 21:29:51 +03:00
|
|
|
with self._updates_lock:
|
|
|
|
self._updates.clear()
|
|
|
|
|
2017-09-08 13:54:38 +03:00
|
|
|
polling = property(fget=get_polling, fset=set_polling)
|
|
|
|
|
2017-09-18 11:59:54 +03:00
|
|
|
def set_error(self, error):
|
|
|
|
"""Sets an error, so that the next call to .poll() will raise it.
|
|
|
|
Can be (and is) used to pass exceptions between threads.
|
|
|
|
"""
|
|
|
|
with self._updates_lock:
|
|
|
|
# Insert at the beginning so the very next poll causes an error
|
|
|
|
# TODO Should this reset the pts and such?
|
|
|
|
self._updates.insert(0, error)
|
|
|
|
self._updates_available.set()
|
|
|
|
|
|
|
|
def check_error(self):
|
|
|
|
with self._updates_lock:
|
|
|
|
if self._updates and isinstance(self._updates[0], Exception):
|
|
|
|
raise self._updates.pop()
|
|
|
|
|
2017-09-07 19:49:08 +03:00
|
|
|
def process(self, update):
|
|
|
|
"""Processes an update object. This method is normally called by
|
|
|
|
the library itself.
|
|
|
|
"""
|
2017-09-08 19:43:37 +03:00
|
|
|
if not self._polling and not self.handlers:
|
2017-09-07 21:17:40 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
with self._updates_lock:
|
|
|
|
if isinstance(update, tl.updates.State):
|
|
|
|
self._state = update
|
|
|
|
elif not hasattr(update, 'pts') or update.pts > self._state.pts:
|
|
|
|
self._state.pts = getattr(update, 'pts', self._state.pts)
|
|
|
|
for handler in self.handlers:
|
|
|
|
handler(update)
|
2017-09-07 19:58:54 +03:00
|
|
|
|
2017-09-08 13:54:38 +03:00
|
|
|
if self._polling:
|
2017-09-07 21:29:51 +03:00
|
|
|
self._updates.append(update)
|
|
|
|
self._updates_available.set()
|