Pārlūkot izejas kodu

Try generating request IDs that look more like Instagram's

Tulir Asokan 4 gadi atpakaļ
vecāks
revīzija
7959801404

+ 1 - 1
mauigpapi/http/thread.py

@@ -88,7 +88,7 @@ class ThreadAPI(BaseAndroidAPI):
 
     async def broadcast(self, thread_id: str, item_type: ThreadItemType, signed: bool = False,
                         client_context: Optional[str] = None, **kwargs) -> CommandResponse:
-        client_context = client_context or str(uuid4())
+        client_context = client_context or self.state.gen_client_context()
         form = {
             "action": ThreadAction.SEND_ITEM.value,
             "send_attribution": "inbox",

+ 33 - 55
mauigpapi/mqtt/conn.py

@@ -18,9 +18,10 @@ from typing import (Union, Set, Optional, Any, Dict, Awaitable, Type, List, Type
 from collections import defaultdict
 from socket import socket, error as SocketError
 from uuid import uuid4
-import logging
 import urllib.request
+import logging
 import asyncio
+import random
 import zlib
 import time
 import json
@@ -534,14 +535,13 @@ class AndroidMQTT:
             self._client._keepalive = state.keep_alive_timeout
 
     async def send_command(self, thread_id: str, action: ThreadAction,
-                           client_context: Optional[str] = None,
-                           offline_threading_id: Optional[str] = None, **kwargs: Any
+                           client_context: Optional[str] = None, **kwargs: Any
                            ) -> Optional[CommandResponse]:
-        client_context = client_context or str(uuid4())
+        client_context = client_context or self.state.gen_client_context()
         req = {
             "thread_id": thread_id,
             "client_context": client_context,
-            "offline_threading_id": offline_threading_id or client_context,
+            "offline_threading_id": client_context,
             "action": action.value,
             # "device_id": self.state.cookies["ig_did"],
             **kwargs,
@@ -561,97 +561,75 @@ class AndroidMQTT:
             return CommandResponse.parse_json(resp.payload.decode("utf-8"))
 
     def send_item(self, thread_id: str, item_type: ThreadItemType, shh_mode: bool = False,
-                  client_context: Optional[str] = None, offline_threading_id: Optional[str] = None,
-                  **kwargs: Any) -> Awaitable[CommandResponse]:
+                  client_context: Optional[str] = None, **kwargs: Any
+                  ) -> Awaitable[CommandResponse]:
         return self.send_command(thread_id, item_type=item_type.value,
                                  is_shh_mode=str(int(shh_mode)), action=ThreadAction.SEND_ITEM,
-                                 client_context=client_context,
-                                 offline_threading_id=offline_threading_id, **kwargs)
+                                 client_context=client_context, **kwargs)
 
     def send_hashtag(self, thread_id: str, hashtag: str, text: str = "", shh_mode: bool = False,
-                     client_context: Optional[str] = None,
-                     offline_threading_id: Optional[str] = None) -> Awaitable[CommandResponse]:
+                     client_context: Optional[str] = None) -> Awaitable[CommandResponse]:
         return self.send_item(thread_id, text=text, item_id=hashtag, shh_mode=shh_mode,
-                              item_type=ThreadItemType.HASHTAG, client_context=client_context,
-                              offline_threading_id=offline_threading_id)
+                              item_type=ThreadItemType.HASHTAG, client_context=client_context)
 
     def send_like(self, thread_id: str, shh_mode: bool = False,
-                  client_context: Optional[str] = None, offline_threading_id: Optional[str] = None,
-                  ) -> Awaitable[CommandResponse]:
+                  client_context: Optional[str] = None) -> Awaitable[CommandResponse]:
         return self.send_item(thread_id, shh_mode=shh_mode, item_type=ThreadItemType.LIKE,
-                              client_context=client_context,
-                              offline_threading_id=offline_threading_id)
+                              client_context=client_context)
 
     def send_location(self, thread_id: str, venue_id: str, text: str = "",
-                      shh_mode: bool = False, client_context: Optional[str] = None,
-                      offline_threading_id: Optional[str] = None,
+                      shh_mode: bool = False, client_context: Optional[str] = None
                       ) -> Awaitable[CommandResponse]:
         return self.send_item(thread_id, text=text, item_id=venue_id, shh_mode=shh_mode,
-                              item_type=ThreadItemType.LOCATION, client_context=client_context,
-                              offline_threading_id=offline_threading_id)
+                              item_type=ThreadItemType.LOCATION, client_context=client_context)
 
     def send_media(self, thread_id: str, media_id: str, text: str = "", shh_mode: bool = False,
-                   client_context: Optional[str] = None,
-                   offline_threading_id: Optional[str] = None) -> Awaitable[CommandResponse]:
+                   client_context: Optional[str] = None) -> Awaitable[CommandResponse]:
         return self.send_item(thread_id, text=text, media_id=media_id, shh_mode=shh_mode,
-                              item_type=ThreadItemType.MEDIA_SHARE, client_context=client_context,
-                              offline_threading_id=offline_threading_id)
+                              item_type=ThreadItemType.MEDIA_SHARE, client_context=client_context)
 
     def send_profile(self, thread_id: str, user_id: str, text: str = "", shh_mode: bool = False,
-                     client_context: Optional[str] = None,
-                     offline_threading_id: Optional[str] = None) -> Awaitable[CommandResponse]:
+                     client_context: Optional[str] = None) -> Awaitable[CommandResponse]:
         return self.send_item(thread_id, text=text, item_id=user_id, shh_mode=shh_mode,
-                              item_type=ThreadItemType.PROFILE, client_context=client_context,
-                              offline_threading_id=offline_threading_id)
+                              item_type=ThreadItemType.PROFILE, client_context=client_context)
 
     def send_reaction(self, thread_id: str, emoji: str, item_id: str,
                       reaction_status: ReactionStatus = ReactionStatus.CREATED,
                       target_item_type: ThreadItemType = ThreadItemType.TEXT,
-                      shh_mode: bool = False, client_context: Optional[str] = None,
-                      offline_threading_id: Optional[str] = None) -> Awaitable[CommandResponse]:
+                      shh_mode: bool = False, client_context: Optional[str] = None
+                      ) -> Awaitable[CommandResponse]:
         return self.send_item(thread_id, reaction_status=reaction_status.value, node_type="item",
                               reaction_type="like", target_item_type=target_item_type.value,
                               emoji=emoji, item_id=item_id, reaction_action_source="double_tap",
                               shh_mode=shh_mode, item_type=ThreadItemType.REACTION,
-                              client_context=client_context,
-                              offline_threading_id=offline_threading_id)
+                              client_context=client_context)
 
     def send_user_story(self, thread_id: str, media_id: str, text: str = "",
-                        shh_mode: bool = False, client_context: Optional[str] = None,
-                        offline_threading_id: Optional[str] = None) -> Awaitable[CommandResponse]:
+                        shh_mode: bool = False, client_context: Optional[str] = None
+                        ) -> Awaitable[CommandResponse]:
         return self.send_item(thread_id, text=text, item_id=media_id, shh_mode=shh_mode,
-                              item_type=ThreadItemType.REEL_SHARE, client_context=client_context,
-                              offline_threading_id=offline_threading_id)
+                              item_type=ThreadItemType.REEL_SHARE, client_context=client_context)
 
     def send_text(self, thread_id: str, text: str = "", shh_mode: bool = False,
-                  client_context: Optional[str] = None, offline_threading_id: Optional[str] = None
-                  ) -> Awaitable[CommandResponse]:
+                  client_context: Optional[str] = None) -> Awaitable[CommandResponse]:
         return self.send_item(thread_id, text=text, shh_mode=shh_mode,
-                              item_type=ThreadItemType.TEXT, client_context=client_context,
-                              offline_threading_id=offline_threading_id)
+                              item_type=ThreadItemType.TEXT, client_context=client_context)
 
