- Улучшенная проверка на дубли для telegram
This commit is contained in:
102
README.md
102
README.md
@@ -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.
|
@@ -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 нет")
|
||||||
|
Reference in New Issue
Block a user