From 2bfa13b0233df3a952e71807e996d975fd2d98cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=9F=D0=B0?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE=D0=B2?= Date: Thu, 10 Jul 2025 20:49:41 +0300 Subject: [PATCH] =?UTF-8?q?Alert=20=D0=BF=D0=B5=D1=80=D0=B2=D1=8B=D0=B9=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BC=D0=B8=D1=82=20=D0=9E=D1=81=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BD=D0=BE=D0=B9=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=BE?= =?UTF-8?q?=D0=BD=D0=B0=D0=BB:=20*=20=D0=9F=D0=BE=D1=80=D1=82=D0=B0=D1=82?= =?UTF-8?q?=D0=B8=D0=B2=D0=BD=D0=B0=D1=8F=20=D1=81=D1=80=D0=B5=D0=B4=D0=B0?= =?UTF-8?q?=20=D0=B2=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=BD=D0=B0=20=D0=B2=D0=B8=D0=BD=D0=B4=D0=BE=D0=B2=D1=81=20?= =?UTF-8?q?*=20=D0=9F=D0=B5=D1=80=D0=B5=D1=81=D1=8B=D0=BB=D0=BA=D0=B0=20?= =?UTF-8?q?=D1=81=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=B8?= =?UTF-8?q?=D0=B7=20=D0=BF=D1=83=D0=B1=D0=BB=D0=B8=D1=87=D0=BD=D0=BE=D0=B3?= =?UTF-8?q?=D0=BE=20=D0=BA=D0=B0=D0=BD=D0=B0=D0=BB=D0=B0=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=B2=D1=8B=D0=BC=20=D1=81=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BC=20*=20=D0=9E=D1=82=D1=81=D0=BB=D0=B5?= =?UTF-8?q?=D0=B6=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=B8=D0=B7=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D1=81=D0=BB=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D1=81=D0=BE=D0=BE?= =?UTF-8?q?=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B9=20*=20=D0=9E=D1=87=D0=B8?= =?UTF-8?q?=D1=81=D1=82=D0=BA=D0=B0=20=D0=BE=D1=82=20=D1=81=D0=B8=D1=81?= =?UTF-8?q?=D1=82=D0=B5=D0=BC=D0=BD=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + main.py | 298 ++++++++++++++++++++++++++++++++++++++ portable_install.bat | 220 ++++++++++++++++++++++++++++ requirements.txt | 2 + telethon_get_chat_list.py | 24 +++ 5 files changed, 547 insertions(+) create mode 100644 .gitignore create mode 100644 main.py create mode 100644 portable_install.bat create mode 100644 requirements.txt create mode 100644 telethon_get_chat_list.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c96afa --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.env +venv* +python* \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..11021da --- /dev/null +++ b/main.py @@ -0,0 +1,298 @@ +# -*- coding: utf-8 -*- + +from telethon import TelegramClient, events +import re +import os +import json +import logging +import asyncio +from datetime import datetime, timedelta +from dotenv import find_dotenv, load_dotenv + +# ==== Настройка логирования ==== +logging.basicConfig( + format='[%(levelname)s] %(asctime)s | %(message)s', + level=logging.INFO, + handlers=[ + logging.StreamHandler() # Вывод в консоль + # logging.FileHandler("bot.log", encoding="utf-8") # Раскомментируй, чтобы сохранять логи в файл + ] +) +log = logging.getLogger(__name__) + +# === Загрузка переменных окружения === +env = find_dotenv() +log.info(f'Использую: {env}') +load_dotenv(env) + +# === Данные юзер-бота === +api_id=os.getenv('api_id') +api_hash=os.getenv('api_hash') +session_name=os.getenv('session_name') + +# === Настройки === +source_channel_username=os.getenv('source_channel_username') # Канал, который слушаем +target_group_chat_id=int(os.getenv('target_group_chat_id')) # Группа, куда пересылаем + +filter_keywords=os.getenv('filter_keywords') # Строки или регулярное выражение для поиска +filter_keywords=filter_keywords.split(',') +filter_negative_keywords=os.getenv('filter_negative_keywords') # Строки или регулярное выражение для поиска +filter_negative_keywords=filter_negative_keywords.split(',') +DATA_FILE=os.getenv('DATA_FILE') # Файл для сохранения сообщений +MAX_MESSAGES=int(os.getenv('MAX_MESSAGES')) # Количество хранящихся сообщений +CHECK_INTERVAL=int(os.getenv('CHECK_INTERVAL')) # Периодичность проверки удалений на канале + +# ======================= + +client = TelegramClient(session_name, api_id, api_hash) + +# ==== Загрузка/сохранение состояния ==== +def load_state(): + if os.path.exists(DATA_FILE): + with open(DATA_FILE, 'r', encoding='utf-8') as f: + return json.load(f) + return {} + +def save_state(data): + with open(DATA_FILE, 'w', encoding='utf-8') as f: + json.dump(data, f, ensure_ascii=False, indent=2) + +# ==== Хранение данных ==== +source_messages = load_state() # {msg_id: {'text': ..., 'forwarded_msg_id': ...}} + +# ======================= +def shorten(text: str, length: int = 80) -> str: + '''Возвращает короткое сообщение для логов''' + text = text.replace('\n', ' ↵ ') + if len(text)>length: + return f'{text[:length-1]}…' + return text + +# ==== Обработчик сообщений из источника ==== +@client.on(events.NewMessage(chats=source_channel_username)) +async def handler(event): + message_text = event.message.text or "" + msg_id = event.message.id + + if message_text: + log.info(f"Новое сообщение: {shorten(message_text)}") + + # Проверяем само сообщение + if any(re.search(keyword, message_text, re.IGNORECASE) for keyword in filter_keywords) \ + and not any(re.search(keyword, message_text, re.IGNORECASE) for keyword in filter_negative_keywords): + new_msg = await client.forward_messages(target_group_chat_id, event.message) + source_messages[msg_id] = { + 'text': message_text, + 'timestamp': datetime.now().timestamp(), + 'forwarded_msg_id': new_msg.id + } + # Ограничиваем размер словаря + if len(source_messages) > MAX_MESSAGES: + oldest = min(source_messages.keys(), key=lambda k: source_messages[k]['timestamp']) + del source_messages[oldest] + save_state(source_messages) + log.info(f"📩 Переслано: {shorten(message_text)}") + return + + # Проверяем цепочку ответов + reply = event.message.reply_to + if reply and reply.reply_to_msg_id: + founded = await check_reply_chain(event, reply.reply_to_msg_id, depth=1) + if founded: + await client.forward_messages(target_group_chat_id, event.message) + log.info(f"⛓ Переслано по цепочке: {shorten(message_text)} | Глубина: {founded}") + + +# ==== Фоновая задача: проверка удалений и изменений ==== +async def monitor_messages(): + while True: + await asyncio.sleep(CHECK_INTERVAL) + + now = datetime.now().timestamp() + cutoff_time = now - 86400 # 24 часа + + for str_msg_id, stored_m_info in list(source_messages.items()): + msg_id = int(str_msg_id) + + # Удаление по возрасту + if stored_m_info['timestamp'] < cutoff_time: + del source_messages[str_msg_id] + continue + + # Получаем данные о пересланном сообщении + try: + target_msg = await client.get_messages(target_group_chat_id, ids=stored_m_info['forwarded_msg_id']) + except Exception as e: + log.warning(f"⚠ Не удалось получить пересланное сообщение {msg_id}: {e}") + target_msg = None + continue + # Если пересланного сообщения нет (удалено) + if target_msg is None or getattr(target_msg, 'empty', False): + log.info(f"🗑 Пересланное сообщение отсутвует (удалено администратором): {shorten(stored_m_info['text'])}") + del source_messages[str_msg_id] + continue + + # Получаем сообщение из источника + try: + source_msg = await client.get_messages(source_channel_username, ids=msg_id) + except Exception as e: + log.warning(f"⚠ Не удалось получить исходное сообщение {msg_id}: {e}") + source_msg = None + continue + # Если исходного сообщения нет (удалено) + if source_msg is None or getattr(source_msg, 'empty', False): + log.info(f"🗑 Исходное сообщение удалено. Удаляю пересланное: {shorten(stored_m_info['text'])}") + await client.delete_messages(target_group_chat_id, msg_id) + del source_messages[str_msg_id] + continue + + # Если текст изменился + if source_msg.text != target_msg.text: + log.info(f"📝 Сообщение изменено: {shorten(target_msg.text)} → {shorten(source_msg.text)}") + # Редактируем пересланное сообщение + try: + # Получаем ID последнего сообщения в целевом чате + last_msg = await client.get_messages(target_group_chat_id, limit=1) + last_msg_id = last_msg[0].id if last_msg else None + + # Проверяем, является ли наше пересланное сообщение последним + if target_msg.id == last_msg_id: + # Это последнее сообщение — можно удалить и переслать заново + await client.delete_messages(target_group_chat_id, target_msg.id) + new_msg = await client.forward_messages(target_group_chat_id, source_msg) + source_messages[str(source_msg.id)] = { + 'text': source_msg.text, + 'timestamp': datetime.now().timestamp(), + 'forwarded_msg_id': new_msg.id + } + log.info("🔁 Сообщение было последним → Заменено") + else: + # Не последнее → отправляем изменённый текст как ответ + new_msg = await client.send_message( + target_group_chat_id, + f"🔄 Текст изменён:\n{source_msg.text}", + reply_to=stored_m_info['forwarded_msg_id'] + ) + source_messages[str(source_msg.id)] = { + 'text': source_msg.text, + 'timestamp': datetime.now().timestamp(), + 'forwarded_msg_id': new_msg.id + } + log.info("💬 Сообщение не последнее → Отправлен ответ") + except Exception as e: + log.error(f"❌ Не удалось обновить: {e}") + + # Сохраняем состояние + save_state(source_messages) + + +async def check_reply_chain(event, msg_id, depth=1, max_depth=10) -> bool|int: + """Рекурсивная проверка цепочки ответов""" + if depth > max_depth: + log.warning(f"🔁 Превышена максимальная глубина цепочки ({max_depth}), остановлено.") + return False + + try: + replied_msg = await event.get_reply_message() + except Exception as e: + log.warning(f"⚠ Не удалось получить сообщение по ID {msg_id}: {e}") + return False + + if not replied_msg: + return False + + text = replied_msg.text or "" + + log.info(f"🔍 Проверяю сообщение на уровне {depth}: {shorten(text,30)}") + + if any(re.search(keyword, text, re.IGNORECASE) for keyword in filter_keywords) \ + and not any(re.search(keyword, text, re.IGNORECASE) for keyword in filter_negative_keywords): + return depth + + if replied_msg.reply_to: + return await check_reply_chain(event, replied_msg.reply_to.reply_to_msg_id, depth + 1) + + return False + +# ==== Удаление системных сообщений о входе/выходе в целевой группе ==== +@client.on(events.ChatAction(chats=target_group_chat_id)) +async def del_join_leave(event: events): + actions = { + 'добавлен': event.user_added, + 'присодинился': event.user_joined, + 'покинул': event.user_left, + } + if True in actions.values(): + action = [act for act, param in actions.items() if param] + action = ', '.join(action) + full_name = ' '.join([name for name in (event.user.first_name, event.user.last_name) if name]) + await event.delete() + log.info(f"Удалено сообщение: Пользователь {full_name}({event.user_id}) {action} чат {target_group_chat_id}") + + +async def fetch_system_messages(days_to_check=5) -> tuple[int, int]: + """Собирает системные сообщения за последние N дней""" + system_messages = [] + + async with client: + try: + chat = await client.get_entity(target_group_chat_id) + except ValueError: + log.info("❌ Не удалось найти чат. Убедитесь, что вы состоите в группе.") + return system_messages + + log.info(f"🔍 Ищем системные сообщения в '{chat.title}' за последние {days_to_check} дней…\n") + + today = datetime.now() + cutoff_date = today - timedelta(days=days_to_check) + + async for message in client.iter_messages(chat): + # Проверяем дату + if message.date.replace(tzinfo=None) < cutoff_date: + continue + + # Проверяем, является ли сообщение системным (ChatAction) + if message.action: + time_str = message.date.strftime('%Y-%m-%d %H:%M:%S') + log.info(f"[{time_str}] | Тип действия: {message.action}") + system_messages.append((chat.id, message.id)) + + log.info(f"✅ Найдено {len(system_messages)} системных сообщений для удаления") + return system_messages + + +async def delete_system_msgs(messages_list): + """Удаляет найденные системные сообщения""" + if not messages_list: + log.info("🚫 Нет сообщений для удаления") + return + + async with client: + chat_id, _ = messages_list[0] + message_ids = [msg_id for _, msg_id in messages_list] + + result = await client.delete_messages(chat_id, message_ids) + deleted_count = len(result._deleted) + + log.info(f"🗑 Удалено {deleted_count} из {len(message_ids)} сообщений") + + +# ==== Запуск бота ==== +async def main(): + await client.start() + + # log.info("🧹 Начинаем очистку старых системных сообщений…") + # messages = await fetch_system_messages(days_to_check=5) + # await delete_system_msgs(messages) + # if not client.is_connected(): + # log.info("🔄 Клиент не подключён, пытаемся восстановить соединение…") + # await client.connect() + + asyncio.create_task(monitor_messages()) + + log.info("😊 Бот запущен. Ожидание событий…") + await client.run_until_disconnected() + +# ======================= +if __name__ == '__main__': + client.loop.run_until_complete(main()) \ No newline at end of file diff --git a/portable_install.bat b/portable_install.bat new file mode 100644 index 0000000..deee269 --- /dev/null +++ b/portable_install.bat @@ -0,0 +1,220 @@ +@echo off +chcp 65001 >nul +cd /d "%~dp0%" +setlocal enabledelayedexpansion + +REM Установка переменных +set home_dir=%~dp0% +set python_dir=python312 +set python_url=https://www.python.org/ftp/python/3.12.10/python-3.12.10-embed-amd64.zip + +:start +REM Проверяем, установлен ли уже 7za.exe +if exist "%python_dir%\7za.exe" (move /y %python_dir%\7za.exe 7za.exe) >nul +if exist 7za.exe ( + echo [ ] 7zip уже скачан: Использую локальную версию +) else ( + echo [*] Скачивание 7zip + certutil.exe -urlcache -split -f "https://www.7-zip.org/a/7zr.exe" 7zr.exe >nul + certutil.exe -urlcache -split -f "https://www.7-zip.org/a/7z2500-extra.7z" 7z2500-extra.7z >nul + 7zr.exe x 7z2500-extra.7z 7za.exe -y >nul + del 7zr.exe + del 7z2500-extra.7z +) + +if exist "%python_dir%/python.exe" ( + for /f "tokens=2" %%V in ('cmd /c ^""%python_dir%\python.exe" --version^"') do set "python_version=%%V" + echo [ ] Python уже скачан: Использую локальную версию !python_version! +) else ( + echo [*] Скачивание Python + certutil.exe -urlcache -split -f "https://www.python.org/ftp/python/3.12.10/python-3.12.10-embed-amd64.zip" python.zip >nul + 7za.exe x python.zip -o"%python_dir%" >nul + for /f "tokens=2" %%V in ('cmd /c ^""%python_dir%\python.exe" --version^"') do set "python_version=%%V" + for /f "tokens=1,2 delims=." %%a in ("!python_version!") do set "python_short_version=python%%a%%b" + echo [*] Скачан Python !python_version! + del python.zip + + echo [*] Установка pip + cd "%python_dir%" + certutil.exe -urlcache -split -f "https://bootstrap.pypa.io/get-pip.py" get-pip.py >nul + python.exe get-pip.py --no-warn-script-location + del get-pip.py + echo [*] Изменение !python_short_version!._pth + echo Lib>> !python_short_version!._pth + echo Lib\site-packages>> !python_short_version!._pth + echo import site>> !python_short_version!._pth + python.exe -m pip install --upgrade pip setuptools wheel --no-cache-dir --no-warn-script-location + cd .. + if exist "requirements.txt" ( + %python_dir%\python.exe -m pip install -r requirements.txt --no-cache-dir --no-warn-script-location + ) +) + +:venv_setup +set update=False +set no_changes=False +if exist "%python_dir%\Scripts\activate.bat" ( +if exist "%python_dir%\pyvenv.cfg" ( + echo [ ] Виртуальное окружение уже сконфигурировано\ + + for /f "tokens=2 delims==" %%H in ('findstr /r "^base-executable[ ]*=" "%python_dir%\pyvenv.cfg"') do (set "installed_home=%%H") + rem Удаляем пробелы и кавычки по краям + for /f "tokens=* delims= " %%B in ("!installed_home!") do (set "installed_home=%%B") + set "installed_home=!installed_home:"=!" + rem Выводим для отладки + echo Текущий путь в pyvenv.cfg: !installed_home! + echo Ожидаемый путь: %home_dir%%python_dir%\python.exe + + rem Сравниваем пути + if /i "!installed_home!"=="%home_dir%%python_dir%\python.exe" ( + echo Конфигурация верная. Обновление не требуется + set no_changes=True + goto skip_venv + ) else ( + echo Путь портативной устаноки изменился. + set /p "update=Требуется обновление. Обновить? [Y/Д] нет по умолчанию: " + if /i "!update!"=="Y" ( + set update=True + echo Обновляем... + ) else ( + if /i "!update!"=="Д" ( + set update=True + echo Обновляем... + goto venv_config + ) + echo Пропускаем обновление. + goto skip_venv + ) + ) +)) + +:venv_config +echo [*] Конфигурация виртуального окружения + +if exist %python_dir%\Lib\site-packages\sitecustomize.py ( + echo [ ] Lib\site-packages\sitecustomize.py существует и не требует обновления + goto skip_sitecustomize +) else ( + echo [*] Создание sitecustomize.py для исправления site.getsitepackages и sys.prefix +) +( + echo import site + echo import os + echo import sys + echo project_root = os.path.abspath^(os.path.join^(os.getenv^('VIRTUAL_ENV'^), '..'^)^) + echo if project_root in sys.path: + echo sys.path.remove^(project_root^) + echo sys.prefix = os.getenv^('VIRTUAL_ENV'^) + echo sys.exec_prefix = os.getenv^('VIRTUAL_ENV'^) + echo def getsitepackages^(^): + echo return [os.path.join^(os.getenv^('VIRTUAL_ENV'^), 'Lib', 'site-packages'^)] + echo site.getsitepackages = getsitepackages +) > "%python_dir%\Lib\site-packages\sitecustomize.py" +:skip_sitecustomize + +echo [*] Создание %python_dir%\pyvenv.cfg +( + echo home = %home_dir%%python_dir% + echo implementation = CPython + echo version_info = %python_version% + echo virtualenv = 20.31.2 + echo include-system-site-packages = false + echo base-prefix = %home_dir%%python_dir% + echo base-exec-prefix = %home_dir%%python_dir% + echo base-executable = %home_dir%%python_dir%\python.exe +) > "%python_dir%\pyvenv.cfg" + +echo [*] Создание %python_dir%\Scripts\activate.bat +( + echo @echo off + echo REM This file is UTF-8 encoded, so we need to update the current code page while executing it + echo chcp 65001 ^> nul + echo set "VIRTUAL_ENV=%home_dir%%python_dir%" + echo set "VIRTUAL_ENV_PROMPT=%python_dir%" + echo if defined _OLD_VIRTUAL_PROMPT ^( + echo set "PROMPT=%%_OLD_VIRTUAL_PROMPT%%" + echo ^) else ^( + echo if not defined PROMPT ^( + echo set "PROMPT=$P$G" + echo ^) + echo if not defined VIRTUAL_ENV_DISABLE_PROMPT ^( + echo set "_OLD_VIRTUAL_PROMPT=%%PROMPT%%" + echo ^) + echo ^) + echo if not defined VIRTUAL_ENV_DISABLE_PROMPT ^( + echo set "PROMPT=(%%VIRTUAL_ENV_PROMPT%%) %%PROMPT%%" + echo ^) + echo if defined _OLD_VIRTUAL_PYTHONHOME ^( + echo set "PYTHONHOME=%%_OLD_VIRTUAL_PYTHONHOME%%" + echo ^) else ^( + echo set "_OLD_VIRTUAL_PYTHONHOME=%%PYTHONHOME%%" + echo ^) + echo set "PYTHONHOME=" + echo if defined _OLD_VIRTUAL_PATH ^( + echo set "PATH=%%_OLD_VIRTUAL_PATH%%" + echo ^) else ^( + echo set "_OLD_VIRTUAL_PATH=%%PATH%%" + echo ^) + echo set "PATH=%%VIRTUAL_ENV%%;%%VIRTUAL_ENV%%\Scripts;%%PATH%%" + echo set "PYTHONPATH=%%VIRTUAL_ENV%%\Lib\site-packages" + echo chcp %%_OLD_CODEPAGE%% ^> nul +) > %python_dir%\Scripts\activate.bat +:skip_venv + +call %python_dir%\Scripts\activate.bat + +if exist run_me.bat ( + echo [ ] run_me.bat существует, не требует обновления + goto skip_run_me +) +echo [+] Создание run_me.bat +( + echo cd /d "%%~dp0%%" + echo call %python_dir%\Scripts\activate.bat + echo call python main.py + echo pause +) > run_me.bat +:skip_run_me + +REM Храним 7zip внутри папки Python +if exist 7za.exe (move /y 7za.exe %python_dir%\7za.exe )>nul + +if %no_changes%==True ( + set /p "reinstall=Изменений не внесено. Желаете переустановить? [Y/Д] нет по умолчанию: " + if /i "!reinstall!"=="Y" ( + goto clear_installation + ) else ( + if /i "!reinstall!"=="Д" (goto clear_installation) + ) + goto end +) else ( + echo [✔️] Установка завершена + pause + goto end +) +:clear_installation +set /p "reinstall_python=Скачать Python и пакеты заново? [Y/Д] нет по умолчанию: " +if /i "!reinstall_python!"=="Y" ( + goto del_all_python +) else ( + if /i "!reinstall_python!"=="Д" (goto del_all_python) +) +goto del_only_settings + +:del_all_python +if exist "%python_dir%\7za.exe" (move /y %python_dir%\7za.exe 7za.exe) >nul +move /y %python_dir%\7za.exe 7za.exe>nul +echo [-] Удаление Python +rmdir /s /q %python_dir% +goto start + +:del_only_settings +echo [-] Удаление настроек окружения +del "%python_dir%\Lib\site-packages\sitecustomize.py" +del "%python_dir%\pyvenv.cfg" +del "%python_dir%\Scripts\activate.bat" +del run_me.bat +goto venv_setup + + +:end diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f9a0b41 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +Telethon==1.40.0 +dotenv==0.9.9 \ No newline at end of file diff --git a/telethon_get_chat_list.py b/telethon_get_chat_list.py new file mode 100644 index 0000000..f666a09 --- /dev/null +++ b/telethon_get_chat_list.py @@ -0,0 +1,24 @@ +import os +from telethon import TelegramClient +from dotenv import find_dotenv, load_dotenv + +# === Загрузка переменных окружения === +env = find_dotenv() +print(f'Использую: {env}') +load_dotenv(env) + +# === Данные юзер-бота === +api_id=26507458 +api_hash='9bf31965a06209eadd1995cec266d3ae' +session_name='Joshua_session' + +client = TelegramClient(session_name, api_id, api_hash) + +async def get_group_info(): + async with client: + groups = await client.get_dialogs() + for dialog in groups: + if dialog.is_group or dialog.is_channel: + print(f'Название: {dialog.name} | ID: {dialog.entity.id}') + +client.loop.run_until_complete(get_group_info()) \ No newline at end of file