account.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  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, Type, TypeVar
  17. import json
  18. from ..types import CurrentUserResponse
  19. from .base import BaseAndroidAPI
  20. T = TypeVar('T')
  21. class AccountAPI(BaseAndroidAPI):
  22. async def current_user(self) -> CurrentUserResponse:
  23. return await self.std_http_get(f"/api/v1/accounts/current_user/", query={"edit": "true"},
  24. response_type=CurrentUserResponse)
  25. async def set_biography(self, text: str) -> CurrentUserResponse:
  26. # TODO entities?
  27. return await self.__command("set_biography", device_id=self.state.device.id, raw_text=text)
  28. async def set_profile_picture(self, upload_id: str) -> CurrentUserResponse:
  29. return await self.__command("change_profile_picture",
  30. use_fbuploader="true", upload_id=upload_id)
  31. async def remove_profile_picture(self) -> CurrentUserResponse:
  32. return await self.__command("remove_profile_picture")
  33. async def set_private(self, private: bool) -> CurrentUserResponse:
  34. return await self.__command("set_private" if private else "set_public")
  35. async def confirm_email(self, slug: str) -> CurrentUserResponse:
  36. # slug can contain slashes, but it shouldn't start or end with one
  37. return await self.__command(f"confirm_email/{slug}")
  38. async def send_recovery_flow_email(self, query: str):
  39. req = {
  40. "_csrftoken": self.state.cookies.csrf_token,
  41. "adid": "",
  42. "guid": self.state.device.uuid,
  43. "device_id": self.state.device.id,
  44. "query": query,
  45. }
  46. # TODO parse response content
  47. return await self.std_http_post(f"/api/v1/accounts/send_recovery_flow_email/", data=req)
  48. async def edit_profile(self, external_url: Optional[str] = None, gender: Optional[str] = None,
  49. phone_number: Optional[str] = None, username: Optional[str] = None,
  50. # TODO should there be a last_name?
  51. first_name: Optional[str] = None, biography: Optional[str] = None,
  52. email: Optional[str] = None) -> CurrentUserResponse:
  53. return await self.__command("edit_profile", device_id=self.state.device.id, email=email,
  54. external_url=external_url, first_name=first_name,
  55. username=username, phone_number=phone_number, gender=gender,
  56. biography=biography)
  57. async def __command(self, command: str, response_type: Type[T] = CurrentUserResponse,
  58. **kwargs: str) -> T:
  59. req = {
  60. "_csrftoken": self.state.cookies.csrf_token,
  61. "_uid": self.state.cookies.user_id,
  62. "_uuid": self.state.device.uuid,
  63. **kwargs,
  64. }
  65. return await self.std_http_post(f"/api/v1/accounts/{command}/", data=req,
  66. filter_nulls=True, response_type=response_type)
  67. async def read_msisdn_header(self, usage: str = "default"):
  68. req = {
  69. "mobile_subno_usage": usage,
  70. "device_id": self.state.device.uuid,
  71. }
  72. return await self.std_http_post("/api/v1/accounts/read_msisdn_header/", data=req)
  73. async def msisdn_header_bootstrap(self, usage: str = "default"):
  74. req = {
  75. "mobile_subno_usage": usage,
  76. "device_id": self.state.device.uuid,
  77. }
  78. return await self.std_http_post("/api/v1/accounts/msisdn_header_bootstrap/", data=req)
  79. async def contact_point_prefill(self, usage: str = "default"):
  80. req = {
  81. "mobile_subno_usage": usage,
  82. "device_id": self.state.device.uuid,
  83. }
  84. return await self.std_http_post("/api/v1/accounts/contact_point_prefill/", data=req)
  85. async def get_prefill_candidates(self):
  86. req = {
  87. "android_device_id": self.state.device.id,
  88. "usages": json.dumps(["account_recovery_omnibox"]),
  89. "device_id": self.state.device.uuid,
  90. }
  91. # TODO parse response content
  92. return await self.std_http_post("/api/v1/accounts/get_prefill_candidates/", data=req)
  93. async def process_contact_point_signals(self):
  94. req = {
  95. "phone_id": self.state.device.phone_id,
  96. "_csrftoken": self.state.cookies.csrf_token,
  97. "_uid": self.state.cookies.user_id,
  98. "device_id": self.state.device.uuid,
  99. "_uuid": self.state.device.uuid,
  100. "google_tokens": json.dumps([]),
  101. }
  102. return await self.std_http_post("/api/v1/accounts/process_contact_point_signals/",
  103. data=req)