#!/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 всегда включен в этой версии