]> git.treefish.org Git - mtxbot.git/blob - src/presence.py
7430ee814f4a5e02b2138fed820473964ba16454
[mtxbot.git] / src / presence.py
1 import aiofiles
2 import asyncio
3 import base64
4 import errno
5 import logging
6 import posix
7
8 class Presence:
9     def __init__(self, room_name, fifo_path):
10         self._room_name = room_name
11         self._fifo_path = fifo_path
12         self._joined_room_id = None
13
14     async def run(self, client):
15         self._log(logging.INFO, "Presence running")
16         self._stop = False
17
18         done, pending = await asyncio.wait(
19             [asyncio.create_task( self._room_joining_loop(client) ),
20              asyncio.create_task( self._message_loop(client) )],
21             return_when = asyncio.FIRST_EXCEPTION
22         )
23
24         for task in done:
25             task.result()
26
27         self._log(logging.INFO, "Presence stopped")
28
29     def stop(self):
30         asyncio.get_running_loop().call_soon_threadsafe(
31             Presence._do_stop, self
32         )
33
34     def _do_stop(self):
35         self._stop = True
36         self._tickle_fifo()
37
38     async def _room_joining_loop(self, client):
39         while not self._stop:
40             await client.sync()
41
42             prior_joined_room_id = self._joined_room_id
43             self._joined_room_id = await self._get_joined_room_id(client)
44
45             if self._joined_room_id != None:
46                 if self._joined_room_id != prior_joined_room_id:
47                     self._log(logging.INFO, "Joined room")
48             else:
49                 if self._joined_room_id != prior_joined_room_id:
50                     self._log(logging.INFO, "Got kicked out of room?")
51                 else:
52                     self._log(logging.INFO, "Not yet a room member")
53                 was_invited = False
54                 for room_id, room in client.invited_rooms.items():
55                     if room.display_name == self._room_name:
56                         was_invited = True
57                         self._log(logging.INFO, "Joining room")
58                         await client.join(room_id)
59                         break
60                 if not was_invited:
61                     self._log(logging.INFO, "Got no room invite yet")
62
63             await asyncio.sleep(3.0)
64
65     async def _message_loop(self, client):
66         if self._stop:
67             return
68         async for line in self._read_fifo(self._fifo_path):
69             if self._stop:
70                 break
71             if self._joined_room_id != None:
72                 try:
73                     msgB64Bytes = line.rstrip("\n").encode("UTF-8")
74                     msgBytes = base64.b64decode(msgB64Bytes)
75                     msgStr = msgBytes.decode("UTF-8")
76                 except:
77                     self._log(logging.WARNING, "Error decoding message")
78                     continue
79                 await client.room_send(
80                     room_id=self._joined_room_id,
81                     message_type="m.room.message",
82                     content={
83                         "msgtype": "m.text",
84                         "body": msgStr
85                     }
86                 )
87             else:
88                 self._log(logging.WARNING, "Dropping message cause no room joined")
89
90     def _tickle_fifo(self):
91         fifo = -1
92         try:
93             fifo = posix.open(self._fifo_path, posix.O_WRONLY | posix.O_NONBLOCK)
94             posix.write(fifo, "\n".encode())
95         except OSError as e:
96             if e.errno == errno.ENXIO:
97                 pass
98         finally:
99             if fifo != -1:
100                 posix.close(fifo)
101
102     async def _get_joined_room_id(self, client):
103         joined_room_ids = ( await client.joined_rooms() ).rooms
104         for room_id, room in client.rooms.items():
105             if room.display_name == self._room_name:
106                 if room_id in joined_room_ids:
107                     return room_id
108         return None
109
110     def _log(self, level, msg, *args, **kwargs):
111         logging.log(level, "P{%s}: %s" % (self._room_name, msg), *args, **kwargs)
112
113     @staticmethod
114     async def _read_fifo(file_path):
115         while True:
116             async with aiofiles.open(file_path) as fifo:
117                 async for line in fifo:
118                     yield(line)