123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 |
- # mautrix-twitter - A Matrix-Twitter DM puppeting bridge
- # Copyright (C) 2020 Tulir Asokan
- #
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Affero General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU Affero General Public License for more details.
- #
- # 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 mauigpapi.state import AndroidState
- from mauigpapi.http import AndroidAPI
- from mauigpapi.errors import (IGLoginTwoFactorRequiredError, IGLoginBadPasswordError,
- IGLoginInvalidUserError, IGLoginError)
- from mautrix.bridge.commands import HelpSection, command_handler
- from .typehint import CommandEvent
- SECTION_AUTH = HelpSection("Authentication", 10, "")
- @command_handler(needs_auth=False, management_only=True, help_section=SECTION_AUTH,
- help_text="Log in to Instagram", help_args="<_username_> <_password_>")
- async def login(evt: CommandEvent) -> None:
- if await evt.sender.is_logged_in():
- await evt.reply("You're already logged in")
- return
- elif len(evt.args) < 2:
- await evt.reply("**Usage:** `$cmdprefix+sp login <username> <password>`")
- return
- username = evt.args[0]
- password = " ".join(evt.args[1:])
- state = AndroidState()
- state.device.generate(username)
- api = AndroidAPI(state)
- await api.simulate_pre_login_flow()
- try:
- resp = await api.login(username, password)
- except IGLoginTwoFactorRequiredError as e:
- tfa_info = e.body.two_factor_info
- msg = "Username and password accepted, but you have two-factor authentication enabled.\n"
- if tfa_info.sms_two_factor_on:
- if tfa_info.totp_two_factor_on:
- msg += (f"Send the code from your authenticator app "
- f"or one sent to {tfa_info.obfuscated_phone_number} here.")
- else:
- msg += f"Send the code sent to {tfa_info.obfuscated_phone_number} here."
- elif tfa_info.totp_two_factor_on:
- msg += "Send the code from your authenticator app here."
- else:
- msg += ("Unfortunately, none of your two-factor authentication methods are currently "
- "supported by the bridge.")
- return
- evt.sender.command_status = {
- "action": "Login",
- "room_id": evt.room_id,
- "next": enter_login_2fa,
- "username": tfa_info.username,
- "2fa_identifier": tfa_info.two_factor_identifier,
- "state": state,
- "api": api,
- }
- except IGLoginInvalidUserError:
- await evt.reply("Invalid username")
- except IGLoginBadPasswordError:
- await evt.reply("Incorrect password")
- else:
- evt.sender.state = state
- user = resp.logged_in_user
- await evt.reply(f"Successfully logged in as {user.full_name} ([@{user.username}]"
- f"(https://instagram.com/{user.username}), user ID: {user.pk})")
- await evt.sender.try_connect()
- async def enter_login_2fa(evt: CommandEvent) -> None:
- api: AndroidAPI = evt.sender.command_status["api"]
- state: AndroidState = evt.sender.command_status["state"]
- identifier = evt.sender.command_status["2fa_identifier"]
- username = evt.sender.command_status["username"]
- evt.sender.command_status = None
- try:
- user = await api.two_factor_login(username, code="".join(evt.args), identifier=identifier)
- except IGLoginError as e:
- await evt.reply(f"Failed to log in: {e.body.message}")
- except Exception as e:
- await evt.reply(f"Failed to log in: {e}")
- evt.log.exception("Failed to log in")
- else:
- evt.sender.state = state
- await evt.reply(f"Successfully logged in as {user.full_name} ([@{user.username}]"
- f"(https://instagram.com/{user.username}), user ID: {user.pk})")
- await evt.sender.try_connect()
- @command_handler(needs_auth=True, help_section=SECTION_AUTH, help_text="Disconnect the bridge from"
- "your Instagram account")
- async def logout(evt: CommandEvent) -> None:
- await evt.sender.logout()
- await evt.reply("Successfully logged out")
|