state.py 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. # mautrix-instagram - A Matrix-Instagram puppeting bridge.
  2. # Copyright (C) 2021 Tulir Asokan
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU Affero General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU Affero General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU Affero General Public License
  15. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. from typing import Optional
  17. from uuid import uuid4
  18. import random
  19. import time
  20. from attr import dataclass
  21. from mautrix.types import SerializableAttrs, field
  22. from ..errors import IGNoChallengeError, IGUserIDNotFoundError
  23. from ..types import ChallengeContext, ChallengeStateResponse
  24. from .application import AndroidApplication
  25. from .cookies import Cookies
  26. from .device import AndroidDevice
  27. from .session import AndroidSession
  28. @dataclass
  29. class AndroidState(SerializableAttrs):
  30. device: AndroidDevice = field(factory=lambda: AndroidDevice())
  31. session: AndroidSession = field(factory=lambda: AndroidSession())
  32. application: AndroidApplication = field(factory=lambda: AndroidApplication())
  33. challenge: Optional[ChallengeStateResponse] = None
  34. challenge_context: Optional[ChallengeContext] = None
  35. _challenge_path: Optional[str] = field(default=None, json="challenge_path")
  36. cookies: Cookies = field(factory=lambda: Cookies())
  37. login_2fa_username: Optional[str] = field(default=None, hidden=True)
  38. _pigeon_session_id: Optional[str] = field(default=None, hidden=True)
  39. def __attrs_post_init__(self) -> None:
  40. if self.application.APP_VERSION_CODE != AndroidApplication().APP_VERSION_CODE:
  41. self.application = AndroidApplication()
  42. @property
  43. def pigeon_session_id(self) -> str:
  44. if not self._pigeon_session_id:
  45. self._pigeon_session_id = str(uuid4())
  46. return self._pigeon_session_id
  47. def reset_pigeon_session_id(self) -> None:
  48. self._pigeon_session_id = None
  49. @property
  50. def user_agent(self) -> str:
  51. return (
  52. f"Instagram {self.application.APP_VERSION} Android ({self.device.descriptor}; "
  53. f"{self.device.language}; {self.application.APP_VERSION_CODE})"
  54. )
  55. @property
  56. def user_id(self) -> str:
  57. if self.session.ds_user_id:
  58. return self.session.ds_user_id
  59. elif self.challenge and self.challenge.user_id:
  60. return str(self.challenge.user_id)
  61. else:
  62. raise IGUserIDNotFoundError()
  63. @property
  64. def challenge_path(self) -> str:
  65. if not self._challenge_path:
  66. raise IGNoChallengeError()
  67. return self._challenge_path
  68. @challenge_path.setter
  69. def challenge_path(self, val: str) -> None:
  70. self._challenge_path = val
  71. @staticmethod
  72. def gen_client_context() -> str:
  73. return str((int(time.time() * 1000) << 22) + random.randint(10000, 5000000))