Tulir Asokan пре 2 година
родитељ
комит
c7614e9be8

+ 11 - 0
mauigpapi/http/base.py

@@ -50,6 +50,7 @@ from ..errors import (
 )
 from ..proxy import ProxyHandler
 from ..state import AndroidState
+from ..types import ChallengeContext
 
 try:
     from aiohttp_socks import ProxyConnector
@@ -235,7 +236,17 @@ class BaseAndroidAPI:
         if isinstance(message, str):
             if message == "challenge_required":
                 err = IGChallengeError(resp, data)
+                self.log.debug(f"Storing challenge URL {err.url}")
                 self.state.challenge_path = err.url
+                try:
+                    self.state.challenge_context = ChallengeContext.parse_json(
+                        err.body.challenge.challenge_context
+                    )
+                except Exception:
+                    self.log.exception(
+                        "Failed to deserialize challenge_context %s",
+                        err.body.challenge.challenge_context,
+                    )
                 raise err
             elif message == "checkpoint_required":
                 raise IGCheckpointError(resp, data)

+ 3 - 0
mauigpapi/http/challenge.py

@@ -30,6 +30,8 @@ class ChallengeAPI(BaseAndroidAPI):
             "guid": self.state.device.uuid,
             "device_id": self.state.device.id,
         }
+        if self.state.challenge_context:
+            query["challenge_context"] = self.state.challenge_context.json()
         self.log.debug("Fetching current challenge state")
         return self.__handle_resp(
             await self.std_http_get(self.__path, query=query, response_type=ChallengeStateResponse)
@@ -124,6 +126,7 @@ class ChallengeAPI(BaseAndroidAPI):
                 f"Challenge closed (step: {resp.step_name}, has user: {bool(resp.logged_in_user)})"
             )
             self.state.challenge = None
+            self.state.challenge_context = None
             self.state.challenge_path = None
         else:
             self.state.challenge = resp

+ 2 - 1
mauigpapi/state/state.py

@@ -23,7 +23,7 @@ from attr import dataclass
 from mautrix.types import SerializableAttrs, field
 
 from ..errors import IGNoChallengeError, IGUserIDNotFoundError
-from ..types import ChallengeStateResponse
+from ..types import ChallengeContext, ChallengeStateResponse
 from .application import AndroidApplication
 from .cookies import Cookies
 from .device import AndroidDevice
@@ -40,6 +40,7 @@ class AndroidState(SerializableAttrs):
     client_session_id_lifetime: int = 1_200_000
     pigeon_session_id_lifetime: int = 1_200_000
     challenge: Optional[ChallengeStateResponse] = None
+    challenge_context: Optional[ChallengeContext] = None
     _challenge_path: Optional[str] = field(default=None, json="challenge_path")
     cookies: Cookies = field(factory=lambda: Cookies())
     login_2fa_username: Optional[str] = field(default=None, hidden=True)

+ 1 - 1
mauigpapi/types/__init__.py

@@ -9,7 +9,7 @@ from .account import (
     ProfileEditParams,
     UserIdentifier,
 )
-from .challenge import ChallengeStateData, ChallengeStateResponse
+from .challenge import ChallengeContext, ChallengeStateData, ChallengeStateResponse
 from .direct_inbox import DMInbox, DMInboxCursor, DMInboxResponse, DMThreadResponse
 from .error import (
     ChallengeData,

+ 3 - 1
mauigpapi/types/challenge.py

@@ -13,7 +13,7 @@
 #
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
-from typing import Optional
+from typing import Optional, Union
 
 from attr import dataclass
 
@@ -41,8 +41,10 @@ class ChallengeStateData(SerializableAttrs):
 @dataclass
 class ChallengeContext(SerializableAttrs):
     step_name: Optional[str] = None
+    nonce_code: Optional[str] = None
     challenge_type_enum: Optional[str] = None
     cni: Optional[int] = None
+    user_id: Optional[Union[str, int]] = None
     is_stateless: bool = False
     present_as_modal: bool = False
 

+ 1 - 0
mauigpapi/types/error.py

@@ -42,6 +42,7 @@ class ChallengeData(SerializableAttrs):
     lock: bool
     logout: bool
     native_flow: bool
+    challenge_context: Optional[str] = None
 
 
 @dataclass

+ 1 - 1
mauigpapi/types/login.py

@@ -13,7 +13,7 @@
 #
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
-from typing import Any, List, Optional
+from typing import Optional
 
 from attr import dataclass
 

+ 1 - 1
mautrix_instagram/commands/auth.py

@@ -97,7 +97,7 @@ async def login(evt: CommandEvent) -> None:
         }
         await evt.reply(msg)
     except IGChallengeError:
-        await api.challenge_auto(reset=True)
+        await api.challenge_auto()
         evt.sender.command_status = {
             **evt.sender.command_status,
             "next": enter_login_security_code,

+ 1 - 1
mautrix_instagram/web/provisioning_api.py

@@ -394,7 +394,7 @@ class ProvisioningAPI:
         self, user: u.User, state: AndroidState, api: AndroidAPI, err: IGChallengeError, after: str
     ) -> web.Response:
         try:
-            resp = await api.challenge_auto(reset=True)
+            resp = await api.challenge_auto(reset=after == "2fa")
         except Exception:
             # Most likely means that the user has to go and verify the login on their phone.
             # Return a 403 in this case so the client knows to show such verbiage.