|
@@ -15,13 +15,14 @@
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
from __future__ import annotations
|
|
from __future__ import annotations
|
|
|
|
|
|
-from typing import TYPE_CHECKING
|
|
|
|
|
|
+from typing import TYPE_CHECKING, Awaitable
|
|
import asyncio
|
|
import asyncio
|
|
import logging
|
|
import logging
|
|
|
|
|
|
from mausignald import SignaldClient
|
|
from mausignald import SignaldClient
|
|
from mausignald.types import (
|
|
from mausignald.types import (
|
|
Address,
|
|
Address,
|
|
|
|
+ ErrorMessage,
|
|
IncomingMessage,
|
|
IncomingMessage,
|
|
MessageData,
|
|
MessageData,
|
|
OfferMessageType,
|
|
OfferMessageType,
|
|
@@ -32,7 +33,7 @@ from mausignald.types import (
|
|
TypingMessage,
|
|
TypingMessage,
|
|
WebsocketConnectionStateChangeEvent,
|
|
WebsocketConnectionStateChangeEvent,
|
|
)
|
|
)
|
|
-from mautrix.types import MessageType
|
|
|
|
|
|
+from mautrix.types import EventID, MessageType, TextMessageEventContent
|
|
from mautrix.util.logging import TraceLogger
|
|
from mautrix.util.logging import TraceLogger
|
|
|
|
|
|
from . import portal as po, puppet as pu, user as u
|
|
from . import portal as po, puppet as pu, user as u
|
|
@@ -50,12 +51,15 @@ class SignalHandler(SignaldClient):
|
|
loop: asyncio.AbstractEventLoop
|
|
loop: asyncio.AbstractEventLoop
|
|
data_dir: str
|
|
data_dir: str
|
|
delete_unknown_accounts: bool
|
|
delete_unknown_accounts: bool
|
|
|
|
+ error_message_events: dict[tuple[Address, str, int], Awaitable[EventID] | None]
|
|
|
|
|
|
def __init__(self, bridge: "SignalBridge") -> None:
|
|
def __init__(self, bridge: "SignalBridge") -> None:
|
|
super().__init__(bridge.config["signal.socket_path"], loop=bridge.loop)
|
|
super().__init__(bridge.config["signal.socket_path"], loop=bridge.loop)
|
|
self.data_dir = bridge.config["signal.data_dir"]
|
|
self.data_dir = bridge.config["signal.data_dir"]
|
|
self.delete_unknown_accounts = bridge.config["signal.delete_unknown_accounts_on_start"]
|
|
self.delete_unknown_accounts = bridge.config["signal.delete_unknown_accounts_on_start"]
|
|
|
|
+ self.error_message_events = {}
|
|
self.add_event_handler(IncomingMessage, self.on_message)
|
|
self.add_event_handler(IncomingMessage, self.on_message)
|
|
|
|
+ self.add_event_handler(ErrorMessage, self.on_error_message)
|
|
self.add_event_handler(
|
|
self.add_event_handler(
|
|
WebsocketConnectionStateChangeEvent, self.on_websocket_connection_state_change
|
|
WebsocketConnectionStateChangeEvent, self.on_websocket_connection_state_change
|
|
)
|
|
)
|
|
@@ -90,6 +94,55 @@ class SignalHandler(SignaldClient):
|
|
self.log.debug("Sync message includes groups meta, syncing groups...")
|
|
self.log.debug("Sync message includes groups meta, syncing groups...")
|
|
await user.sync_groups()
|
|
await user.sync_groups()
|
|
|
|
|
|
|
|
+ try:
|
|
|
|
+ event_id_future = self.error_message_events.pop(
|
|
|
|
+ (sender.address, user.username, evt.timestamp)
|
|
|
|
+ )
|
|
|
|
+ except KeyError:
|
|
|
|
+ pass
|
|
|
|
+ else:
|
|
|
|
+ self.log.debug(f"Got previously errored message {evt.timestamp} from {sender.address}")
|
|
|
|
+ event_id = await event_id_future if event_id_future is not None else None
|
|
|
|
+ if event_id is not None:
|
|
|
|
+ portal = await po.Portal.get_by_chat_id(sender.address, receiver=user.username)
|
|
|
|
+ if portal and portal.mxid:
|
|
|
|
+ await sender.intent_for(portal).redact(portal.mxid, event_id)
|
|
|
|
+
|
|
|
|
+ async def on_error_message(self, err: ErrorMessage) -> None:
|
|
|
|
+ self.log.warning(
|
|
|
|
+ f"Error reading message from {err.data.sender}/{err.data.sender_device} "
|
|
|
|
+ f"(timestamp: {err.data.timestamp}, content hint: {err.data.content_hint}): "
|
|
|
|
+ f"{err.data.message}"
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ sender = await pu.Puppet.get_by_address(Address.parse(err.data.sender))
|
|
|
|
+ user = await u.User.get_by_username(err.account)
|
|
|
|
+ portal = await po.Portal.get_by_chat_id(sender.address, receiver=user.username)
|
|
|
|
+ if not portal or not portal.mxid:
|
|
|
|
+ return
|
|
|
|
+
|
|
|
|
+ # Add the error to the error_message_events dictionary, then wait for 10 seconds until
|
|
|
|
+ # sending an error. If a success for the timestamp comes in before the 10 seconds is up,
|
|
|
|
+ # don't send the error message.
|
|
|
|
+ error_message_event_key = (sender.address, user.username, err.data.timestamp)
|
|
|
|
+ self.error_message_events[error_message_event_key] = None
|
|
|
|
+
|
|
|
|
+ await asyncio.sleep(10)
|
|
|
|
+
|
|
|
|
+ err_text = (
|
|
|
|
+ "There was an error receiving a message. Check your Signal app for missing messages."
|
|
|
|
+ )
|
|
|
|
+ if error_message_event_key in self.error_message_events:
|
|
|
|
+ fut = self.error_message_events[error_message_event_key] = self.loop.create_future()
|
|
|
|
+ event_id = None
|
|
|
|
+ try:
|
|
|
|
+ event_id = await portal._send_message(
|
|
|
|
+ intent=sender.intent_for(portal),
|
|
|
|
+ content=TextMessageEventContent(body=err_text, msgtype=MessageType.NOTICE),
|
|
|
|
+ )
|
|
|
|
+ finally:
|
|
|
|
+ fut.set_result(event_id)
|
|
|
|
+
|
|
@staticmethod
|
|
@staticmethod
|
|
async def on_websocket_connection_state_change(
|
|
async def on_websocket_connection_state_change(
|
|
evt: WebsocketConnectionStateChangeEvent,
|
|
evt: WebsocketConnectionStateChangeEvent,
|