Files
bot-news-linux-gaming/discord_client.py
Евгений (ХрамычЪ) Храмов 845f96209d Завершение модульной рефакторизации и исправления
Исправлены все основные проблемы:
- Исправлена логика фильтрации сообщений по топикам в Telegram
- Исправлен бесконечный цикл в VK клиенте get_wall_posts()
- Добавлена асинхронная поддержка для VK в главном файле
- Дедупликация работает корректно для всех платформ
- Добавлена полная документация в CLAUDE.md и README.md

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-11 15:11:39 +03:00

169 lines
7.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
import re
import time
import logging
import asyncio
from config import DISCORD_CONFIG
# Discord импорты - только если Discord включен
if DISCORD_CONFIG['enabled']:
try:
import discord
except ImportError:
logging.error("Discord.py не установлен, но Discord включен в конфигурации")
DISCORD_CONFIG['enabled'] = False
class DiscordClient:
def __init__(self, content_processor):
self.logger = logging.getLogger(__name__)
self.content_processor = content_processor
self.config = DISCORD_CONFIG
if not self.config['enabled']:
self.logger.info("Discord клиент отключен в конфигурации")
async def get_messages(self, client, channel_id):
"""Получение сообщений из Discord канала"""
if not self.is_enabled():
return [], []
self.logger.debug("Получаем сообщения из Discord канала")
channel = client.get_channel(channel_id)
if not channel:
self.logger.error(f"ID канала Discord {channel_id} не существует")
return [], []
messages = []
titles = []
async for message in channel.history(limit=100):
self.logger.debug(f"Сообщение Discord: {message.content}")
messages.append(message.content)
# Извлекаем заголовок из сообщения
title = self._extract_title_from_message(message.content)
if title:
titles.append(title)
self.logger.debug(f"Найдено {len(messages)} сообщений и {len(titles)} заголовков в Discord")
return messages, titles
def _extract_title_from_message(self, message_text):
"""Извлечение заголовка из текста сообщения"""
if not message_text:
return None
# Ищем заголовки в формате "### Заголовок\t"
pattern = re.compile(r'^### (.*?)\t', re.MULTILINE)
match = pattern.search(message_text)
if match:
return match.group(1).strip()
# Ищем заголовки в формате "------ ### Заголовок\t"
pattern2 = re.compile(r'--+\n### (.*?)\t', re.MULTILINE)
match2 = pattern2.search(message_text)
if match2:
return match2.group(1).strip()
# Если не найдено, ищем в первой строке
lines = message_text.strip().split('\n')
if lines:
first_line = lines[0].strip()
# Убираем префиксы типа "###", "**", и табы
first_line = re.sub(r'^[#*\s\t-]+', '', first_line)
first_line = re.sub(r'\t.*$', '', first_line)
return first_line.strip() if first_line else None
return None
async def send_message(self, channel, content):
"""Отправка сообщения в Discord канал"""
if not self.is_enabled():
return
try:
# Разбиваем содержимое на части по 2000 символов (лимит Discord)
for i in range(0, len(content), 2000):
await channel.send(content[i:i+2000])
self.logger.info("Сообщение успешно отправлено в Discord")
except Exception as e:
self.logger.error(f"Ошибка отправки сообщения в Discord: {e}")
async def check_and_publish_news(self, news_list):
"""Проверка и публикация новостей в Discord"""
if not self.is_enabled():
self.logger.info("Discord отключен, пропускаем публикацию")
return
self.logger.info("Начинаем проверку новостей для Discord")
intents = discord.Intents.default()
intents.messages = True
client = discord.Client(intents=intents)
@client.event
async def on_ready():
self.logger.debug(f"Успешный логин в Discord: {client.user}")
channel_id = self.config['channel_id']
# Получаем существующие сообщения и заголовки
discord_messages, discord_titles = await self.get_messages(client, channel_id)
if not news_list:
self.logger.warning("Список новостей пуст")
await client.close()
return
# Фильтруем новости для публикации
list_for_public = []
for topic_id, topic_title in news_list:
# Проверяем по заголовкам и по полному тексту сообщений
title_exists = any(topic_title == title for title in discord_titles)
text_contains = any(topic_title in (msg or '') for msg in discord_messages)
if not title_exists and not text_contains:
list_for_public.append((topic_id, topic_title))
else:
self.logger.debug(f"Новость '{topic_title}' уже есть в Discord (title_exists={title_exists}, text_contains={text_contains})")
if not list_for_public:
self.logger.warning("Новостей для публикации в Discord нет")
await client.close()
return
self.logger.info(f"Новости для публикации в Discord: {list_for_public}")
channel = client.get_channel(channel_id)
if not channel:
self.logger.error(f"ID канала Discord {channel_id} не существует")
await client.close()
return
# Публикуем новости
for topic_id, topic_title in reversed(list_for_public):
from site_api import SiteAPI
site_api = SiteAPI()
text_data = site_api.get_news_content(topic_id, self.content_processor)
if text_data:
content = f"----------------------------------------------------------\n### {topic_title}\t\n" + text_data + "\n@here"
await self.send_message(channel, content)
time.sleep(1.0)
else:
self.logger.warning(f"Не удалось получить содержимое новости {topic_id}")
await client.close()
try:
await client.start(self.config['token'])
except Exception as e:
self.logger.error(f"Ошибка запуска Discord клиента: {e}")
def is_enabled(self):
"""Проверка, включен ли Discord клиент"""
return (self.config['enabled'] and
self.config['token'] and
self.config['channel_id'])