Завершение модульной рефакторизации и исправления
Исправлены все основные проблемы: - Исправлена логика фильтрации сообщений по топикам в 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>
This commit is contained in:
198
site_api.py
Normal file
198
site_api.py
Normal file
@@ -0,0 +1,198 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import re
|
||||
import time
|
||||
import logging
|
||||
import requests
|
||||
|
||||
from config import URL_POST, URL_NEWS, URL_CHANGELOG, HEADERS_SITE, SITE_CONFIG
|
||||
|
||||
|
||||
class SiteAPI:
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.config = SITE_CONFIG
|
||||
|
||||
def make_request(self, url, headers=None, params=None, max_retries=10, retry_delay=10):
|
||||
"""Универсальный метод для HTTP запросов с повторными попытками"""
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
if params:
|
||||
response = requests.get(url, params=params)
|
||||
elif headers:
|
||||
response = requests.get(url, headers=headers)
|
||||
else:
|
||||
response = requests.get(url)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response
|
||||
else:
|
||||
self.logger.warning(f"HTTP {response.status_code} для {url}, попытка {attempt + 1}/{max_retries}")
|
||||
|
||||
except requests.RequestException as err:
|
||||
self.logger.warning(f"Ошибка HTTP запроса к {url} (попытка {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} попыток запроса к {url} неудачны")
|
||||
return None
|
||||
|
||||
def get_changelog(self):
|
||||
"""Получение changelog с GitLab"""
|
||||
self.logger.debug("Получаем changelog")
|
||||
response = self.make_request(URL_CHANGELOG, HEADERS_SITE)
|
||||
|
||||
if response and response.status_code == 200:
|
||||
matches = re.findall(r'###Scripts version (\d+)### / stable', response.text)
|
||||
self.logger.debug(f"Найдены версии в истории изменений: {matches}")
|
||||
|
||||
if matches:
|
||||
last_version = int(max(matches))
|
||||
self.logger.info(f"Последняя стабильная версия в changelog: {last_version}")
|
||||
return matches, last_version, response
|
||||
|
||||
self.logger.error(f'Ошибка при запросе changelog: {response.status_code if response else "No Response"}')
|
||||
return None, None, None
|
||||
|
||||
def get_news(self):
|
||||
"""Получение списка новостей с сайта"""
|
||||
self.logger.debug("Получаем список новостей")
|
||||
response = self.make_request(URL_NEWS, HEADERS_SITE)
|
||||
|
||||
if response and response.status_code == 200:
|
||||
data = response.json()
|
||||
topics = data['topic_list']['topics']
|
||||
|
||||
# Фильтруем новости, исключая описание категории
|
||||
filtered_topics = [
|
||||
(topic['id'], str(topic['title']))
|
||||
for topic in topics
|
||||
if topic['title'] != 'Описание категории «Новости»'
|
||||
]
|
||||
|
||||
# Фильтруем по ID темы
|
||||
filtered_topics = [
|
||||
(topic_id, title)
|
||||
for topic_id, title in filtered_topics
|
||||
if topic_id >= self.config['start_topic_id']
|
||||
]
|
||||
|
||||
self.logger.debug(f"Получено {len(filtered_topics)} новостей")
|
||||
return filtered_topics
|
||||
else:
|
||||
self.logger.error(f"Ошибка при запросе новостей: {response.status_code if response else 'Нет доступа к сайту'}")
|
||||
return []
|
||||
|
||||
def get_news_content(self, post_id, content_processor):
|
||||
"""Получение содержимого конкретной новости"""
|
||||
self.logger.debug(f"Получаем содержимое поста с ID: {post_id}")
|
||||
|
||||
url = f"https://linux-gaming.ru/t/{post_id}.json"
|
||||
response = self.make_request(url, HEADERS_SITE)
|
||||
|
||||
if response and response.status_code == 200:
|
||||
topic_data = response.json()
|
||||
posts = topic_data.get('post_stream', {}).get('posts', [])
|
||||
|
||||
# Ищем первый пост
|
||||
for post in posts:
|
||||
if post.get('post_number') == 1:
|
||||
html_content = post.get('cooked', 'Нет содержимого')
|
||||
text_data = content_processor.html_to_text(html_content)
|
||||
return text_data
|
||||
|
||||
self.logger.error(f"Первый пост не найден в теме с ID: {post_id}")
|
||||
return None
|
||||
else:
|
||||
self.logger.error(f"Не удалось получить содержимое поста с ID: {post_id}")
|
||||
return None
|
||||
|
||||
def post_to_site(self, post_data):
|
||||
"""Публикация новости на сайт"""
|
||||
title = post_data.get('title', 'Без названия')
|
||||
|
||||
while True:
|
||||
try:
|
||||
response = requests.post(url=URL_POST, headers=HEADERS_SITE, json=post_data)
|
||||
|
||||
if response.status_code == 200:
|
||||
self.logger.info("Новость опубликована на сайте!")
|
||||
return response
|
||||
elif response.status_code == 422:
|
||||
self.logger.warning(f'Новость "{title}" уже опубликована: {response.status_code}')
|
||||
return response
|
||||
else:
|
||||
self.logger.error(f'Ошибка при отправке новости "{title}" на сайт: {response.status_code}')
|
||||
except requests.RequestException as error:
|
||||
self.logger.error(f'Ошибка при отправке новости "{title}" на сайт: {error}')
|
||||
|
||||
time.sleep(900) # Ждем 15 минут перед повторной попыткой
|
||||
|
||||
def create_script_update_post(self, script_ver, next_version, changelog_response, content_processor):
|
||||
"""Создание поста для обновления скрипта"""
|
||||
self.logger.debug(f"Создаем пост для обновления скрипта версии {script_ver}")
|
||||
|
||||
post_text = content_processor.create_script_content(script_ver, next_version, changelog_response)
|
||||
|
||||
if post_text:
|
||||
site_text = f"[center][img]/uploads/default/original/1X/5cfa59077a5275971401fab0114e56f3ffdd0ec4.png[/img][/center]\n{post_text}"
|
||||
|
||||
post_data = {
|
||||
"title": f"Кумулятивное обновление скриптов {script_ver}",
|
||||
"raw": site_text,
|
||||
"category": self.config['category_num']
|
||||
}
|
||||
|
||||
self.logger.debug(f"Данные поста: {post_data}")
|
||||
return post_text, post_data
|
||||
|
||||
return None, None
|
||||
|
||||
def check_script_versions(self, content_processor):
|
||||
"""Проверка и публикация новых версий скриптов"""
|
||||
self.logger.info("Проверяем новые версии скриптов")
|
||||
|
||||
# Получаем changelog
|
||||
matches_changelog, last_changelog, resp_changelog = self.get_changelog()
|
||||
if not matches_changelog or not last_changelog:
|
||||
return
|
||||
|
||||
# Получаем существующие новости
|
||||
news_list = self.get_news()
|
||||
|
||||
# Извлекаем номера версий из заголовков новостей
|
||||
pattern = re.compile(r'Кумулятивное обновление скриптов (\d+)')
|
||||
|
||||
def extract_number(title):
|
||||
match = pattern.search(title)
|
||||
return int(match.group(1)) if match else None
|
||||
|
||||
numbers = [extract_number(title) for _, title in news_list if extract_number(title) is not None]
|
||||
|
||||
if numbers:
|
||||
last_topics_script = max(numbers)
|
||||
self.logger.info(f"Последняя новость на сайте о версии: {last_topics_script}")
|
||||
|
||||
if last_topics_script >= last_changelog:
|
||||
self.logger.warning("Нет новых версий скриптов PortProton")
|
||||
return
|
||||
else:
|
||||
self.logger.warning("На сайте нет новостей о скриптах")
|
||||
|
||||
# Публикуем новые версии
|
||||
self._publish_new_script_versions(matches_changelog, resp_changelog, content_processor)
|
||||
|
||||
def _publish_new_script_versions(self, matches_changelog, resp_changelog, content_processor):
|
||||
"""Публикация новых версий скриптов"""
|
||||
for script_ver, next_version in zip(reversed(matches_changelog[:-1]), reversed(matches_changelog[1:])):
|
||||
self.logger.info(f"Найдена новая версия скрипта {script_ver}")
|
||||
|
||||
post_text, post_data = self.create_script_update_post(
|
||||
script_ver, next_version, resp_changelog, content_processor
|
||||
)
|
||||
|
||||
if post_data:
|
||||
self.logger.debug(f"Публикуем {post_data}")
|
||||
self.post_to_site(post_data)
|
Reference in New Issue
Block a user