-    def mark_seen(self, thread_id: str, item_id: str, client_context: Optional[str] = None,
-                  offline_threading_id: Optional[str] = None) -> Awaitable[None]:
+    def mark_seen(self, thread_id: str, item_id: str, client_context: Optional[str] = None
+                  ) -> Awaitable[None]:
         return self.send_command(thread_id, item_id=item_id, action=ThreadAction.MARK_SEEN,
-                                 client_context=client_context,
-                                 offline_threading_id=offline_threading_id)
+                                 client_context=client_context)
 
     def mark_visual_item_seen(self, thread_id: str, item_id: str,
-                              client_context: Optional[str] = None,
-                              offline_threading_id: Optional[str] = None
-                              ) -> Awaitable[CommandResponse]:
+                              client_context: Optional[str] = None) -> Awaitable[CommandResponse]:
         return self.send_command(thread_id, item_id=item_id,
                                  action=ThreadAction.MARK_VISUAL_ITEM_SEEN,
-                                 client_context=client_context,
-                                 offline_threading_id=offline_threading_id)
+                                 client_context=client_context)
 
     def indicate_activity(self, thread_id: str, activity_status: TypingStatus = TypingStatus.TEXT,
-                          client_context: Optional[str] = None,
-                          offline_threading_id: Optional[str] = None
-                          ) -> Awaitable[CommandResponse]:
+                          client_context: Optional[str] = None) -> Awaitable[CommandResponse]:
         return self.send_command(thread_id, activity_status=activity_status.value,
                                  action=ThreadAction.INDICATE_ACTIVITY,
-                                 client_context=client_context,
-                                 offline_threading_id=offline_threading_id)
+                                 client_context=client_context)
 
     # endregion

