state.py 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. # mautrix-instagram - A Matrix-Instagram puppeting bridge.
  2. # Copyright (C) 2020 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 UUID
  18. import random
  19. import time
  20. from attr import dataclass
  21. import attr
  22. from mautrix.types import SerializableAttrs
  23. from ..errors import IGNoCheckpointError, IGCookieNotFoundError, IGUserIDNotFoundError
  24. from ..types import ChallengeStateResponse
  25. from .device import AndroidDevice
  26. from .session import AndroidSession
  27. from .application import AndroidApplication
  28. from .experiments import AndroidExperiments
  29. from .cookies import Cookies
  30. @dataclass
  31. class AndroidState(SerializableAttrs):
  32. device: AndroidDevice = attr.ib(factory=lambda: AndroidDevice())
  33. session: AndroidSession = attr.ib(factory=lambda: AndroidSession())
  34. application: AndroidApplication = attr.ib(factory=lambda: AndroidApplication())
  35. experiments: AndroidExperiments = attr.ib(factory=lambda: AndroidExperiments())
  36. client_session_id_lifetime: int = 1_200_000
  37. pigeon_session_id_lifetime: int = 1_200_000
  38. challenge: Optional[ChallengeStateResponse] = None
  39. _challenge_path: Optional[str] = attr.ib(default=None, metadata={"json": "challenge_path"})
  40. cookies: Cookies = attr.ib(factory=lambda: Cookies())
  41. def __attrs_post_init__(self) -> None:
  42. if self.application.APP_VERSION_CODE != AndroidApplication().APP_VERSION_CODE:
  43. self.application = AndroidApplication()
  44. @property
  45. def client_session_id(self) -> str:
  46. return str(self._gen_temp_uuid("clientSessionId", self.client_session_id_lifetime))
  47. @property
  48. def pigeon_session_id(self) -> str:
  49. return str(self._gen_temp_uuid("pigeonSessionId", self.pigeon_session_id_lifetime))
  50. @property
  51. def user_agent(self) -> str:
  52. return (f"Instagram {self.application.APP_VERSION} Android ({self.device.descriptor}; "
  53. f"{self.device.language}; {self.application.APP_VERSION_CODE})")
  54. @property
  55. def user_id(self) -> str:
  56. try:
  57. return self.cookies.user_id
  58. except IGCookieNotFoundError:
  59. if not self.challenge or not self.challenge.user_id:
  60. raise IGUserIDNotFoundError()
  61. return str(self.challenge.user_id)
  62. @property
  63. def challenge_path(self) -> str:
  64. if not self._challenge_path:
  65. raise IGNoCheckpointError()
  66. return self._challenge_path
  67. @challenge_path.setter
  68. def challenge_path(self, val: str) -> None:
  69. self._challenge_path = val
  70. def _gen_temp_uuid(self, seed: str, lifetime: int) -> UUID:
  71. rand = random.Random(f"{seed}{self.device.id}{round(time.time() * 1000 / lifetime)}")
  72. return UUID(int=rand.getrandbits(128), version=4)