mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-25 10:53:44 +03:00
Add quart-based example (#1207)
This commit is contained in:
parent
4f1edeb750
commit
2a7d4317bd
|
@ -213,6 +213,9 @@ lot of headaches to get threads and asyncio to work together. Instead,
|
||||||
consider using `Quart <https://pgjones.gitlab.io/quart/>`_, an asyncio-based
|
consider using `Quart <https://pgjones.gitlab.io/quart/>`_, an asyncio-based
|
||||||
alternative to `Flask <flask.pocoo.org/>`_.
|
alternative to `Flask <flask.pocoo.org/>`_.
|
||||||
|
|
||||||
|
Check out `quart_login.py`_ for an example web-application based on Quart.
|
||||||
|
|
||||||
.. _logging: https://docs.python.org/3/library/logging.html
|
.. _logging: https://docs.python.org/3/library/logging.html
|
||||||
.. _@SpamBot: https://t.me/SpamBot
|
.. _@SpamBot: https://t.me/SpamBot
|
||||||
.. _issue 297: https://github.com/LonamiWebs/Telethon/issues/297
|
.. _issue 297: https://github.com/LonamiWebs/Telethon/issues/297
|
||||||
|
.. _quart_login.py: https://github.com/LonamiWebs/Telethon/tree/master/telethon_examples#quart_loginpy
|
||||||
|
|
|
@ -90,6 +90,30 @@ send messages, delete them, and download media. The code is a bit
|
||||||
long which may make it harder to follow, and requires saving some
|
long which may make it harder to follow, and requires saving some
|
||||||
state in order for downloads to work later.
|
state in order for downloads to work later.
|
||||||
|
|
||||||
|
### [`quart_login.py`]
|
||||||
|
|
||||||
|
* Usable as: **user**.
|
||||||
|
* Difficulty: **medium**.
|
||||||
|
|
||||||
|
Web-based application using [Quart](https://pgjones.gitlab.io/quart/index.html)
|
||||||
|
(an `asyncio` alternative to [Flask](http://flask.pocoo.org/)) and Telethon
|
||||||
|
together.
|
||||||
|
|
||||||
|
The example should work as a base for Quart applications *with a single
|
||||||
|
global client*, and it should be easy to adapt for multiple clients by
|
||||||
|
following the comments in the code.
|
||||||
|
|
||||||
|
It showcases how to login manually (ask for phone, code, and login),
|
||||||
|
and once the user is logged in, some messages and photos will be shown
|
||||||
|
in the page.
|
||||||
|
|
||||||
|
There is nothing special about Quart. It was chosen because it's a
|
||||||
|
drop-in replacement for Flask, the most popular option for web-apps.
|
||||||
|
You can use any `asyncio` library with Telethon just as well,
|
||||||
|
like [Sanic](https://sanic.readthedocs.io/en/latest/index.html) or
|
||||||
|
[aiohttp](https://docs.aiohttp.org/en/stable/). You can even use Flask,
|
||||||
|
if you learn how to use `threading` and `asyncio` together.
|
||||||
|
|
||||||
### [`gui.py`]
|
### [`gui.py`]
|
||||||
|
|
||||||
* Usable as: **user and bot**.
|
* Usable as: **user and bot**.
|
||||||
|
@ -111,6 +135,7 @@ assumes some [`asyncio`] knowledge, but otherwise is easy to follow.
|
||||||
[CC0 License]: https://github.com/LonamiWebs/Telethon/blob/master/telethon_examples/LICENSE
|
[CC0 License]: https://github.com/LonamiWebs/Telethon/blob/master/telethon_examples/LICENSE
|
||||||
[@BotFather]: https://t.me/BotFather
|
[@BotFather]: https://t.me/BotFather
|
||||||
[`assistant.py`]: https://raw.githubusercontent.com/LonamiWebs/Telethon/master/telethon_examples/assistant.py
|
[`assistant.py`]: https://raw.githubusercontent.com/LonamiWebs/Telethon/master/telethon_examples/assistant.py
|
||||||
|
[`quart_login.py`]: https://raw.githubusercontent.com/LonamiWebs/Telethon/master/telethon_examples/quart_login.py
|
||||||
[`gui.py`]: https://raw.githubusercontent.com/LonamiWebs/Telethon/master/telethon_examples/gui.py
|
[`gui.py`]: https://raw.githubusercontent.com/LonamiWebs/Telethon/master/telethon_examples/gui.py
|
||||||
[`interactive_telegram_client.py`]: https://raw.githubusercontent.com/LonamiWebs/Telethon/master/telethon_examples/interactive_telegram_client.py
|
[`interactive_telegram_client.py`]: https://raw.githubusercontent.com/LonamiWebs/Telethon/master/telethon_examples/interactive_telegram_client.py
|
||||||
[`print_messages.py`]: https://raw.githubusercontent.com/LonamiWebs/Telethon/master/telethon_examples/print_messages.py
|
[`print_messages.py`]: https://raw.githubusercontent.com/LonamiWebs/Telethon/master/telethon_examples/print_messages.py
|
||||||
|
|
120
telethon_examples/quart_login.py
Normal file
120
telethon_examples/quart_login.py
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
import base64
|
||||||
|
import os
|
||||||
|
|
||||||
|
from quart import Quart, request
|
||||||
|
|
||||||
|
from telethon import TelegramClient, utils
|
||||||
|
|
||||||
|
|
||||||
|
def get_env(name, message):
|
||||||
|
if name in os.environ:
|
||||||
|
return os.environ[name]
|
||||||
|
return input(message)
|
||||||
|
|
||||||
|
|
||||||
|
# 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: ')
|
||||||
|
|
||||||
|
|
||||||
|
# Helper method to add the HTML head/body
|
||||||
|
def html(inner):
|
||||||
|
return '''
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Telethon + Quart</title>
|
||||||
|
</head>
|
||||||
|
<body>{}</body>
|
||||||
|
</html>
|
||||||
|
'''.format(inner)
|
||||||
|
|
||||||
|
|
||||||
|
# Helper method to format messages nicely
|
||||||
|
async def format_message(message):
|
||||||
|
if message.photo:
|
||||||
|
content = '<img src="data:image/png;base64,{}" alt="{}" />'.format(
|
||||||
|
base64.b64encode(await message.download_media(bytes)).decode(),
|
||||||
|
message.raw_text
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# client.parse_mode = 'html', so bold etc. will work!
|
||||||
|
content = (message.text or '(action message)').replace('\n', '<br>')
|
||||||
|
|
||||||
|
return '<p><strong>{}</strong>: {}<sub>{}</sub></p>'.format(
|
||||||
|
utils.get_display_name(message.sender),
|
||||||
|
content,
|
||||||
|
message.date
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Define the global phone and Quart app variables
|
||||||
|
phone = None
|
||||||
|
app = Quart(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Check form parameters (phone/code)
|
||||||
|
form = await request.form
|
||||||
|
if 'phone' in form:
|
||||||
|
phone = form['phone']
|
||||||
|
await client.send_code_request(phone)
|
||||||
|
|
||||||
|
if 'code' in form:
|
||||||
|
await client.sign_in(code=form['code'])
|
||||||
|
|
||||||
|
# If we're logged in, show them some messages from their first dialog
|
||||||
|
if await client.is_user_authorized():
|
||||||
|
# They are logged in, show them some messages from their first dialog
|
||||||
|
dialog = (await client.get_dialogs())[0]
|
||||||
|
result = '<h1>{}</h1>'.format(dialog.title)
|
||||||
|
async for m in client.iter_messages(dialog, 10):
|
||||||
|
result += await(format_message(m))
|
||||||
|
|
||||||
|
return html(result)
|
||||||
|
|
||||||
|
# Ask for the phone if we don't know it yet
|
||||||
|
if phone is None:
|
||||||
|
return html('''
|
||||||
|
<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
|
||||||
|
return html('''
|
||||||
|
<form action="/" method="post">
|
||||||
|
Telegram code: <input name="code" type="text" placeholder="70707">
|
||||||
|
<input type="submit">
|
||||||
|
</form>''')
|
||||||
|
|
||||||
|
|
||||||
|
# By default, `Quart.run` uses `asyncio.run()`, which creates a new asyncio
|
||||||
|
# event loop. If we create the `TelegramClient` before, `telethon` will
|
||||||
|
# use `asyncio.get_event_loop()`, which is the implicit loop in the main
|
||||||
|
# thread. These two loops are different, and it won't work.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# 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
|
Loading…
Reference in New Issue
Block a user