- Улучшенная проверка на дубли для telegram

This commit is contained in:
2025-08-20 15:33:53 +03:00
parent 47e1adfed7
commit 073fa11127
2 changed files with 176 additions and 23 deletions

102
README.md
View File

@@ -123,18 +123,54 @@ print("Discord:", bot.discord_client.is_enabled())
bot.start() bot.start()
``` ```
## 🛠 Утилиты ## ⚡ Быстрые команды
### Очистка каналов
```bash ```bash
python3 del-tests.py # Полная переустановка
rm -rf venv keys.py *.session
bash setup_venv.sh
cp keys_example.py keys.py
# Обновление зависимостей
source venv/bin/activate
pip install --upgrade -r requirements.txt
# Проверка всех модулей
python3 -c "from news_bot_modular import NewsBot; bot = NewsBot(); print('✓ Все модули загружены успешно')"
``` ```
Удаляет все сообщения из всех настроенных каналов.
### Проверка синтаксиса ## 🛠 Утилиты и скрипты
### Управление ботом
```bash ```bash
# Запуск бота через виртуальное окружение
bash run_bot.sh
# Прямой запуск (если venv активировано)
python3 news-bot-modular.py
# Системная установка как сервис
bash install-service.sh
# Управление сервисом
bash service-control.sh start # Запуск
bash service-control.sh stop # Остановка
bash service-control.sh restart # Перезапуск
bash service-control.sh status # Статус
```
### Обслуживание
```bash
# Очистка каналов от тестовых сообщений
python3 del-tests.py
# Переустановка виртуального окружения
rm -rf venv
bash setup_venv.sh
# Проверка синтаксиса
python3 -m py_compile news-bot-modular.py python3 -m py_compile news-bot-modular.py
python3 -m py_compile content_processor.py python3 -m py_compile telegram_client.py
``` ```
## 🐛 Отладка ## 🐛 Отладка
@@ -146,6 +182,60 @@ python3 -m py_compile content_processor.py
- 🔴 ERROR - ошибки - 🔴 ERROR - ошибки
- ⚪ CRITICAL - критические ошибки - ⚪ CRITICAL - критические ошибки
## 🚀 Развертывание на сервере
### Быстрое развертывание
```bash
# 1. Клонирование репозитория
git clone <repository-url>
cd bot-news-linux-gaming
# 2. Автоматическая установка
bash setup_venv.sh
# 3. Конфигурация
cp keys_example.py keys.py
nano keys.py # Заполнить все API ключи
# 4. Тестовый запуск
bash run_bot.sh
```
### Установка как системный сервис
```bash
# После настройки конфигурации
bash install-service.sh
# Управление сервисом
sudo systemctl start bot-news-linux-gaming
sudo systemctl enable bot-news-linux-gaming # Автозапуск
sudo systemctl status bot-news-linux-gaming # Проверка статуса
# Просмотр логов
journalctl -u bot-news-linux-gaming -f
```
### Требования к серверу
- **ОС**: Ubuntu 20.04+ / Debian 11+
- **Python**: 3.8+
- **Память**: минимум 512MB RAM
- **Диск**: 100MB свободного места
- **Сеть**: доступ к интернету для API запросов
## 🔧 Конфигурация топиков Telegram
Для публикации в конкретном топике супергруппы:
```python
# В файле keys.py добавить:
telegram_topic_id = 123456 # ID первого сообщения топика
```
ID топика можно получить:
1. Перейти в топик супергруппы
2. Скопировать ссылку на первое сообщение топика
3. ID будет в конце ссылки: `/c/channel_id/topic_id`
## 📝 Лицензия ## 📝 Лицензия
Этот проект является частной разработкой для автоматизации публикации новостей Linux Gaming. Этот проект является частной разработкой для автоматизации публикации новостей Linux Gaming.

View File

