Browse Source

Update app version ID

Tulir Asokan 2 năm trước cách đây
mục cha
commit
d6ebf5bbd2

+ 38 - 4
mauigpapi/http/thread.py

@@ -138,11 +138,38 @@ class ThreadAPI(BaseAndroidAPI):
             f"/api/v1/direct_v2/threads/{thread_id}/", query=query, response_type=DMThreadResponse
         )
 
+    # /threads/.../get_items/ with urlencoded form body:
+    # visual_message_return_type:     unseen
+    # _uuid:                          device uuid
+    # original_message_client_contexts:["client context"]
+    # item_ids:                       [item id]
+
+    async def get_thread_participant_requests(self, thread_id: str, page_size: int = 10):
+        return await self.std_http_get(
+            f"/api/v1/direct_v2/threads/{thread_id}/participant_requests/",
+            query={"page_size": str(page_size)},
+        )
+
+    async def mark_seen(
+        self, thread_id: str, item_id: str, client_context: str | None = None
+    ) -> None:
+        if not client_context:
+            client_context = self.state.gen_client_context()
+        data = {
+            "thread_id": thread_id,
+            "action": "mark_seen",
+            "client_context": client_context,
+            "_uuid": self.state.device.uuid,
+            "offline_threading_id": client_context,
+        }
+        await self.std_http_post(
+            f"/api/v1/direct_v2/threads/{thread_id}/items/{item_id}/seen/", data=data
+        )
+
     async def create_group_thread(self, recipient_users: list[int | str]) -> Thread:
         return await self.std_http_post(
             "/api/v1/direct_v2/create_group_thread/",
             data={
-                "_csrftoken": self.state.cookies.csrf_token,
                 "_uuid": self.state.device.uuid,
                 "_uid": self.state.session.ds_user_id,
                 "recipient_users": json.dumps(
@@ -173,10 +200,17 @@ class ThreadAPI(BaseAndroidAPI):
             },
         )
 
-    async def delete_item(self, thread_id: str, item_id: str) -> None:
+    async def delete_item(
+        self, thread_id: str, item_id: str, orig_client_context: str | None = None
+    ) -> None:
         await self.std_http_post(
             f"/api/v1/direct_v2/threads/{thread_id}/items/{item_id}/delete/",
-            data={"_csrftoken": self.state.cookies.csrf_token, "_uuid": self.state.device.uuid},
+            data={
+                "is_shh_mode": "0",
+                "send_attribution": "direct_thread",
+                "_uuid": self.state.device.uuid,
+                "original_message_client_context": orig_client_context,
+            },
         )
 
     async def _broadcast(
@@ -195,12 +229,12 @@ class ThreadAPI(BaseAndroidAPI):
             "thread_ids": f"[{thread_id}]",
             "is_shh_mode": "0",
             "client_context": client_context,
-            "_csrftoken": self.state.cookies.csrf_token,
             "device_id": self.state.device.id,
             "mutation_token": client_context,
             "_uuid": self.state.device.uuid,
             **kwargs,
             "offline_threading_id": client_context,
+            "is_x_transport_forward": "false",
         }
         return await self.std_http_post(
             f"/api/v1/direct_v2/threads/broadcast/{item_type}/",

+ 19 - 3
mauigpapi/http/upload.py

@@ -44,12 +44,13 @@ class UploadAPI(BaseAndroidAPI):
                 }
             ),
             "media_type": str(MediaType.IMAGE.value),
+            "sticker_burnin_params": json.dumps([]),
             "upload_id": upload_id,
             "xsharing_user_ids": json.dumps([]),
         }
         if mime == "image/jpeg":
             params["image_compression"] = json.dumps(
-                {"lib_name": "moz", "lib_version": "3.1.m", "quality": 80}
+                {"lib_name": "moz", "lib_version": "3.1.m", "quality": "0"}
             )
         if width and height:
             params["original_width"] = str(width)
@@ -101,6 +102,8 @@ class UploadAPI(BaseAndroidAPI):
         if audio:
             params["is_direct_voice"] = "1"
         else:
+            params["sticker_burnin_params"] = json.dumps([])
+            params["hflip"] = "false"
             params["direct_v2"] = "1"
             params["for_direct_story"] = "1"
             params["content_tags"] = "use_default_cover"
@@ -121,6 +124,7 @@ class UploadAPI(BaseAndroidAPI):
         if not audio:
             headers["segment-type"] = "3"
             headers["segment-start-offset"] = "0"
