Чтение канала через Telegram-userbot'а через Pyrogram или telethon

17.01.2025

Илья Замарацких

Telegram

Python

Содержание

Во время разработки одного из проектов появилась необходимость автоматического получения постов из Telegram канала, на который пользователь подписан. Со стороны ботов это возможно только при условии участия бота в администрации канала, что не предоставлялось возможным. Обходным решением может быть создание бота на основе пользовательского аккаунта при помощи библиотеки Pyrogram.

Обратите внимание: автор Pyrogram прекратил поддержку библиотеки 24 декабря 2024. Автор статьи не гарантирует совместимость с современными функциями Telegram и работу кода. В данной статье используется Python версии 3.12.7.

Также, во время реализации бота на Pyrogram попалась библиотека Telethon. Она поддерживается, работает в некоторых ситуациях стабильнее, но куда хуже задокументирована с позиции типов, что может усложнить процесс разработки, особенно если вы используете аннотацию типов, как я.

Создание приложения

Прежде чем начать разработку бота, потребуется создать приложение для Telegram на специальной странице. Посредством этого приложения будет получен доступ к клиентскому API Telegram

Telegram apps page

После аутентификации, перейдите в раздел API development tools. Далее, потребуется создать приложение с произвольными данными.

Telegram app create page

После создания приложения, потребуется сохранить параметры api_id и api_hash.

Telegram app params

Подготовка окружения

Создание файла окружения

Создайте файл .env, в котором будут описаны все требуемые параметры:

# api_id приложения Telegram
USERBOT_APP_ID=123 
# api_hash приложения Telegram
USERBOT_APP_HASH=abc
# мобильный телефон аккаунта, который вы будете использовать. Используется только в Pyrogram
USER_PHONE=88005553535

Установка зависимостей

Для обоих библиотек потребуется вспомогательная библиотека для загрузки параметров из .env, я буду использовать python-dotenv

# для pip
pip install python-dotenv==1.0.1

Далее, установите требуемую библиотеку, которую хотите использовать:

# установка pyrogram
pip install pyrogram==2.0.106
# установка telethon
pip install telethon==1.37.0

Как долго живет сессия?

Зависит от настроек аккаунта. Сессия Pyrogram/telethon регистрируется как полноценная сессия в Telegram и может быть прервана досрочно или по тайм-ауту активности, который можно установить в пользовательских настройках.

Как получить ID канала?

С точки зрения клиента, пост в канале - сообщение в отдельном чате, поэтому в первую очередь потребуется ID канала. Один из простых способов для этого - переслать сообщение боту @ShowJsonBot. Далее, найти поле forward_origin и взять оттуда значение id. В примере ниже - это значение -1001112223333. Для Pyrogram потребуется полное значение, для telethon уберите -100 в начале, то есть оставьте значение 1112223333

...
  "forward_origin": {
   "type": "channel",
   "chat": {
    "id": -1001112223333,
    "title": "TestChannel",
    "type": "channel"
   },
   "message_id": 145151243,
   "date": 1735919220
  }
...

Поместите ID канала в переменную CHANNEL_ID в .env файле.

Разработка на Pyrogram

Получение файла сессии

Прежде чем мы начнем управлять аккаунту, потребуется создать и подтвердить сессию. Создадим для этого следующий скрипт

from dotenv import load_dotenv
import asyncio
from pyrogram.client import Client
import os

load_dotenv()  # загружаем .env

API_ID = os.environ["USERBOT_APP_ID"]
API_HASH = os.environ["USERBOT_APP_HASH"]
ACCOUNT_PHONE = os.environ["USER_PHONE"]


async def main():
    async with Client(
        name="userbot", api_id=API_ID, api_hash=API_HASH, phone_number=ACCOUNT_PHONE
    ) as app:
        await app.send_message("me", "It worked!")


if __name__ == "__main__":
    asyncio.run(main())

После запуска скрипта, вы получите примерно следующий вывод, где потребуется ввести код подтверждения из Telegram и облачный пароль, если он у вас настроен. Если все отработает правильно, вы увидите в Избранных сообщение It worked!

TgCrypto is missing! Pyrogram will work the same, but at a much slower speed. More info: https://docs.pyrogram.org/topics/speedups
Welcome to Pyrogram (version 2.0.106)
Pyrogram is free software and comes with ABSOLUTELY NO WARRANTY. Licensed
under the terms of the GNU Lesser General Public License v3.0 (LGPL-3.0).

The confirmation code has been sent via Telegram app
Enter confirmation code: <ВАШ КОД>
The two-step verification is enabled and a password is required
Password hint: None
Enter password (empty to recover): <ОБЛАЧНЫЙ ПАРОЛЬ>

Если вас интересует ускорение работы бота, можете изучить документацию по этой теме и установить дополнительные зависимости tgcrypto и применить uvloop в своем проекте для ускорения работы.

