diff --git a/example-config.yaml b/example-config.yaml index b467086d..ee68b25d 100644 --- a/example-config.yaml +++ b/example-config.yaml @@ -72,18 +72,21 @@ bridge: # Whether or not to use native Matrix replies. At the time of writing, only riot-web supports # replies and the format of them is subject to change. - native_replies: True + native_replies: true # If native replies are disabled, should the custom replies contain a link to the message being # replied to? - link_in_reply: False + link_in_reply: false # Show message editing as a reply to the original message. # If this is false, message edits are not shown at all, as Matrix does not support editing yet. - edits_as_replies: False + edits_as_replies: false # Whether or not Matrix bot messages (type m.notice) should be bridged. - bridge_notices: False + bridge_notices: false # The maximum number of simultaneous Telegram deletions to handle. # A large number of simultaneous redactions could put strain on your homeserver. max_telegram_delete: 10 + # Allow logging in within Matrix. If false, the only way to log in is using the out-of-Matrix + # login website (see appservice.public config section) + allow_matrix_login: true # The prefix for commands. Only required in non-management rooms. command_prefix: "!tg" diff --git a/mautrix_telegram/commands/auth.py b/mautrix_telegram/commands/auth.py index 80d4d39b..dd8c4cd9 100644 --- a/mautrix_telegram/commands/auth.py +++ b/mautrix_telegram/commands/auth.py @@ -54,8 +54,30 @@ def register(evt): async def login(evt): if evt.sender.logged_in: return await evt.reply("You are already logged in.") - elif len(evt.args) == 0: - return await evt.reply("**Usage:** `$cmdprefix+sp login `") + evt.sender.command_status = { + "next": enter_phone, + "action": "Login", + } + if evt.config["appservice.public.enabled"]: + prefix = evt.config["appservice.public.external"] + url = f"{prefix}/login?mxid={evt.sender.mxid}" + if evt.config.get("bridge.allow_matrix_login", True): + return await evt.reply("\n\n".join(( + "This bridge instance allows you to log in inside or outside Matrix.", + "If you would like to log in within Matrix, please send your phone number here.", + f"If you would like to log in outside of Matrix, [click here]({url})."))) + return await evt.reply("This bridge instance does not allow logging in inside Matrix.\n\n" + f"Please visit [the login page]({url}) to log in.") + return await evt.reply( + "This bridge instance does not allow you to log in outside of Matrix.\n\n" + "Please send your phone number here to start the login process.") + + +@command_handler(needs_auth=False) +async def enter_phone(evt): + if len(evt.args) == 0: + return await evt.reply("**Usage:** `$cmdprefix+sp enter-phone `") + phone_number = evt.args[0] try: await evt.sender.ensure_started(even_if_no_session=True) @@ -81,6 +103,9 @@ async def login(evt): evt.log.exception("Error requesting phone code") return await evt.reply("Unhandled exception while requesting code. " "Check console for more details.") + finally: + if evt.sender.command_status["next"] == enter_phone: + evt.sender.command_status = None @command_handler(needs_auth=False) diff --git a/mautrix_telegram/commands/meta.py b/mautrix_telegram/commands/meta.py index 0338b402..e7ea94e0 100644 --- a/mautrix_telegram/commands/meta.py +++ b/mautrix_telegram/commands/meta.py @@ -50,9 +50,9 @@ def help(evt): **cancel** - Cancel an ongoing action (such as login). #### Authentication -**login** <_phone_> - Request an authentication code. -**logout** - Log out from Telegram. -**ping** - Check if you're logged into Telegram. +**login** - Request an authentication code. +**logout** - Log out from Telegram. +**ping** - Check if you're logged into Telegram. #### Initiating chats **search** [_-r|--remote_] <_query_> - Search your contacts or the Telegram servers for users. diff --git a/mautrix_telegram/public/__init__.py b/mautrix_telegram/public/__init__.py index 890d24cb..6695a19a 100644 --- a/mautrix_telegram/public/__init__.py +++ b/mautrix_telegram/public/__init__.py @@ -42,16 +42,22 @@ class PublicBridgeWebsite: pkg_resources.resource_filename("mautrix_telegram", "public/")) async def get_login(self, request): - return self.render_login( - request.rel_url.query["mxid"] if "mxid" in request.rel_url.query else "") + user = (User.get_by_mxid(request.rel_url.query["mxid"], create=False) + if "mxid" in request.rel_url.query else None) + if not user: + return self.render_login(mxid=request.rel_url.query["mxid"], state="request") + elif not user.whitelisted: + return self.render_login(mxid=user.mxid, error="You are not whitelisted.", status=403) + await user.ensure_started() + if not user.logged_in: + return self.render_login(mxid=user.mxid, state="request") - def render_login(self, mxid, state="request", phone="", code="", password="", - error="", message="", username="", status=200): - return web.Response(status=status, - content_type="text/html", - text=self.login.render(mxid=mxid, state=state, phone=phone, code=code, - message=message, username=username, error=error, - password=password)) + return self.render_login(mxid=user.mxid, username=user.username) + + def render_login(self, status=200, username="", state="", error="", message="", mxid=""): + return web.Response(status=status, content_type="text/html", + text=self.login.render(username=username, state=state, error=error, + message=message, mxid=mxid)) async def post_login(self, request): self.log.debug(request) @@ -59,9 +65,11 @@ class PublicBridgeWebsite: if "mxid" not in data: return self.render_login(error="Please enter your Matrix ID.", status=400) - user = User.get_by_mxid(data["mxid"]) + user = await User.get_by_mxid(data["mxid"]).ensure_started() if not user.whitelisted: return self.render_login(mxid=user.mxid, error="You are not whitelisted.", status=403) + elif user.logged_in: + return self.render_login(mxid=user.mxid, username=user.username) if "phone" in data: try: @@ -93,7 +101,7 @@ class PublicBridgeWebsite: try: user_info = await user.client.sign_in(code=data["code"]) asyncio.ensure_future(user.post_login(user_info), loop=self.loop) - if user.command_status.action == "Login": + if user.command_status and user.command_status.action == "Login": user.command_status = None return self.render_login(mxid=user.mxid, state="logged-in", status=200, username=user_info.username) @@ -105,14 +113,14 @@ class PublicBridgeWebsite: error="Phone code expired.") except SessionPasswordNeededError: if "password" not in data: - if user.command_status.action == "Login": + if user.command_status and user.command_status.action == "Login": user.command_status = { "next": enter_password, "action": "Login (password entry)", } return self.render_login( mxid=user.mxid, state="password", status=200, - error="Code accepted, but you have 2-factor authentication is enabled.") + message="Code accepted, but you have 2-factor authentication is enabled.") except Exception: self.log.exception("Error sending phone code") return self.render_login(mxid=user.mxid, state="code", status=500, @@ -124,7 +132,7 @@ class PublicBridgeWebsite: try: user_info = await user.client.sign_in(password=data["password"]) asyncio.ensure_future(user.post_login(user_info), loop=self.loop) - if user.command_status.action == "Login (password entry)": + if user.command_status and user.command_status.action == "Login (password entry)": user.command_status = None return self.render_login(mxid=user.mxid, state="logged-in", status=200, username=user_info.username) diff --git a/mautrix_telegram/public/login.css b/mautrix_telegram/public/login.css index 461ec7e0..95d461b8 100644 --- a/mautrix_telegram/public/login.css +++ b/mautrix_telegram/public/login.css @@ -7,3 +7,27 @@ form[data-status="code"] > div.status-code, form[data-status="password"] > div.status-password { display: initial; } + +.container { + margin-top: 3rem; + max-width: 60rem; +} + +.error, .message { + border-radius: .25rem; + padding: .5rem 1rem; + border: 1px solid transparent; + margin: .5rem 0; +} + +.error { + border-color: #f5c6cb; + background-color: #f8d7da; + color: #721c24; +} + +.message { + border-color: #c3e6cb; + background-color: #d4edda; + color: #155724; +} diff --git a/mautrix_telegram/public/login.html.mako b/mautrix_telegram/public/login.html.mako index c2aefbf0..6818030c 100644 --- a/mautrix_telegram/public/login.html.mako +++ b/mautrix_telegram/public/login.html.mako @@ -7,33 +7,71 @@ + + + -
- % if state == "logged-in": -

Logged in successfully!

-

Logged in as @${username}

+
+ % if username: + % if state == "logged-in": +

Logged in successfully!

+

+ Logged in as @${username}. + You should be invited to Telegram portals on Matrix very soon. +

+ % else: +

You're already logged in!

+

+ You're logged in as @${username}. +

+

+ If you want to log in with another account, log out using the logout + management command first. +

+ % endif % else:

Log in to Telegram

- % if error: -
${error}
- % endif - % if message: -
${message}
- % endif + % if error: +
${error}
+ % endif + % if message: +
${message}
+ % endif
- - % if state == "request": - - - % elif state == "code": - - - % elif state == "password": - - - % endif +
+ + + % if state == "request": + + + + % elif state == "code": + + + +
+ +
+ % elif state == "password": + + + +
+ +
+ % endif +
% endif