+ 4 - 0
mauigpapi/state/state.py

@@ -79,6 +79,10 @@ class AndroidState(SerializableAttrs):
     def challenge_path(self, val: str) -> None:
         self._challenge_path = val
 
+    @staticmethod
+    def gen_client_context() -> str:
+        return str((int(time.time() * 1000) << 22) + random.randint(10000, 5000000))
+
     def _gen_temp_uuid(self, seed: str, lifetime: int) -> UUID:
         rand = random.Random(f"{seed}{self.device.id}{round(time.time() * 1000 / lifetime)}")
         return UUID(int=rand.getrandbits(128), version=4)

+ 14 - 9
mautrix_instagram/portal.py

@@ -16,11 +16,11 @@
 from typing import (Dict, Tuple, Optional, List, Deque, Set, Any, Union, AsyncGenerator,
                     Awaitable, NamedTuple, TYPE_CHECKING, cast)
 from collections import deque
-from uuid import uuid4
 from io import BytesIO
 import mimetypes
 import asyncio
 
+import asyncpg
 import magic
 
 from mauigpapi.types import (Thread, ThreadUser, ThreadItem, RegularMediaItem, MediaType,
@@ -172,7 +172,7 @@ class Portal(DBPortal, BasePortal):
         elif not sender.is_connected:
             await self._send_bridge_error("You're not connected to Instagram", confirmed=True)
             return
-        request_id = str(uuid4())
+        request_id = sender.state.gen_client_context()
         self._reqid_dedup.add(request_id)
         self.log.debug(f"Handling Matrix message {event_id} from {sender.mxid}/{sender.igpk} "
                        f"with request ID {request_id}")
@@ -222,11 +222,16 @@ class Portal(DBPortal, BasePortal):
             await self._send_bridge_error(resp.payload.message)
         else:
             self._msgid_dedup.appendleft(resp.payload.item_id)
-            await DBMessage(mxid=event_id, mx_room=self.mxid, item_id=resp.payload.item_id,
-                            receiver=self.receiver, sender=sender.igpk).insert()
+            try:
+                await DBMessage(mxid=event_id, mx_room=self.mxid, item_id=resp.payload.item_id,
+                                receiver=self.receiver, sender=sender.igpk).insert()
+            except asyncpg.UniqueViolationError as e:
+                self.log.warning(f"Error while persisting {event_id} "
+                                 f"({resp.payload.client_context}) -> {resp.payload.item_id}: {e}")
             self._reqid_dedup.remove(request_id)
             await self._send_delivery_receipt(event_id)
-            self.log.debug(f"Handled Matrix message {event_id} -> {resp.payload.item_id}")
+            self.log.debug(f"Handled Matrix message {event_id} ({resp.payload.client_context}) "
+                           f"-> {resp.payload.item_id}")
 
     async def handle_matrix_reaction(self, sender: 'u.User', event_id: EventID,
                                      reacting_to: EventID, emoji: str) -> None:
@@ -568,11 +573,11 @@ class Portal(DBPortal, BasePortal):
             self.log.debug(f"Ignoring message {item.item_id} ({item.client_context}) by "
                            f"{item.user_id} as it was sent by us (client_context in dedup queue)")
         elif item.item_id in self._msgid_dedup:
-            self.log.debug(f"Ignoring message {item.item_id} by {item.user_id}"
-                           " as it was already handled (message.id in dedup queue)")
+            self.log.debug(f"Ignoring message {item.item_id} ({item.client_context}) by "
+                           f"{item.user_id} as it was already handled (message.id in dedup queue)")
         elif await DBMessage.get_by_item_id(item.item_id, self.receiver) is not None:
-            self.log.debug(f"Ignoring message {item.item_id} by {item.user_id}"
-                           " as it was already handled (message.id found in database)")
+            self.log.debug(f"Ignoring message {item.item_id} ({item.client_context}) by "
+                           f"{item.user_id} as it was already handled (message.id in database)")
         else:
             self.log.debug(f"Starting handling of message {item.item_id} ({item.client_context}) "
                            f"by {item.user_id}")