+        # TODO call GET /rupload_igvideo with same params and priority header
         return (
             await self.std_http_post(
                 f"/rupload_igvideo/{name}",
@@ -133,7 +137,12 @@ class UploadAPI(BaseAndroidAPI):
         )
 
     async def finish_upload(
-        self, upload_id: str, source_type: str, video: bool = False
+        self,
+        upload_id: str,
+        source_type: str,
+        video: bool = False,
+        width: int | None = None,
+        height: int | None = None,
     ) -> FinishUploadResponse:
         headers = {
             "retry_context": json.dumps(
@@ -146,7 +155,6 @@ class UploadAPI(BaseAndroidAPI):
         }
         req = {
             "timezone_offset": self.state.device.timezone_offset,
-            "_csrftoken": self.state.cookies.csrf_token,
             "source_type": source_type,
             "_uid": self.state.session.ds_user_id,
             "device_id": self.state.device.id,
@@ -156,6 +164,14 @@ class UploadAPI(BaseAndroidAPI):
         }
         query = {}
         if video:
+            if width and height:
+                req["extra"] = {
+                    "source_width": width,
+                    "source_height": height,
+                }
+            req["filter_type"] = "0"
+            req["include_e2ee_mentioned_user_list"] = "false"
+            req["video_result"] = ""
             query["video"] = "1"
         return await self.std_http_post(
             "/api/v1/media/upload_finish/",

+ 23 - 2
mauigpapi/mqtt/conn.py

@@ -191,6 +191,7 @@ class AndroidMQTT:
             RealtimeTopic.RS_RESP,  # 245
             RealtimeTopic.T_RTC_LOG,  # 274
             RealtimeTopic.SEND_MESSAGE_RESPONSE,  # 133
+            RealtimeTopic.LARGE_SCALE_FIRE_AND_FORGET_SYNC,  # 279
             RealtimeTopic.MESSAGE_SYNC,  # 146
             RealtimeTopic.LIGHTSPEED_RESPONSE,  # 179
             RealtimeTopic.UNKNOWN_PP,  # 34
@@ -229,7 +230,7 @@ class AndroidMQTT:
                 "platform": "android",
                 "ig_mqtt_route": "django",
                 "pubsub_msg_type_blacklist": "direct, typing_type",
-                "auth_cache_enabled": "1",
+                # "auth_cache_enabled": "1",
             },
         )
         return zlib.compress(cfg.to_thrift(), level=9)
@@ -765,7 +766,11 @@ class AndroidMQTT:
             "thread_id": thread_id,
             "client_context": client_context,
             "offline_threading_id": client_context,
+            "mutation_token": client_context,
             "action": action.value,
+            "is_shh_mode": "0",
+            "sampled": False,
+            "session_id": f"UFS-{self.state.pigeon_session_id}-0",
             # "device_id": self.state.cookies["ig_did"],
             **kwargs,
         }
@@ -902,7 +907,15 @@ class AndroidMQTT:
         target_item_type: ThreadItemType = ThreadItemType.TEXT,
         shh_mode: bool = False,
         client_context: str | None = None,
+        original_message_client_context: str | None = None,
     ) -> Awaitable[CommandResponse]:
