Files
bot-news-linux-gaming/vk_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

187 lines
8.9 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 sys
import time
import logging
import requests
from config import VK_CONFIG, URL_VK_POST, URL_VK_GET, PARAMS_VK_GET
class VKClient:
def __init__(self, content_processor):
self.logger = logging.getLogger(__name__)
self.content_processor = content_processor
self.config = VK_CONFIG
def get_wall_posts(self, max_retries=10, retry_delay=10):
"""Получение постов со стены VK с повторными попытками"""
self.logger.debug("Получаем посты со стены VK")
wall_posts = []
params = PARAMS_VK_GET.copy()
while True:
success = False
for attempt in range(max_retries):
try:
response = requests.get(URL_VK_GET, params=params)
wall_data_json = response.json()
if 'error' in wall_data_json:
error_code = wall_data_json['error']['error_code']
error_msg = wall_data_json['error']['error_msg']
self.logger.error(f"Ошибка VK API {error_code}: {error_msg}")
return wall_posts, [] # Возвращаем то что получили
items = wall_data_json.get('response', {}).get('items', [])
if not items:
self.logger.warning("Постов на стене нет")
return wall_posts, [] # Завершаем цикл
wall_posts.extend((post['text'] for post in items if 'text' in post))
if len(items) < 100:
# Получили все посты
success = True
break
params['offset'] = str(int(params['offset']) + 100)
success = True
break
except requests.RequestException as e:
self.logger.warning(f"Ошибка при получении постов VK (попытка {attempt + 1}/{max_retries}): {e}")
if attempt < max_retries - 1:
self.logger.info(f"Повторная попытка через {retry_delay} секунд...")
time.sleep(retry_delay)
if not success:
self.logger.error(f"Все {max_retries} попыток получения постов VK неудачны")
break
# Если получили меньше 100 постов на последнем запросе, завершаем цикл
if len(items) < 100:
break
# Извлечение заголовков из постов
wall_titles = []
pattern = re.compile(r'### (.*?)\t', re.MULTILINE)
for message in wall_posts:
# Ищем заголовки в формате "### Заголовок\t"
matches = pattern.findall(message)
wall_titles.extend(matches)
# Также ищем в первой строке поста
lines = message.strip().split('\n') if message else []
if lines:
first_line = lines[0].strip()
# Убираем префиксы и табы
first_line = re.sub(r'^[#*\s\t]+', '', first_line)
first_line = re.sub(r'\t.*$', '', first_line)
if first_line and first_line not in wall_titles:
wall_titles.append(first_line)
self.logger.info(f"Найдено {len(wall_posts)} постов и {len(wall_titles)} заголовков в VK")
self.logger.debug(f"Заголовки VK: {wall_titles[:5]}...") # Показываем первые 5 заголовков
return wall_posts, wall_titles
def post_message(self, content, attachments=None, max_retries=10, retry_delay=10):
"""Отправка сообщения в VK с повторными попытками"""
params_post = {
'access_token': self.config['api_key'],
'v': '5.199',
'owner_id': str(self.config['owner_id'])
}
# Форматирование контента для VK
formatted_content = self.content_processor.format_for_vk(content)
data = {
'message': formatted_content
}
if attachments:
params_post['attachments'] = attachments
for attempt in range(max_retries):
try:
response = requests.post(url=URL_VK_POST, params=params_post, data=data)
if response.status_code == 200:
self.logger.info("Сообщение успешно опубликовано в VK")
self.logger.debug(response.json())
return response
else:
self.logger.warning(f"Ошибка при публикации в VK: {response.status_code} - {response.reason} (попытка {attempt + 1}/{max_retries})")
except requests.RequestException as err:
self.logger.warning(f"Ошибка VK API (попытка {attempt + 1}/{max_retries}): {err}")
if attempt < max_retries - 1:
self.logger.info(f"Повторная попытка публикации через {retry_delay} секунд...")
time.sleep(retry_delay)
self.logger.error(f"Все {max_retries} попыток публикации в VK неудачны")
return None
def check_and_publish_news(self, news_list):
"""Проверка и публикация новостей в VK"""
self.logger.info("Начинаем проверку новостей для VK")
vk_posts, vk_titles = self.get_wall_posts()
if not vk_posts:
self.logger.warning("Постов на стене VK нет")
if not news_list:
self.logger.warning("Список новостей пуст")
return
# Фильтруем новости для публикации
list_for_public = []
for topic_id, topic_title in news_list:
# Проверяем по заголовкам и по полному тексту сообщений
title_exists = any(topic_title == title for title in vk_titles)
text_contains = any(topic_title in vk_post for vk_post in vk_posts)
if not title_exists and not text_contains:
list_for_public.append((topic_id, topic_title))
else:
self.logger.debug(f"Новость '{topic_title}' уже есть в VK (title_exists={title_exists}, text_contains={text_contains})")
if not list_for_public:
self.logger.warning("Новостей для публикации в VK нет")
return
self.logger.info(f"Новости для публикации в VK: {list_for_public}")
# Публикуем новости
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"{topic_title}\t\n" + text_data + "\n"
# Извлекаем ссылки для прикрепления
links = self.content_processor.extract_links(content)
# Специальная обработка для постов о скриптах
if "Кумулятивное обновление скриптов" in topic_title:
# Добавляем изображение для постов о скриптах
self.post_message(content, "photo-99238527_457244491")
else:
if links:
# Берем первую ссылку как прикрепление
self.post_message(content, links[0] if len(links) == 1 else None)
else:
self.post_message(content)
time.sleep(1.0) # Пауза между постами
else:
self.logger.warning(f"Не удалось получить содержимое новости {topic_id}")
def is_enabled(self):
"""Проверка, включен ли VK клиент"""
return True # VK всегда включен в этой версии