浏览代码

Add option to delete unknown signald accounts on startup

Tulir Asokan 4 年之前
父节点
当前提交
4941a27ba2
共有 5 个文件被更改,包括 34 次插入11 次删除
  1. 4 0
      mausignald/signald.py
  2. 1 0
      mautrix_signal/config.py
  3. 3 0
      mautrix_signal/example-config.yaml
  4. 25 0
      mautrix_signal/signal.py
  5. 1 11
      mautrix_signal/user.py

+ 4 - 0
mausignald/signald.py

@@ -148,6 +148,10 @@ class SignaldClient(SignaldRPCClient):
         await self.request_v1("mark_read", account=username, timestamps=timestamps, when=when,
                               to=sender.serialize())
 
+    async def list_accounts(self) -> List[Account]:
+        resp = await self.request_v1("list_accounts")
+        return [Account.deserialize(acc) for acc in resp.get("accounts", [])]
+
     async def list_contacts(self, username: str) -> List[Profile]:
         resp = await self.request_v1("list_contacts", account=username)
         return [Profile.deserialize(contact) for contact in resp["profiles"]]

+ 1 - 0
mautrix_signal/config.py

@@ -51,6 +51,7 @@ class Config(BaseBridgeConfig):
         copy("signal.outgoing_attachment_dir")
         copy("signal.avatar_dir")
         copy("signal.data_dir")
+        copy("signal.delete_unknown_accounts_on_start")
         copy("signal.remove_file_after_handling")
 
         copy("metrics.enabled")

+ 3 - 0
mautrix_signal/example-config.yaml

@@ -75,6 +75,9 @@ signal:
     avatar_dir: ~/.config/signald/avatars
     # Directory where signald stores auth data. Used to delete data when logging out.
     data_dir: ~/.config/signald/data
+    # Whether or not unknown signald accounts should be deleted when the bridge is started.
+    # When this is enabled, any UserInUse errors should be resolved by restarting the bridge.
+    delete_unknown_accounts_on_start: false
     # Whether or not message attachments should be removed from disk after they're bridged.
     remove_file_after_handling: true
 

+ 25 - 0
mautrix_signal/signal.py

@@ -16,6 +16,8 @@
 from typing import Optional, List, TYPE_CHECKING
 import asyncio
 import logging
+import os.path
+import shutil
 
 from mausignald import SignaldClient
 from mausignald.types import (Message, MessageData, Address, TypingNotification, TypingAction,
@@ -35,9 +37,13 @@ SIGNAL_TYPING_TIMEOUT = 15000
 class SignalHandler(SignaldClient):
     log: TraceLogger = logging.getLogger("mau.signal")
     loop: asyncio.AbstractEventLoop
+    data_dir: str
+    delete_unknown_accounts: bool
 
     def __init__(self, bridge: 'SignalBridge') -> None:
         super().__init__(bridge.config["signal.socket_path"], loop=bridge.loop)
+        self.data_dir = bridge.config["signal.data_dir"]
+        self.delete_unknown_accounts = bridge.config["signal.delete_unknown_accounts_on_start"]
         self.add_event_handler(Message, self.on_message)
         self.add_event_handler(ListenEvent, self.on_listen)
 
@@ -141,12 +147,31 @@ class SignalHandler(SignaldClient):
             portal = await po.Portal.get_by_mxid(message.mx_room)
             await sender.intent_for(portal).mark_read(portal.mxid, message.mxid)
 
+    def delete_data(self, username: str) -> None:
+        path = os.path.join(self.data_dir, username)
+        extra_dir = f"{path}.d/"
+        try:
+            self.log.debug("Removing %s", path)
+            os.remove(path)
+        except FileNotFoundError as e:
+            self.log.warning(f"Failed to remove signald data file: {e}")
+        self.log.debug("Removing %s", extra_dir)
+        shutil.rmtree(extra_dir, ignore_errors=True)
+
     async def start(self) -> None:
         await self.connect()
+        known_usernames = set()
         async for user in u.User.all_logged_in():
             # TODO report errors to user?
+            known_usernames.add(user.username)
             if await self.subscribe(user.username):
                 asyncio.create_task(user.sync())
+        if self.delete_unknown_accounts:
+            self.log.debug("Checking for unknown accounts to delete")
+            for account in await self.list_accounts():
+                if account.account_id not in known_usernames:
+                    self.log.warning(f"Unknown account ID {account.account_id}, deleting...")
+                    self.delete_data(account.account_id)
 
     async def stop(self) -> None:
         await self.disconnect()

+ 1 - 11
mautrix_signal/user.py

@@ -17,8 +17,6 @@ from typing import Union, Dict, Optional, AsyncGenerator, TYPE_CHECKING, cast
 from collections import defaultdict
 from uuid import UUID
 import asyncio
-import os.path
-import shutil
 
 from mausignald.types import Account, Address, Profile, Group, GroupV2, ListenEvent, ListenAction
 from mautrix.bridge import BaseUser, async_getter_lock
@@ -94,15 +92,7 @@ class User(DBUser, BaseUser):
         await self.bridge.signal.unsubscribe(username)
         # Wait a while for signald to finish disconnecting
         await asyncio.sleep(1)
-        path = os.path.join(self.config["signal.data_dir"], username)
-        extra_dir = f"{path}.d/"
-        try:
-            self.log.debug("Removing %s", path)
-            os.remove(path)
-        except FileNotFoundError as e:
-            self.log.warning(f"Failed to remove signald data file: {e}")
-        self.log.debug("Removing %s", extra_dir)
-        shutil.rmtree(extra_dir, ignore_errors=True)
+        self.bridge.signal.delete_data(username)
         self._track_metric(METRIC_LOGGED_IN, False)
 
     async def on_signin(self, account: Account) -> None: