From e1355ae5d82ea744ebbfc4f06339cb159d5ecf21 Mon Sep 17 00:00:00 2001 From: Phil Jones Date: Thu, 1 Aug 2019 17:15:04 +0000 Subject: [PATCH] Improve Quart example (#1229) --- telethon_examples/quart_login.py | 96 +++++++++++++++++++------------- 1 file changed, 57 insertions(+), 39 deletions(-) diff --git a/telethon_examples/quart_login.py b/telethon_examples/quart_login.py index b4715c66..bb960c03 100644 --- a/telethon_examples/quart_login.py +++ b/telethon_examples/quart_login.py @@ -1,7 +1,8 @@ import base64 import os -from quart import Quart, request +import hypercorn.asyncio +from quart import Quart, render_template_string, request from telethon import TelegramClient, utils @@ -12,24 +13,44 @@ def get_env(name, message): return input(message) +BASE_TEMPLATE = ''' + + + + + Telethon + Quart + + {{ content | safe }} + +''' + +PHONE_FORM = ''' +
+ Phone (international format): + +
+''' + +CODE_FORM = ''' +
+ Telegram code: + +
+''' + # Session name, API ID and hash to use; loaded from environmental variables SESSION = os.environ.get('TG_SESSION', 'quart') API_ID = int(get_env('TG_API_ID', 'Enter your API ID: ')) API_HASH = get_env('TG_API_HASH', 'Enter your API hash: ') +# Telethon client +client = TelegramClient(SESSION, API_ID, API_HASH) +client.parse_mode = 'html' # <- Render things nicely +phone = None -# Helper method to add the HTML head/body -def html(inner): - return ''' - - - - - Telethon + Quart - - {} - -'''.format(inner) +# Quart app +app = Quart(__name__) +app.secret_key = 'CHANGE THIS TO SOMETHING SECRET' # Helper method to format messages nicely @@ -50,18 +71,20 @@ async def format_message(message): ) -# Define the global phone and Quart app variables -phone = None -app = Quart(__name__) +# Connect the client before we start serving with Quart +@app.before_serving +async def startup(): + await client.connect() + + +# After we're done serving (near shutdown), clean up the client +@app.after_serving +async def cleanup(): + await client.disconnect() -# Quart handlers @app.route('/', methods=['GET', 'POST']) async def root(): - # Connect if we aren't yet - if not client.is_connected(): - await client.connect() - # We want to update the global phone variable to remember it global phone @@ -82,22 +105,18 @@ async def root(): async for m in client.iter_messages(dialog, 10): result += await(format_message(m)) - return html(result) + return await render_template_string(BASE_TEMPLATE, content=result) # Ask for the phone if we don't know it yet if phone is None: - return html(''' -
- Phone (international format): - -
''') + return await render_template_string(BASE_TEMPLATE, content=PHONE_FORM) # We have the phone, but we're not logged in, so ask for the code - return html(''' -
- Telegram code: - -
''') + return await render_template_string(BASE_TEMPLATE, content=CODE_FORM) + + +async def main(): + await hypercorn.asyncio.serve(app, hypercorn.Config()) # By default, `Quart.run` uses `asyncio.run()`, which creates a new asyncio @@ -108,13 +127,12 @@ async def root(): # So, we have to manually pass the same `loop` to both applications to # make 100% sure it works and to avoid headaches. # -# Quart doesn't seem to offer a way to run inside `async def` -# (see https://gitlab.com/pgjones/quart/issues/146) so we must -# run and block on it last. +# To run Quart inside `async def`, we must use `hypercorn.asyncio.serve()` +# directly. # # This example creates a global client outside of Quart handlers. # If you create the client inside the handlers (common case), you -# won't have to worry about any of this. -client = TelegramClient(SESSION, API_ID, API_HASH) -client.parse_mode = 'html' # <- render things nicely -app.run(loop=client.loop) # <- same event loop as telethon +# won't have to worry about any of this, but it's still good to be +# explicit about the event loop. +if __name__ == '__main__': + client.loop.run_until_complete(main())