+        kwargs = (
+            {
+                "original_message_client_context": original_message_client_context,
+            }
+            if original_message_client_context
+            else {}
+        )
         return self.send_item(
             thread_id,
             reaction_status=reaction_status.value,
@@ -911,10 +924,15 @@ class AndroidMQTT:
             target_item_type=target_item_type.value,
             emoji=emoji,
             item_id=item_id,
-            reaction_action_source="double_tap",
+            reaction_action_source=(
+                "double_tap" if reaction_status == ReactionStatus.CREATED else "action_menu"
+            ),
             shh_mode=shh_mode,
             item_type=ThreadItemType.REACTION,
             client_context=client_context,
+            super_react_type="none",
+            send_attribution="direct_thread",
+            **kwargs,
         )
 
     def send_user_story(
@@ -962,6 +980,9 @@ class AndroidMQTT:
             client_context=client_context,
             replied_to_item_id=replied_to_item_id,
             replied_to_client_context=replied_to_client_context,
+            send_attribution="direct_thread",
+            send_silently=False,
+            is_x_transport_forward=False,
         )
 
     def mark_seen(

+ 2 - 0
mauigpapi/mqtt/subscription.py

@@ -344,6 +344,7 @@ _topic_map: dict[str, str] = {
     "/rs_req": "244",
     "/rs_resp": "245",
     "/t_rtc_log": "274",
+    "/ig_large_scale_fire_and_forget_sync": "279",
 }
 
 _reverse_topic_map: dict[str, str] = {value: key for key, value in _topic_map.items()}
@@ -366,6 +367,7 @@ class RealtimeTopic(Enum):
     RS_REQ = "/rs_req"
     RS_RESP = "/rs_resp"
     T_RTC_LOG = "/t_rtc_log"
+    LARGE_SCALE_FIRE_AND_FORGET_SYNC = "/ig_large_scale_fire_and_forget_sync"
 
     @property
     def encoded(self) -> str:

+ 3 - 3
mauigpapi/state/application.py

@@ -20,9 +20,9 @@ from mautrix.types import SerializableAttrs
 
 @dataclass
 class AndroidApplication(SerializableAttrs):
-    APP_VERSION: str = "256.0.0.18.105"
-    APP_VERSION_CODE: str = "407842973"
+    APP_VERSION: str = "279.0.0.23.112"
+    APP_VERSION_CODE: str = "466535821"
     FACEBOOK_ANALYTICS_APPLICATION_ID: str = "567067343352427"
 
-    BLOKS_VERSION_ID: str = "0928297a84f74885ff39fc1628f8a40da3ef1c467555d555bfd9f8fe1aaacafe"
+    BLOKS_VERSION_ID: str = "2fecedb8f3d321e9fc9b22f9693830c31650ff5b50516bdde984ff1b2090d2ac"
     CAPABILITIES: str = "3brTv10="

+ 2 - 1
mautrix_instagram/matrix.py

@@ -149,7 +149,8 @@ class MatrixHandler(BaseMatrixHandler):
         if not message:
             return
         user.log.debug(f"Marking {message.item_id} in {portal.thread_id} as read")
-        await user.mqtt.mark_seen(portal.thread_id, message.item_id)
+        await user.client.mark_seen(portal.thread_id, message.item_id)
+        # await user.mqtt.mark_seen(portal.thread_id, message.item_id)
 
     @staticmethod
     async def handle_typing(room_id: RoomID, typing: list[UserID]) -> None:

+ 8 - 2
mautrix_instagram/portal.py

@@ -709,7 +709,10 @@ class Portal(DBPortal, BasePortal):
 
         async with self._reaction_lock:
             resp = await sender.mqtt.send_reaction(
-                self.thread_id, item_id=message.item_id, emoji=emoji
+                self.thread_id,
+                item_id=message.item_id,
+                emoji=emoji,
+                original_message_client_context=message.client_context,
             )
             if resp.status != "ok":
                 if resp.payload and resp.payload.message == "invalid unicode emoji":
@@ -758,6 +761,7 @@ class Portal(DBPortal, BasePortal):
                     item_id=reaction.ig_item_id,
                     reaction_status=ReactionStatus.DELETED,
                     emoji="",
+                    # TODO set original_message_client_context
                 )
             except Exception as e:
                 raise Exception(f"Removing reaction failed: {e}")
@@ -769,7 +773,9 @@ class Portal(DBPortal, BasePortal):
         if message and not message.is_internal:
             try:
                 await message.delete()
-                await sender.client.delete_item(self.thread_id, message.item_id)
+                await sender.client.delete_item(
+                    self.thread_id, message.item_id, message.client_context
+                )
                 self.log.trace(f"Removed {message} after Matrix redaction")
             except Exception as e:
                 raise Exception(f"Removing message failed: {e}")

+ 5 - 0
mautrix_instagram/user.py

@@ -1042,6 +1042,9 @@ class User(DBUser, BaseUser):
         self.mqtt = None
         self.state = None
         self.seq_id = None
+        if self._seq_id_save_task and not self._seq_id_save_task.done():
+            self._seq_id_save_task.cancel()
+            self._seq_id_save_task = None
         self.snapshot_at_ms = None
         self.thread_sync_completed = False
         self._is_logged_in = False
@@ -1052,6 +1055,8 @@ class User(DBUser, BaseUser):
 
     async def _save_seq_id_after_sleep(self) -> None:
         await asyncio.sleep(120)
+        if self.seq_id is None:
+            return
         self._seq_id_save_task = None
         self.log.trace("Saving sequence ID %d/%d", self.seq_id, self.snapshot_at_ms)
         try: