Improve Quart example (#1229)

This commit is contained in:
Phil Jones 2019-08-01 17:15:04 +00:00 committed by Lonami
parent 2b277dd558
commit e1355ae5d8

View File

@ -1,7 +1,8 @@
import base64 import base64
import os import os
from quart import Quart, request import hypercorn.asyncio
from quart import Quart, render_template_string, request
from telethon import TelegramClient, utils from telethon import TelegramClient, utils
@ -12,24 +13,44 @@ def get_env(name, message):
return input(message) return input(message)
BASE_TEMPLATE = '''
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>Telethon + Quart</title>
</head>
<body>{{ content | safe }}</body>
</html>
'''
PHONE_FORM = '''
<form action='/' method='post'>
Phone (international format): <input name='phone' type='text' placeholder='+34600000000'>
<input type='submit'>
</form>
'''
CODE_FORM = '''
<form action='/' method='post'>
Telegram code: <input name='code' type='text' placeholder='70707'>
<input type='submit'>
</form>
'''
# Session name, API ID and hash to use; loaded from environmental variables # Session name, API ID and hash to use; loaded from environmental variables
SESSION = os.environ.get('TG_SESSION', 'quart') SESSION = os.environ.get('TG_SESSION', 'quart')
API_ID = int(get_env('TG_API_ID', 'Enter your API ID: ')) API_ID = int(get_env('TG_API_ID', 'Enter your API ID: '))
API_HASH = get_env('TG_API_HASH', 'Enter your API hash: ') 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 # Quart app
def html(inner): app = Quart(__name__)
return ''' app.secret_key = 'CHANGE THIS TO SOMETHING SECRET'
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Telethon + Quart</title>
</head>
<body>{}</body>
</html>
'''.format(inner)
# Helper method to format messages nicely # Helper method to format messages nicely
@ -50,18 +71,20 @@ async def format_message(message):
) )
# Define the global phone and Quart app variables # Connect the client before we start serving with Quart
phone = None @app.before_serving
app = Quart(__name__) 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']) @app.route('/', methods=['GET', 'POST'])
async def root(): 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 # We want to update the global phone variable to remember it
global phone global phone
@ -82,22 +105,18 @@ async def root():
async for m in client.iter_messages(dialog, 10): async for m in client.iter_messages(dialog, 10):
result += await(format_message(m)) 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 # Ask for the phone if we don't know it yet
if phone is None: if phone is None:
return html(''' return await render_template_string(BASE_TEMPLATE, content=PHONE_FORM)
<form action="/" method="post">
Phone (international format): <input name="phone" type="text" placeholder="+34600000000">
<input type="submit">
</form>''')
# We have the phone, but we're not logged in, so ask for the code # We have the phone, but we're not logged in, so ask for the code
return html(''' return await render_template_string(BASE_TEMPLATE, content=CODE_FORM)
<form action="/" method="post">
Telegram code: <input name="code" type="text" placeholder="70707">
<input type="submit"> async def main():
</form>''') await hypercorn.asyncio.serve(app, hypercorn.Config())
# By default, `Quart.run` uses `asyncio.run()`, which creates a new asyncio # 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 # So, we have to manually pass the same `loop` to both applications to
# make 100% sure it works and to avoid headaches. # make 100% sure it works and to avoid headaches.
# #
# Quart doesn't seem to offer a way to run inside `async def` # To run Quart inside `async def`, we must use `hypercorn.asyncio.serve()`
# (see https://gitlab.com/pgjones/quart/issues/146) so we must # directly.
# run and block on it last.
# #
# This example creates a global client outside of Quart handlers. # This example creates a global client outside of Quart handlers.
# If you create the client inside the handlers (common case), you # If you create the client inside the handlers (common case), you
# won't have to worry about any of this. # won't have to worry about any of this, but it's still good to be
client = TelegramClient(SESSION, API_ID, API_HASH) # explicit about the event loop.
client.parse_mode = 'html' # <- render things nicely if __name__ == '__main__':
app.run(loop=client.loop) # <- same event loop as telethon client.loop.run_until_complete(main())