Библиотека Telethon
предназначена для облегчения разработки программ на Python, которые могут взаимодействовать с платформой Telegram. Представляет собой асинхронную обертку API Telegram, которая делает всю тяжелую и нудную работу, тем самым позволяя сосредоточиться на разработке приложения.
Telethon
в виртуальное окружение:# создаем виртуальное окружение $ python3 -m venv .venv --prompt VirtualEnv # активируем виртуальное окружение $ source .venv/bin/activate # обновляем установщик пакетов python3 -m pip install --upgrade pip # ставим пакет telethon python3 -m pip install --upgrade telethon
Telethon
;entity
пакета Telethon
;Telethon
;Telethon
;Telethon
.Telethon
.Прежде чем работать с API Telegram, необходимо получить собственный идентификатор и хеш API:
Примечание. Идентификатор и хэш API используются только приложением, а не номером телефона. Другими словами можно использовать этот идентификатор и хэш с любым номером телефона или даже для учетных записей ботов.
После получения идентификатора и хэша API можно написать код для входа в учетную запись Telegram!
# файл tеst.py from telethon import TelegramClient # Подставляем собственные значения из `my.telegram.org` api_id = 12345 api_hash = '0123456789abcdef0123456789abcdef' # Первый аргумент - это имя файла `.session` (допускаются абсолютные пути). with TelegramClient('anon', api_id, api_hash) as client: client.loop.run_until_complete(client.send_message('me', 'Hello, myself!'))
Важно! Не называйте скрипт
telethon.py
! Python попытается импортировать клиент изtelethon.py
, которого там нет. В итоге сценарий завершится неудачей с ошибкойImportError
: cannot import nameTelegramClient ...
.
В первой строке импортируется имя класса, чтобы создать экземпляр клиента. Затем определяются переменные для удобного хранения идентификатора и хеша API.
Наконец, создается новый экземпляр TelegramClient
и называем его client
. Теперь можно использовать переменную client
для чего угодно, например для отправки сообщения самому себе.
Примечание. Так как
Telethon
является асинхронной библиотекой, то необходимо дождатьсяawait
выполнения функций сопрограммы (или, в противном случае, запускать цикл до их завершения). В этом примере не создаетсяасинхронное определение
async def`.
Использование контекстного менеджера with
является предпочтительным способом использования библиотеки. Он автоматически запустит клиент TelegramClient.start()
, выполнив вход или регистрацию при необходимости.
Если файл .session
уже существует и куда нибудь перемещен или переименован, то клиент не сможет снова войти (имейте это в виду)!
Также можно использовать пакет Telethon
для своих ботов (обычных учетных записей ботов, а не пользователей). Для этого все равно понадобится идентификатор и хэш API, но процесс очень похож:
from telethon.sync import TelegramClient api_id = 12345 api_hash = '0123456789abcdef0123456789abcdef' bot_token = '12345:0123456789abcdef0123456789abcdef' # если нам нужен явный токен бота, # то нужно вручную вызвать `TelegramClient.start()`. bot = TelegramClient('bot', api_id, api_hash).start(bot_token=bot_token) # Тогда можем использовать экземпляр клиента как обычно. with bot as bot_client: ...
Для получения аккаунта бота, необходимо поговорить с @BotFather
.
Для доступа к Telegram через прокси сервер, необходим Python >= 3.6 и установка модуля python-socks[asyncio]
. Затем в коде необходимо изменить строку с созданием клиента:
# было TelegramClient('anon', api_id, api_hash) # стало (замените протокол, IP и порт на собственные). proxy=("socks5", '127.0.0.1', 4444) TelegramClient('anon', api_id, api_hash, proxy=proxy)
Аргумент proxy
должен быть словарем (или кортежем для обратной совместимости), состоящим из параметров, как в описании модуля PySocks
.
Допустимые значения аргумента proxy_type
:
socks.SOCKS5
или 'socks5'
socks.SOCKS4
или 'socks4'
socks.HTTP
или 'http'
python_socks.ProxyType.SOCKS5
python_socks.ProxyType.SOCKS4
python_socks.ProxyType.HTTP
Пример:
proxy = { # обязательный протокол 'proxy_type': 'socks5', # обязательный IP-адрес прокси 'addr': '1.1.1.1', # обязательный номер порта прокси-сервера 'port': 5555, # имя пользователя, если прокси требует авторизации (необязательно) 'username': 'foo', # пароль, если прокси-сервер требует авторизации (необязательно) 'password': 'bar', # использовать удаленное или локальное разрешение, по умолчанию удаленное (необязательно) 'rdns': True } client = TelegramClient('anon', api_id, api_hash, proxy=proxy)
Для обратной совместимости с PySocks
возможен следующий формат (но не рекомендуется):
proxy = (socks.SOCKS5, '1.1.1.1', 5555, True, 'foo', 'bar') client = TelegramClient('anon', api_id, api_hash, proxy=proxy)
MTProto
.Прокси MTProto
- это альтернатива Telegram обычным прокси, работающая немного по-другому. Доступны следующие протоколы:
ConnectionTcpMTProxyAbridged
;ConnectionTcpMTProxyIntermediate
;ConnectionTcpMTProxyRandomizedIntermediate
(предпочтительный).Если используется прокси-сервер MTProto
, то на данный момент необходимо вручную указать эти специальные режимы подключения. В итоге код будет выглядеть следующим образом:
from telethon import TelegramClient, connection # необходимо изменить соединение client = TelegramClient( 'anon', api_id, api_hash, # Используем один из доступных режимов подключения. # Обычно этот вариант работает с большинством прокси. connection=connection.ConnectionTcpMTProxyRandomizedIntermediate, # Затем передайте данные прокси в виде кортежа: # (имя хоста, порт, секрет прокси) # Если у прокси нет секрета, то вместо него пишем: # '00000000000000000000000000000000' proxy=('mtproxy.example.com', 2002, 'secret') )
В будущих обновлениях команда разработчиков telethon
может упростить использование прокси-серверов MTProto
(например, убрать аргумент connection
).
Короче говоря, тот же код, который выше, но без комментариев, чтобы было понятнее:
from telethon import TelegramClient, connection client = TelegramClient( 'anon', api_id, api_hash, connection=connection.ConnectionTcpMTProxyRandomizedIntermediate, proxy=('mtproxy.example.com', 2002, 'secret') )
telethon
Рассмотрим более длинный пример, с целью изучения некоторых методов, которые может предложить библиотека telethon
. Они известны как "дружественные методы", и их необходимо использовать всегда, если это возможно.
Код ниже показывает, как войти в Telegram, получить информацию о себе, отправить сообщения, файлы, войти в чат, распечатать сообщения и загрузить файлы.
from telethon import TelegramClient # подставляем собственные значения api_id = 12345 api_hash = '0123456789abcdef0123456789abcdef' client = TelegramClient('anon', api_id, api_hash) async def main(): # Получение информации о себе me = await client.get_me() # `me` - это пользовательский объект. Можно красиво напечатать # любой объект Telegram с помощью метода `.stringify`: print(me.stringify()) # Можно получить доступ ко всем атрибутам объектов Telegram с помощью # оператора точки. Например, чтобы получить имя пользователя: username = me.username print(username) print(me.phone) # Можно распечатать все диалоги/разговоры, в которых вы участвуете: async for dialog in client.iter_dialogs(): print(dialog.name, 'has ID', dialog.id) # Можно отправлять сообщения самому себе... await client.send_message('me', 'Hello, myself!') # ... к какому-нибудь идентификатору чата await client.send_message(-100123456, 'Hello, group!') # ... к каким-то контактам await client.send_message('+34600123123', 'Hello, friend!') # ... или даже к любому имени пользователя await client.send_message('username', 'Testing Telethon!') # конечно, можно использовать `markdown` в своих сообщениях: message = await client.send_message( 'me', 'This message has **bold**, `code`, __italics__ and ' 'a [nice website](https://example.com)!', link_preview=False ) # Отправка сообщения возвращает объект отправленного # сообщения, который можно использовать повторно print(message.raw_text) # можно отвечать на сообщения напрямую, если есть объект сообщения. await message.reply('Cool!') # или отправляйте файлы, музыку, документы, альбомы... await client.send_file('me', '/home/me/Pictures/holidays.jpg') # можно распечатать историю сообщений любого чата: async for message in client.iter_messages('me'): print(message.id, message.text) # можно также загружать мультимедиа из сообщений! # Метод вернет путь, по которому был сохранен файл. if message.photo: path = await message.download_media() # печатает после завершения загрузки print('File saved to', path) with client: client.loop.run_until_complete(main())
Прежде чем продолжить, необходимо понимать, что делает представленный код, обратить внимание на то, как вызываются и используются методы и т. д.
ВАЖНО! Обратите внимание, что
Telethon
- это асинхронная библиотека, и поэтому к ней нужно привыкнуть и изучить основы модуляasyncio
. Это очень поможет. Для начала, можно написать весь свой код внутри некоторого асинхронного определения, например:client = ... async def do_something(me): ... async def main(): # Большая часть кода должна находиться здесь. # можно создать и использовать свою собственную асинхронную функцию (do_something). # функции должны быть быть асинхронными, если им нужно ждать получения данных. me = await client.get_me() await do_something(me) with client: client.loop.run_until_complete(main())После осознания всего этого, можно использовать (если хотите) хук
telethon.sync
, но необходимо учитывать, что можно столкнуться с другими проблемами (у iPython, Anaconda и т. д. есть некоторые проблемы с ним).
Получение обновлений - важная тема для такой платформы обмена сообщениями, как Telegram. Всегда встает потребность получать уведомления, когда приходит новое сообщение, когда участник присоединяется, когда кто-то начинает печатать и т. д. Для этого можно использовать события events
.
Важно! Настоятельно рекомендуется включать логирование при работе с событиями, так как исключения в обработчиках событий по умолчанию скрыты. Для этого необходимо добавить следующий фрагмент кода в самый верх сценария:
import logging logging.basicConfig(format='[%(levelname) 5s/%(asctime)s] %(name)s: %(message)s', level=logging.WARNING)
Начнем с примера автоматизации ответов:
from telethon import TelegramClient, events client = TelegramClient('anon', api_id, api_hash) @client.on(events.NewMessage) async def my_event_handler(event): if 'hello' in event.raw_text: await event.reply('hi!') client.start() client.run_until_disconnected()
В этом небольшом фрагменте кода некоторые вещи могут быть неясны. Разберем его:
@client.on(events.NewMessage)
Этот декоратор Python прикреплен к определению функции my_event_handler()
и по сути означает, что при событии NewMessage
будет вызвана упомянутая функция обратного вызова:
async def my_event_handler(event): if 'hello' in event.raw_text: await event.reply('hi!')
Если происходит событие NewMessage
и в тексте сообщения содержится слово hello, то отвечаем на это событие сообщением hi! event.reply('hi!')
.
Примечание. Обработчики событий должны быть асинхронными по определению. Telethon - это асинхронная библиотека, основанная на
asyncio
, которая является более безопасным и зачастую более быстрым подходом чем потокиthreads
.Другими словами, необходимо дождаться
await
всех вызовов, использующих сетевые запросы, а это большинство из них.
@client.on(events.NewMessage(outgoing=True, pattern=r'\.save')) async def handler(event): if event.is_reply: replied = await event.get_reply_message() sender = replied.sender await client.download_profile_photo(sender) await event.respond(f'Saved your photo {sender.username}')
Событие в коде фильтрует сообщения (только те, которые отправляем outgoing=True
, активируют метод), затем фильтруем по регулярному выражению r'.save'
, которое будет соответствовать сообщениям, начинающимся с '.save'
.
Внутри функции проверяем, отвечает ли событие на другое сообщение или нет. Если да, то получаем ответное сообщение и отправителя этого сообщения, а также загружаем его фотографию профиля.
Теперь удалим сообщения, содержащие слово 'heck'. Например, если в чате запрещено ругаться.
@client.on(events.NewMessage(pattern=r'(?i).*heck')) async def handler(event): await event.delete()
С помощью регулярного выражения r'(?i).*heck'
сопоставляем слово heck без учета регистра в любом месте сообщения.
Здесь представлен только объект события events.NewMessage
, но есть еще много других полезных событий.
Дополнительно смотрите материал "Все о получении обновлений модулем Telethon".
entity
пакета Telethon
.В библиотеке широко используется понятие entity
"сущности". Сущность будет ссылаться на любой объект User
, Chat
или Channel
, который API может вернуть в ответ на определенные методы, такие как GetUsersRequest
.
Когда нужен пользователь или чат, где произошло событие, необходимо использовать следующие методы:
async def handler(event): # Правильно chat = await event.get_chat() sender = await event.get_sender() chat_id = event.chat_id sender_id = event.sender_id # ПЛОХО. Не делате этого chat = event.chat sender = event.sender chat_id = event.chat.id sender_id = event.sender.id
События похожи на сообщения, но не содержат всей информации, которую содержит сообщение! Когда получаем сообщение вручную, оно будет содержать всю необходимую информацию. Когда получаем обновление о сообщении, оно не содержит всей информации, поэтому придется использовать методы, а не свойства.
Запомните как правило: новые события сообщений ведут себя так же, как объекты сообщений, поэтому с ними можно делать все, что можно делать с объектом сообщения.
Telethon
.При разработке любой библиотеки, некоторые решения неизбежно окажутся неправильными в будущем. Одним из таких решений было использование потоков. Начиная с Python 3.6 стало возможным использование модуля asyncio
с пользой для такой библиотеки, как Telethon.
Если приложение Telegram имеет старый код, просто используйте старые версии библиотеки! В этом нет ничего плохого, кроме отсутствия новых обновлений или исправлений, при этом возможно использовать исправленную версию с помощью pip install telethon==0.19.1.6
.
Нет смысла поддерживать синхронную версию Telethon, т.к. опыт показывает, что у людей нет времени обновляться, а было несколько изменений и чисток. Использование более старой версии - правильный путь.
Иногда принимаются и другие небольшие решения. Все это будет отражено в журнале изменений (истории версий), который следует прочитать при обновлении.
Если хотите окунуться в asyncio
, вот некоторые вещи, которые понадобятся, чтобы начать миграцию действительно старого кода:
# 1. Импортируйте клиент из telethon.sync from telethon.sync import TelegramClient # 2. Измените этого монстра... try: assert client.connect() if not client.is_user_authorized(): client.send_code_request(phone_number) me = client.sign_in(phone_number, input('Enter code: ')) ... # ОСТАЛЬНАЯ ЧАСТЬ КОДА finally: client.disconnect() # ...на следующую строку: with client: ... # ОСТАЛЬНАЯ ЧАСТЬ КОДА # 3. client.idle() больше не существует. # Измени это... client.idle() # на следующую строку: client.run_until_disconnected() # 4. `client.add_update_handler` больше не существует. # Измените это... client.add_update_handler(handler) # ...на следующую строку: client.add_event_handler(handler)
Кроме того, все обработчики обновлений должны быть асинхронными, т.е. нужно ожидать вызовов методов, которые зависят от сетевых запросов, таких как получение чата или отправителя. Если обновления не используются, то все готово!
Telethon
.Весь код ниже предполагает, что выполнено одно из следующих действий:
from telethon import TelegramClient, sync # или from telethon.sync import TelegramClientЭто делает примеры короче и облегчает размышление.
Для быстрых скриптов, не требующих обновлений (например публикация сообщений в канал Telegram), гораздо удобнее забыть об asyncio
и просто работать с последовательным кодом. Такое поведение может оказаться мощным гибридом для работы под управлением Python REPL.
# Обратите внимание, что `telethon.sync` # будет управлять циклом `asyncio` from telethon.sync import TelegramClient with TelegramClient(...) as client: # обратите внимание на отсутствие `await` или loop.run_until_complete(). # т.к. цикл не выполняется, это делается за кулисами. print(client.get_me().username) message = client.send_message('me', 'Hi!') import time time.sleep(5) message.delete() # Также можно использовать гибрид синхронной # части и асинхронных обработчиков событий. from telethon import events @client.on(events.NewMessage(pattern='(?i)hi|hello')) async def handler(event): await event.reply('hey') client.run_until_disconnected()
Контекстный менеджер with
и некоторые методы, такие как, TelegramClient.start()
, TelegramClient.disconnect()
и TelegramClient.run_until_disconnected()
, по умолчанию работают как в синхронном, так и в асинхронном контекстах для удобства и во избежание небольших накладных расходов при использовании таких методов, как отправка сообщения, получение сообщений и т.д. Это сохраняет лучшее из оба мира как разумный вариант по умолчанию.
Примечание. Как правило, находясь внутри
async def
и при этом нужен клиент, то необходимо дождатьсяawait
вызовов API. Если вызываете другие функции, которым также требуются вызовы API, сделайте ихasync def
и тоже ожидайте их. В противном случае в асинхронном режиме делать нечего.
Telethon
.Когда будете готовы микро-оптимизировать свое приложение или просто не требуется вызывать какие-либо неосновные методы из синхронного контекста, то просто избавьтесь от импорта telethon.sync
и работайте внутри async def
:
Волшебный модуль telethon.sync
по сути оборачивает каждый метод asyncio.run(main())
с некоторыми другими хитростями. Это накладные расходы, которые платите, если импортируете его, и то, что экономите, если не делаете этого.
import asyncio from telethon import TelegramClient, events async def main(): async with TelegramClient(...) as client: # обратите внимание на скобки, здесь ожидается # вызов `client.get_me()`, а не свойства `.username`. print((await client.get_me()).username) message = await client.send_message('me', 'Hi!') await asyncio.sleep(5) await message.delete() @client.on(events.NewMessage(pattern='(?i)hi|hello')) async def handler(event): await event.reply('hey') await client.run_until_disconnected() asyncio.run(main())
Библиотека везде использует
asyncio
. Хотите научиться делать все правильно? Несмотря на то, что модулюasyncio
посвящен отдельный раздел на https://docs-python.ru, в документации к библиотекеTelethon
существует материал под названиемОсвоение asyncio для использования с Telethon
, который познакомит с миромasyncio
.