91 lines
2.7 KiB
Python
91 lines
2.7 KiB
Python
import logging
|
|
from time import sleep, time
|
|
from queue import Queue, Empty
|
|
from threading import Thread
|
|
from contextlib import suppress
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class MessageQueueHandler:
|
|
keys = ['message.queue']
|
|
type_id = 'ControlEventHandler'
|
|
avg_word_len = 5
|
|
drain_poll_freq = 0.2
|
|
|
|
def __init__(self, wpm):
|
|
self.connector = None
|
|
self.wpm = wpm
|
|
self._queue = Queue()
|
|
self._drain_queue = Queue()
|
|
self._thread = Thread(target=self._dispatch_messages)
|
|
|
|
self._commands = {
|
|
'message.queue': self.handle_queue,
|
|
'message.queue.drain': self.handle_drain
|
|
}
|
|
|
|
def _dispatch_messages(self):
|
|
for message in iter(self._queue.get, None):
|
|
message['typing'] = not self._queue.empty()
|
|
self.connector.send_message(message)
|
|
self._sleep(self._get_sleep_time(message))
|
|
|
|
def _get_sleep_time(self, message):
|
|
words_per_min = message['wpm'] if 'wpm' in message else self.wpm
|
|
chars_per_min = self.avg_word_len * words_per_min / 60
|
|
return len(message['message']) / chars_per_min
|
|
|
|
def _sleep(self, seconds):
|
|
poll_freq = self.drain_poll_freq
|
|
if seconds < poll_freq:
|
|
poll_freq = seconds
|
|
|
|
sleep_until = time() + seconds
|
|
while time() < sleep_until:
|
|
sleep(poll_freq)
|
|
with suppress(Empty):
|
|
self._drain_queue.get(block=False)
|
|
self._drain()
|
|
break
|
|
|
|
def _drain(self):
|
|
with suppress(Empty):
|
|
while True:
|
|
message = self._queue.get(block=False)
|
|
if message is None:
|
|
break
|
|
message['typing'] = False
|
|
self.connector.send_message(message)
|
|
|
|
def handle_event(self, message, _):
|
|
try:
|
|
self._commands[message['key']](message)
|
|
except KeyError:
|
|
LOG.error('IGNORING MESSAGE: Invalid message received: %s', message)
|
|
|
|
def handle_queue(self, message):
|
|
for unpacked in self._generate_messages_from_queue(message):
|
|
self._queue.put(unpacked)
|
|
|
|
@staticmethod
|
|
def _generate_messages_from_queue(queue_message):
|
|
for message in queue_message['messages']:
|
|
yield {
|
|
'key': 'message.send',
|
|
**message
|
|
}
|
|
|
|
def handle_drain(self, _):
|
|
self._drain_queue.put(True)
|
|
|
|
def start(self):
|
|
self._thread.start()
|
|
|
|
def cleanup(self):
|
|
# clearing the queue forces the loop in
|
|
# _dispatch_messages to block on _queue.get
|
|
self._queue.queue.clear()
|
|
self._queue.put(None)
|
|
self._thread.join()
|