diff --git a/.gitignore b/.gitignore index 7cdcf54..56c5ae7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .fleet __pycache__ keys*.py -vkdel.py \ No newline at end of file +vkdel.py +tgdel.py +*.session \ No newline at end of file diff --git a/news-bot.py b/news-bot.py index 5e84ff3..52a8590 100755 --- a/news-bot.py +++ b/news-bot.py @@ -3,17 +3,21 @@ import re import sys import time -import urllib.parse +import asyncio import discord import logging import colorlog import requests import html2text +import urllib.parse + +from telethon import events from bs4 import BeautifulSoup +from telethon.sync import TelegramClient +from telethon.errors import FloodWaitError import keys - url_post = "https://linux-gaming.ru/posts.json" url_news = "https://linux-gaming.ru/c/news/6.json" url_vk_post = "https://api.vk.com/method/wall.post" @@ -58,6 +62,7 @@ def main(): check_version(last_changelog, resp_changelog) check_discord_public() check_vk_posts() + check_tg_news() def make_soup(resp_changelog): @@ -87,6 +92,7 @@ def html_to_text(html_content): # Удаление строк, содержащих '* * *' markdown_text = re.sub(r'^.*\* \* \*.*$', '', markdown_text, flags=re.MULTILINE) + # Фикс ненумерованных списков markdown_text = re.sub(r'^.*\* ', '* ', markdown_text, flags=re.MULTILINE) # Убираем переносы строк, кроме строк, начинающихся с * @@ -111,6 +117,10 @@ def html_to_text(html_content): # Удаление избыточных пустых строк после удаления строк markdown_text = re.sub(r'\n\s*\n', '\n', markdown_text) + # Замена текстов типа "image1280×474 99.7 KB", "807×454 64.1 KB" на "." + markdown_text = re.sub(r'image\d+×\d+\s+\d+(\.\d+)?\s+KB', '.', markdown_text) + markdown_text = re.sub(r'\d+×\d+\s+\d+(\.\d+)?\s+KB', '.', markdown_text) + # Изменение ссылок без описания markdown_text = re.sub(r'\[\]\((https:\/\/[^\)]+)\)', r'[.](\1)', markdown_text) markdown_text = re.sub(r'\[\s]\((https:\/\/[^\)]+)\)', r'[.](\1)', markdown_text) @@ -322,8 +332,8 @@ async def discord_post(post_text, client): await channel.send(f"{post_text}") -async def get_discord_messages(client, channel_id): - channel = client.get_channel(channel_id) +async def get_discord_messages(client_discord, channel_id): + channel = client_discord.get_channel(channel_id) if not channel: logging.error(f"ID канала Discord {channel_id} не существует") return [] @@ -344,31 +354,31 @@ async def get_discord_messages(client, channel_id): def check_discord_public(): intents = discord.Intents.default() intents.messages = True - client = discord.Client(intents=intents) + client_discord = discord.Client(intents=intents) - @client.event + @client_discord.event async def on_ready(): - logging.debug(f"Успешный логин в discord {client.user}") + logging.debug(f"Успешный логин в discord {client_discord.user}") channel_id = keys.dicord_channel - discord_messages = await get_discord_messages(client, channel_id) + discord_messages = await get_discord_messages(client_discord, channel_id) list_titles_and_ids = news() if list_titles_and_ids: list_for_public = [] for topic_id, topic_title in list_titles_and_ids: - if topic_title not in discord_messages and topic_id > 698: + if topic_title not in discord_messages and topic_id > keys.start_topic_id: list_for_public.append((topic_id, topic_title)) if not list_for_public: logging.info(f"Новостей для публикации в дискорд нет") - await client.close() + await client_discord.close() else: logging.info(f"Новости для публикации в дискорд: {list_for_public}") - channel = client.get_channel(channel_id) + channel = client_discord.get_channel(channel_id) if not channel: logging.error(f"ID канала Discord {channel_id} не существует") - await client.close() + await client_discord.close() return for topic_id, topic_title in reversed(list_for_public): @@ -378,9 +388,9 @@ def check_discord_public(): # Разбиваем содержимое на части по 4000 символов for i in range(0, len(content), 2000): await channel.send(content[i:i+2000]) - await client.close() + await client_discord.close() - client.run(keys.discord_token) + client_discord.run(keys.discord_token) def vk_post(url, post_text, links=None): @@ -460,7 +470,7 @@ def check_vk_posts(): logging.info(f"Новостей для публикации в ВК нет") else: for topic_id, topic_title in reversed(list_for_public): - if topic_id > 698: + if topic_id > keys.start_topic_id: logging.info(f"Новости для публикации в ВК: {list_for_public}") text_data = news_content(topic_id) if text_data: @@ -480,5 +490,66 @@ def check_vk_posts(): logging.info(f"Новостей для публикации в ВК нет") +def tg_post(post_text, client_tg): + # Отправка сообщения + client_tg.send_message(keys.channel_username_tg, post_text) + # Завершение сеанса + client_tg.disconnect() + + +async def get_tg_messages(client_tg, channel_username_tg): + messages = [] + async for message in client_tg.iter_messages(channel_username_tg, limit=999): + if message.text: # Проверка на NoneType + logging.debug(f"Найдены сообщения в Telegram канале {message.text}") + messages.append(message.text) + return messages + + +def check_tg_news(): + session_file = 'LG_news' + loop = asyncio.new_event_loop() # Создание нового цикла событий + asyncio.set_event_loop(loop) # Установка нового цикла событий + + client_tg = TelegramClient('LG_news', keys.api_id_tg, keys.api_hash_tg) + + @client_tg.on(events.NewMessage(chats=keys.channel_username_tg)) + async def handler(event): + logging.debug(f"Новое сообщение в Telegram: {event.message.message}") + + async def main_tg(): + await client_tg.start() + tg_messages = await get_tg_messages(client_tg, keys.channel_username_tg) + list_titles_and_ids = news() + if list_titles_and_ids: + list_for_public = [] + + for topic_id, topic_title in list_titles_and_ids: + if all(topic_title not in (msg or '') for msg in tg_messages) and topic_id > keys.start_topic_id: + list_for_public.append((topic_id, topic_title)) + + if not list_for_public: + logging.info(f"Новостей для публикации в Telegram нет") + await client_tg.disconnect() + else: + logging.info(f"Новости для публикации в Telegram: {list_for_public}") + for topic_id, topic_title in reversed(list_for_public): + text_data = news_content(topic_id) + if text_data: + content = f"----------------------------------------------------------\n### {topic_title}\t\n" + text_data + "\n" + while True: + try: + await client_tg.send_message(keys.channel_username_tg, content) + break # Сообщение отправлено успешно, выходим из цикла + except FloodWaitError as e: + logging.warning(f"Flood wait error: нужно подождать {e.seconds} секунд.") + await asyncio.sleep(e.seconds) # Ждем указанное время перед повторной попыткой + await client_tg.disconnect() + + loop = asyncio.get_event_loop() + loop.run_until_complete(main_tg()) + + + if __name__ == '__main__': main()