После выполнения скрипта будет создан файл userbot.session, который будет использоваться для управления аккаунтом. Если вы держите скрипт в другой директории (например, в папке src) лучше дополнительно указать параметр workdir для Client чтобы файл сессии не сохранялся в директорию кода. Следующий пример подойдет, если вы запускаете код из одной директории

# ...

async def main():
    async with Client(
        name="userbot",
        api_id=API_ID,
        api_hash=API_HASH,
        phone_number=ACCOUNT_PHONE,
        workdir="./",
    ) as app:
        await app.send_message("me", "It worked!")

# ...

Отслеживаем определенный канал

После этого, перепишем код и создадим функцию-обработчик, которая будет проверять, от требуемого чата это сообщение или нет.

from dotenv import load_dotenv
from pyrogram.types import Message
from pyrogram.client import Client
from pyrogram import filters
import os

load_dotenv()  # загружаем .env

API_ID = os.environ["USERBOT_APP_ID"]
API_HASH = os.environ["USERBOT_APP_HASH"]
ACCOUNT_PHONE = os.environ["USER_PHONE"]
CHANNEL_ID: int = int(os.environ["CHANNEL_ID"])

app = Client(
    name="userbot",
    api_id=API_ID,
    api_hash=API_HASH,
    phone_number=ACCOUNT_PHONE,
    workdir="./",
)


@app.on_message(filters.channel) # используем встроенный в библиотеку фильтр на сообщения из каналов
async def channel_handler(client: Client, message: Message):
    if message.sender_chat.id == CHANNEL_ID: # проверяем, нужный ли это канал
        if message.caption is not None: # проверяем, есть ли подпись к медиафайлу в сообщении
            print(message.caption)
        elif message.text is not None: # проверяем, есть ли у сообщения текст
            print(message.text)
        else:
            print("Сообщение без текста!")


if __name__ == "__main__":
    app.run()

Если вы хотите усложнить логику обработки, полезно будет изучить поля класса Message.

Полезные ссылки

Разработка на telethon

Получение файла сессии

Прежде чем мы начнем управлять аккаунту, потребуется создать и подтвердить сессию. Создадим для этого следующий скрипт

from dotenv import load_dotenv
from telethon import TelegramClient
import os

load_dotenv()  # загружаем .env

API_ID = os.environ["USERBOT_APP_ID"]
API_HASH = os.environ["USERBOT_APP_HASH"]

app = TelegramClient(
    session="userbot",
    api_id=int(API_ID),
    api_hash=API_HASH,
)

if __name__ == "__main__":
    app.start()
    app.run_until_disconnected()

После запуска скрипта, вы получите примерно следующий вывод, где потребуется ввести код подтверждения из Telegram и облачный пароль, если он у вас настроен. Если все отработает правильно, вы увидите аналогичный вывод в конце с именем вашего аккаунта. Можете завершить работу скрипта при помощи <CTRL+C>

Please enter your phone (or bot token): <ВАШ НОМЕР>
Please enter the code you received: <ВАШ КОД>
Please enter your password: <ОБЛАЧНЫЙ ПАРОЛЬ>
Signed in successfully as <ИМЯ ВАШЕГО АККАУНТА>; remember to not break the ToS or you will risk an account ban!

В качестве ускорения работы бота, можете установить библиотеку cryptg

После выполнения скрипта будет создан файл userbot.session, который будет использоваться для управления аккаунтом. Файл создается по умолчанию в рабочей директории, откуда вы запускаете скрипт.

Отслеживаем определенный канал

После этого, перепишем код и создадим функцию-обработчик, которая будет проверять, от требуемого чата это сообщение или нет.

from dotenv import load_dotenv
from telethon import TelegramClient, events, types
from telethon.tl.types import PeerChannel
import os

load_dotenv()  # загружаем .env

API_ID = os.environ["USERBOT_APP_ID"]
API_HASH = os.environ["USERBOT_APP_HASH"]
CHANNEL_ID: int = int(os.environ["CHANNEL_ID"])

app = TelegramClient(
    session="userbot",
    api_id=int(API_ID),
    api_hash=API_HASH,
)


@app.on(events.NewMessage(incoming=True))
async def handler(event: events.NewMessage.Event):
    message: types.Message = event.message
    text = message.message
    peer_id = event.message.peer_id
    if not isinstance(peer_id, PeerChannel):
        return

    channel_id = peer_id.channel_id
    if channel_id == CHANNEL_ID:
        if text is None or len(text) == 0:
            print("Нет текста!")
            return
        print(text)


if __name__ == "__main__":
    app.start()
    app.run_until_disconnected()

Флаг incoming отвечает за фильтрацию входящих сообщений для исключения отправленных нами сообщений.

Полезные ссылки

Заключение

Оба примера выводят текст сообщения в консоль, однако вы можете свободно расширить логику. Для своего проекта я помещаю сообщения в очередь RabbitMQ для дальшейшей обработки. Надеюсь, это руководство поможет вам в реализации ваших проектов!