annotate bots/unread_to_mqtt.py @ 1:2a288d2cb88c

add unread_to_mqtt bridge
author drewp@bigasterisk.com
date Tue, 11 Feb 2025 19:20:47 -0800
parents
children 6fc2c741f1a6
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
1
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
1 import asyncio
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
2 import json
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
3 import logging
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
4 import sys
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
5 from dataclasses import dataclass
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
6 from typing import cast
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
7
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
8 import aiomqtt
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
9
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
10 from bigastbot import BigAstBot
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
11
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
12 logging.basicConfig(level=logging.DEBUG,
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
13 format='%(asctime)s %(levelname)s %(name)s %(message)s',
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
14 datefmt='%Y-%m-%d %H:%M:%S')
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
15 log = logging.getLogger()
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
16
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
17
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
18 @dataclass
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
19 class UnreadToMqtt:
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
20 email: str
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
21 mqtt: aiomqtt.Client
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
22
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
23 async def run(self):
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
24 while True:
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
25 try:
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
26 log.info(f'connecting to zulip as {self.email}')
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
27 bot = BigAstBot(email=self.email)
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
28 self.unread_msg_ids = set()
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
29 self.last_sent: int | None = None
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
30
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
31 async for ev in bot.get_registration_and_events(event_types=[
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
32 'message',
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
33 'update_message_flags',
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
34 ]):
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
35 await self._update_unreads_with_event(ev)
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
36 except aiomqtt.MqttError:
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
37 raise
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
38 except Exception as e:
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
39 log.error(e)
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
40 await asyncio.sleep(1)
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
41 continue
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
42
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
43 async def _update_unreads_with_event(self, ev):
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
44 if 'unread_msgs' in ev:
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
45 # looks like registration response
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
46 self._on_registration_response(ev)
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
47 elif ev['type'] == 'message':
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
48 self._on_message_event(ev)
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
49 elif ev['type'] == 'update_message_flags':
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
50 self._on_flag_change_event(ev)
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
51
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
52 if self.last_sent != len(self.unread_msg_ids):
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
53 await self._send_to_mqtt(len(self.unread_msg_ids))
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
54
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
55 def _on_flag_change_event(self, ev):
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
56 log.debug("_on_flag_change_event: %s", ev)
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
57 if ev['flag'] == 'read':
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
58 for msg_id in ev['messages']:
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
59 self.unread_msg_ids.discard(msg_id)
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
60
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
61 def _on_message_event(self, ev):
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
62 log.debug("_on_message_event: %s", ev)
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
63 if 'read' not in ev['flags']:
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
64 self.unread_msg_ids.add(ev['message']['id'])
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
65
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
66 def _on_registration_response(self, ev):
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
67 log.debug("_on_registration_response: %s", ev)
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
68 for msg_type in ['pms', 'streams', 'huddles']: # mentions?
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
69 for group in ev['unread_msgs'][msg_type]:
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
70 self.unread_msg_ids.update(group['unread_message_ids'])
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
71
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
72 async def _send_to_mqtt(self, num_unread):
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
73 await self.mqtt.publish(f'/zulip/unread/{self.email}',
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
74 json.dumps({'all': num_unread}),
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
75 retain=True)
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
76 self.last_sent = num_unread
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
77
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
78
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
79 async def main():
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
80 user_emails = sys.argv[1:]
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
81 while True:
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
82 try:
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
83 log.info('connecting to mqtt')
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
84 async with aiomqtt.Client("mqtt2.bigasterisk.com") as client:
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
85 await asyncio.gather(*[
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
86 UnreadToMqtt(email=user_email, mqtt=client).run()
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
87 for user_email in user_emails
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
88 ])
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
89 except aiomqtt.MqttError:
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
90 continue
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
91
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
92
2a288d2cb88c add unread_to_mqtt bridge
drewp@bigasterisk.com
parents:
diff changeset
93 asyncio.run(main())