@@ -31,17 +31,7 @@ class TelegramNewsClient:
if message.text: if message.text:
# Если указан топик, фильтруем только сообщения из этого топика # Если указан топик, фильтруем только сообщения из этого топика
if self.config['topic_id']: if self.config['topic_id']:
is_in_topic = False is_in_topic = self._is_message_in_topic(message, self.config['topic_id'])
# В Telegram топиках все сообщения имеют reply_to_msg_id равный ID первого сообщения топика
if hasattr(message, 'reply_to_msg_id') and message.reply_to_msg_id:
# Сообщение принадлежит топику если его reply_to_msg_id равен topic_id
if message.reply_to_msg_id == self.config['topic_id']:
is_in_topic = True
# Также проверяем, если само сообщение является первым сообщением топика
elif message.id == self.config['topic_id']:
is_in_topic = True
self.logger.debug(f"Сообщение ID {message.id}, reply_to_msg_id={getattr(message, 'reply_to_msg_id', None)}, нужен топик {self.config['topic_id']}, в топике: {is_in_topic}") self.logger.debug(f"Сообщение ID {message.id}, reply_to_msg_id={getattr(message, 'reply_to_msg_id', None)}, нужен топик {self.config['topic_id']}, в топике: {is_in_topic}")
@@ -57,9 +47,82 @@ class TelegramNewsClient:
if title: if title:
titles.append(title) titles.append(title)
self.logger.debug(f"Извлечено {len(titles)} заголовков из Telegram сообщений") self.logger.info(f"Извлечено {len(titles)} заголовков из Telegram сообщений: {titles[:5]}{'...' if len(titles) > 5 else ''}")
return messages, titles return messages, titles
def _is_message_in_topic(self, message, topic_id):
"""Проверка принадлежности сообщения к топику с улучшенной логикой"""
# Само сообщение является первым сообщением топика
if message.id == topic_id:
return True
# Проверяем прямой ответ на сообщение топика
if hasattr(message, 'reply_to_msg_id') and message.reply_to_msg_id:
if message.reply_to_msg_id == topic_id:
return True
# Дополнительная проверка: может быть это ответ на ответ в топике
# В некоторых случаях Telegram API может показывать вложенную структуру
try:
# Получаем информацию о сообщении, на которое отвечали
reply_to = message.reply_to
if reply_to and hasattr(reply_to, 'reply_to_msg_id'):
# Рекурсивно проверяем цепочку ответов
current_reply_id = reply_to.reply_to_msg_id
depth = 0
while current_reply_id and depth < 10: # Ограничиваем глубину для безопасности
if current_reply_id == topic_id:
return True
# В реальном случае здесь нужно было бы получать сообщение по ID
# Но это может быть слишком дорого по API запросам
break
except Exception as e:
self.logger.debug(f"Ошибка при проверке вложенной структуры ответов: {e}")
return False
def _check_duplicate_news(self, topic_title, tg_titles, tg_messages):
"""Улучшенная проверка дубликатов новостей с детальным логированием"""
# Проверяем точное совпадение с заголовками
title_exists = topic_title in tg_titles
if title_exists:
self.logger.debug(f"Найдено точное совпадение заголовка: '{topic_title}'")
return True
# Проверяем частичные совпадения в заголовках (нормализованные)
normalized_topic = topic_title.lower().strip()
for existing_title in tg_titles:
normalized_existing = existing_title.lower().strip()
if normalized_topic == normalized_existing:
self.logger.debug(f"Найдено нормализованное совпадение заголовка: '{topic_title}' == '{existing_title}'")
return True
# Проверяем наличие заголовка как части структурированного сообщения
# Ищем точное совпадение в формате "### Заголовок\t"
formatted_title = f"### {topic_title}\t"
for message in tg_messages:
if message and formatted_title in message:
self.logger.debug(f"Найдено совпадение форматированного заголовка в сообщении: '{formatted_title}'")
return True
# Дополнительная проверка: ищем заголовок в начале сообщений
for message in tg_messages:
if message:
message_lines = message.strip().split('\n')
if message_lines:
first_line = message_lines[0].strip()
# Убираем префиксы и сравниваем
import re
cleaned_first_line = re.sub(r'^[#*\s\t]+', '', first_line)
cleaned_first_line = re.sub(r'\t.*$', '', cleaned_first_line).strip()
if cleaned_first_line.lower() == normalized_topic:
self.logger.debug(f"Найдено совпадение в первой строке сообщения: '{cleaned_first_line}' == '{topic_title}'")
return True
self.logger.debug(f"Новость '{topic_title}' не найдена среди {len(tg_titles)} существующих заголовков")
return False
def _extract_title_from_message(self, message_text): def _extract_title_from_message(self, message_text):
"""Извлечение заголовка из текста сообщения""" """Извлечение заголовка из текста сообщения"""
import re import re
@@ -180,14 +243,14 @@ class TelegramNewsClient:
# Фильтруем новости для публикации # Фильтруем новости для публикации
list_for_public = [] list_for_public = []
for topic_id, topic_title in news_list: for topic_id, topic_title in news_list:
# Проверяем по заголовкам и по полному тексту сообщений # Улучшенная проверка дубликатов
title_exists = any(topic_title == title for title in tg_titles) is_duplicate = self._check_duplicate_news(topic_title, tg_titles, tg_messages)
text_contains = any(topic_title in (msg or '') for msg in tg_messages)
if not title_exists and not text_contains: if not is_duplicate:
list_for_public.append((topic_id, topic_title)) list_for_public.append((topic_id, topic_title))
self.logger.debug(f"Новость '{topic_title}' добавлена в список для публикации")
else: else:
self.logger.debug(f"Новость '{topic_title}' уже есть в Telegram (title_exists={title_exists}, text_contains={text_contains})") self.logger.debug(f"Новость '{topic_title}' уже существует в Telegram, пропускаем")
if not list_for_public: if not list_for_public:
self.logger.warning("Новостей для публикации в Telegram нет") self.logger.warning("Новостей для публикации в Telegram нет")