فهرست منبع

Update app version ID again (#135)

Tulir Asokan 1 سال پیش
والد
کامیت
45c880a8bb

+ 4 - 4
mauigpapi/http/base.py

@@ -143,7 +143,7 @@ class BaseAndroidAPI:
             "x-ig-bandwidth-speed-kbps": "-1.000",
             "x-ig-bandwidth-totalbytes-b": "0",
             "x-ig-bandwidth-totaltime-ms": "0",
-            "x-ig-app-startup-country": self.state.device.language.split("_")[1],
+            # "x-ig-app-startup-country": self.state.device.language.split("_")[1],
             "x-bloks-version-id": self.state.application.BLOKS_VERSION_ID,
             "x-ig-www-claim": self.state.session.ig_www_claim or "0",
             "x-bloks-is-layout-rtl": str(self.state.device.is_layout_rtl).lower(),
@@ -155,10 +155,10 @@ class BaseAndroidAPI:
             "x-fb-connection-type": self.state.device.connection_type,
             "x-ig-capabilities": self.state.application.CAPABILITIES,
             "x-ig-app-id": self.state.application.FACEBOOK_ANALYTICS_APPLICATION_ID,
-            "ig-client-endpoint": "unknown",
-            "x-fb-rmd": "cached=0;state=NO_MATCH",
-            "x-tigon-is-retry": "False",
+            "priority": "u=3",
             "ig-u-ig-direct-region-hint": self.state.session.region_hint,
+            # "ig-client-endpoint": "unknown",
+            # "x-fb-rmd": "cached=0;state=NO_MATCH",
             **self._rupload_headers,
         }
         return {k: v for k, v in headers.items() if v is not None}

+ 63 - 10
mauigpapi/http/thread.py

@@ -25,7 +25,8 @@ from ..types import (
     CommandResponse,
     DMInboxResponse,
     DMThreadResponse,
-    FetchedClipInfo,
+    FetchedClipsInfo,
+    MediaShareItem,
     Thread,
     ThreadAction,
     ThreadItemType,
@@ -139,11 +140,50 @@ 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 send_delivery_receipt(
+        self, thread_id: str, sender_id: int | str, item_id: str
+    ) -> None:
+        data = {
+            "thread_id": thread_id,
+            "_uuid": self.state.device.uuid,
+            "sender_ig_id": str(sender_id),
+            "dr_disable": "1",
+            "item_id": item_id,
+        }
+        await self.std_http_post("/api/v1/direct_v2/delivery_receipt/", 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(
@@ -174,10 +214,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(
@@ -196,12 +243,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}/",
@@ -222,9 +269,15 @@ class ThreadAPI(BaseAndroidAPI):
             thread_id, item_type.value, CommandResponse, signed, client_context, **kwargs
         )
 
-    async def fetch_clip(self, media_id: int) -> FetchedClipInfo:
-        return await self.std_http_get(
-            f"/api/v1/clips/item/",
-            query={"clips_media_id": str(media_id)},
-            response_type=FetchedClipInfo,
+    async def fetch_clip(self, media_id: int) -> MediaShareItem:
+        return (
+            (
+                await self.std_http_get(
+                    f"/api/v1/clips/item/",
+                    query={"clips_media_ids": json.dumps([str(media_id)])},
+                    response_type=FetchedClipsInfo,
+                )
+            )
+            .clips_items[0]
+            .media
         )

+ 26 - 5
mauigpapi/mqtt/conn.py

@@ -187,12 +187,11 @@ class AndroidMQTT:
         subscribe_topics = [
             RealtimeTopic.PUBSUB,  # 88
             RealtimeTopic.SUB_IRIS_RESPONSE,  # 135
-            RealtimeTopic.RS_REQ,  # 244
             RealtimeTopic.REALTIME_SUB,  # 149
             RealtimeTopic.REGION_HINT,  # 150
-            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
@@ -217,7 +216,7 @@ class AndroidMQTT:
                 subscribe_topics=subscribe_topic_ids,
                 client_type="cookie_auth",
                 app_id=567067343352427,
-                # region_preference=self.state.session.region_hint or "LLA",
+                region_preference=self.state.session.region_hint or "LLA",
                 device_secret="",
                 client_stack=3,
             ),
@@ -231,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)
@@ -784,9 +783,15 @@ 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,
+            "btt_dual_send": False,
+            "is_ae_dual_send": False,
         }
         lock_start = time.monotonic()
         async with self._message_response_waiter_lock:
@@ -911,7 +916,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,
@@ -920,10 +933,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(
@@ -975,6 +993,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 = "294.0.0.33.87"
+    APP_VERSION_CODE: str = "500160596"
     FACEBOOK_ANALYTICS_APPLICATION_ID: str = "567067343352427"
 
-    BLOKS_VERSION_ID: str = "0928297a84f74885ff39fc1628f8a40da3ef1c467555d555bfd9f8fe1aaacafe"
+    BLOKS_VERSION_ID: str = "4cf8328dae765ededd07d166b6774eeb1eb23c13979a715d6bd2ea9d06bb0560"
     CAPABILITIES: str = "3brTv10="

+ 1 - 0
mauigpapi/types/__init__.py

@@ -74,6 +74,7 @@ from .thread_item import (
     CreativeConfig,
     ExpiredMediaItem,
     FetchedClipInfo,
+    FetchedClipsInfo,
     ImageVersion,
     ImageVersions,
     LinkContext,

+ 5 - 0
mauigpapi/types/thread_item.py

@@ -606,6 +606,11 @@ class PlaceholderItem(SerializableAttrs):
 @dataclass
 class FetchedClipInfo(SerializableAttrs):
     media: MediaShareItem
+
+
+@dataclass
+class FetchedClipsInfo(SerializableAttrs):
+    clips_items: List[FetchedClipInfo]
     status: str
 
 

+ 1 - 0
mautrix_instagram/matrix.py

@@ -149,6 +149,7 @@ class MatrixHandler(BaseMatrixHandler):
         if not message:
             return
         user.log.debug(f"Marking {message.item_id} in {portal.thread_id} as read")
+        # await user.client.mark_seen(portal.thread_id, message.item_id)
         await user.mqtt.mark_seen(portal.thread_id, message.item_id)
 
     @staticmethod

+ 9 - 3
mautrix_instagram/portal.py

@@ -715,7 +715,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":
@@ -762,6 +765,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}")
@@ -773,7 +777,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}")
@@ -883,7 +889,7 @@ class Portal(DBPortal, BasePortal):
             try:
                 fetched_clip = await source.client.fetch_clip(reel_clip_id)
                 reuploaded_video = await self._reupload_instagram_media(
-                    source, fetched_clip.media, intent
+                    source, fetched_clip, intent
                 )
             except Exception:
                 self.log.exception(f"Failed to fetch clip {reel_clip_id}, using fallback")

+ 3 - 0
mautrix_instagram/user.py

@@ -1132,6 +1132,9 @@ class User(DBUser, BaseUser):
             if not sender:
                 # I don't think we care about adds with no sender
                 return
+            background_task.create(
+                self.client.send_delivery_receipt(portal.thread_id, sender.pk, evt.message.item_id)
+            )
             await portal.handle_instagram_item(self, sender, evt.message)
         elif evt.message.op == Operation.REMOVE:
             # Removes don't have a sender, only the message sender can unsend messages anyway