[ВОЛК] правка логики реадктирования
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-01-10 17:02:58 +03:00
parent 2f0c2766aa
commit f9aa9dfb29
+170 -93
View File
@@ -193,6 +193,81 @@ def _send_photo_sync(send_photo_url, image_data, caption, chat_id, disable_notif
# Пробрасываем остальные исключения как есть # Пробрасываем остальные исключения как есть
raise raise
def _edit_caption_sync(edit_caption_url, chat_id, message_id, caption, timeout):
"""
Синхронная функция для редактирования caption через requests.
Вызывается из отдельного потока для избежания проблем с event loop в gunicorn.
Args:
edit_caption_url: URL для редактирования caption в Telegram API
chat_id: ID чата/канала
message_id: ID сообщения для редактирования
caption: Новый текст caption
timeout: Кортеж (connect_timeout, read_timeout) или число для общего таймаута
Returns:
requests.Response объект
"""
try:
data = {
'chat_id': chat_id,
'message_id': message_id,
'caption': caption,
'parse_mode': 'HTML'
}
response = requests.post(
edit_caption_url,
json=data,
timeout=timeout
)
return response
except requests.exceptions.Timeout as e:
raise
except requests.exceptions.RequestException as e:
raise
except Exception as e:
raise
def _edit_text_sync(edit_text_url, chat_id, message_id, text, timeout):
"""
Синхронная функция для редактирования текста через requests.
Вызывается из отдельного потока для избежания проблем с event loop в gunicorn.
Args:
edit_text_url: URL для редактирования текста в Telegram API
chat_id: ID чата/канала
message_id: ID сообщения для редактирования
text: Новый текст сообщения
timeout: Кортеж (connect_timeout, read_timeout) или число для общего таймаута
Returns:
requests.Response объект
"""
try:
data = {
'chat_id': chat_id,
'message_id': message_id,
'text': text,
'parse_mode': 'HTML',
'disable_web_page_preview': True
}
response = requests.post(
edit_text_url,
json=data,
timeout=timeout
)
return response
except requests.exceptions.Timeout as e:
raise
except requests.exceptions.RequestException as e:
raise
except Exception as e:
raise
async def publish_to_tg(vk_post_id): async def publish_to_tg(vk_post_id):
"""Публикация одной записи по VK post ID""" """Публикация одной записи по VK post ID"""
logger.info(f"Запуск публикации для записи VK ID {vk_post_id}") logger.info(f"Запуск публикации для записи VK ID {vk_post_id}")
@@ -552,32 +627,41 @@ async def publish_to_tg(vk_post_id):
# Используем message_has_image для определения типа сообщения # Используем message_has_image для определения типа сообщения
# Если было отправлено изображение, редактируем caption, иначе - текст # Если было отправлено изображение, редактируем caption, иначе - текст
if message_has_image: if message_has_image:
# Сообщение с изображением - редактируем caption # Сообщение с изображением - редактируем caption через requests
edit_caption_success = False
try: try:
edit_caption_payload = { # Используем requests через asyncio.to_thread() для стабильной работы в gunicorn
'chat_id': CHANNEL_ID, edit_timeout = (10.0, 30.0) # (connect, read)
'message_id': message_id,
'caption': final_text, edit_caption_response = await asyncio.to_thread(
'parse_mode': 'HTML' _edit_caption_sync,
} edit_caption_url,
try: CHANNEL_ID,
edit_caption_response = await httpx_client.post(edit_caption_url, json=edit_caption_payload) message_id,
except httpx.TimeoutException as e: final_text,
logger.error(f"ТАЙМАУТ при редактировании подписи для записи VK ID {vk_post_id}") edit_timeout
logger.error(f"Детали таймаута: {type(e).__name__}: {e}") )
if hasattr(e, 'request'):
logger.error(f"Запрос, вызвавший таймаут: {e.request.method} {e.request.url if hasattr(e.request, 'url') else 'N/A'}")
raise # Пробрасываем таймаут наверх
if edit_caption_response.status_code == 200: if edit_caption_response.status_code == 200:
edit_result = edit_caption_response.json() try:
if not edit_result.get('ok'): edit_result = edit_caption_response.json()
except Exception as json_error:
logger.error(f"Ошибка при парсинге JSON ответа при редактировании caption для записи VK ID {vk_post_id}: {json_error}")
logger.error(f"Текст ответа: {edit_caption_response.text[:500]}")
raise Exception(f"Ошибка парсинга JSON ответа: {json_error}")
if edit_result.get('ok'):
edit_caption_success = True
logger.info(f"Caption успешно отредактирован для записи VK ID {vk_post_id}")
else:
# Ошибка в ответе API
log_telegram_error(edit_caption_response, f"[Редактирование подписи для VK ID {vk_post_id}]") log_telegram_error(edit_caption_response, f"[Редактирование подписи для VK ID {vk_post_id}]")
error_code = edit_result.get('error_code') error_code = edit_result.get('error_code')
if error_code == 429: if error_code == 429:
retry_after = edit_result.get('parameters', {}).get('retry_after', 60) retry_after = edit_result.get('parameters', {}).get('retry_after', 60)
raise RetryAfterException(retry_after) raise RetryAfterException(retry_after)
raise Exception(f"API error: {edit_result.get('description', 'Unknown error')}") # Не критичная ошибка - сообщение уже опубликовано
logger.warning(f"Не удалось отредактировать caption для записи VK ID {vk_post_id}: {edit_result.get('description', 'Unknown error')}. Сообщение опубликовано, но без ссылок.")
else: else:
log_telegram_error(edit_caption_response, f"[Редактирование подписи для VK ID {vk_post_id}]") log_telegram_error(edit_caption_response, f"[Редактирование подписи для VK ID {vk_post_id}]")
if edit_caption_response.status_code == 429: if edit_caption_response.status_code == 429:
@@ -588,50 +672,64 @@ async def publish_to_tg(vk_post_id):
except: except:
pass pass
raise RetryAfterException(retry_after) raise RetryAfterException(retry_after)
raise Exception(f"HTTP {edit_caption_response.status_code}: {edit_caption_response.text}") # Не критичная ошибка - сообщение уже опубликовано
logger.warning(f"Не удалось отредактировать caption для записи VK ID {vk_post_id}: HTTP {edit_caption_response.status_code}. Сообщение опубликовано, но без ссылок.")
except RetryAfterException: except RetryAfterException:
raise raise
except httpx.TimeoutException as e:
logger.error(f"ТАЙМАУТ при редактировании подписи для записи VK ID {vk_post_id}")
logger.error(f"Тип исключения: {type(e).__name__}")
logger.error(f"Сообщение об ошибке: {str(e)}")
logger.error(f"Полная информация об исключении: {repr(e)}")
if hasattr(e, 'request'):
logger.error(f"Запрос, вызвавший таймаут: {e.request.method} {e.request.url if hasattr(e.request, 'url') else 'N/A'}")
logger.error(f"Трассировка стека:\n{traceback.format_exc()}")
raise # Пробрасываем таймаут наверх
except Exception as e: except Exception as e:
logger.warning(f"Не удалось отредактировать подпись для записи VK ID {vk_post_id}: {e}. Пробуем отредактировать текстовое сообщение.") # Проверяем, является ли это таймаутом
edit_text_payload = { is_timeout = (
'chat_id': CHANNEL_ID, isinstance(e, requests.exceptions.Timeout) or
'message_id': message_id, 'Timeout' in str(e) or
'text': final_text, 'timeout' in str(e).lower() or
'parse_mode': 'HTML', 'disconnected' in str(e).lower()
'disable_web_page_preview': True )
}
try: if is_timeout:
edit_text_response = await httpx_client.post(edit_text_url, json=edit_text_payload) logger.warning(f"Таймаут при редактировании caption для записи VK ID {vk_post_id}: {e}. Сообщение опубликовано, но без ссылок.")
except httpx.TimeoutException as timeout_e: else:
logger.error(f"ТАЙМАУТ при редактировании текста (fallback) для записи VK ID {vk_post_id}") logger.warning(f"Не удалось отредактировать caption для записи VK ID {vk_post_id}: {e}. Сообщение опубликовано, но без ссылок.")
logger.error(f"Тип исключения: {type(timeout_e).__name__}")
logger.error(f"Сообщение об ошибке: {str(timeout_e)}") # Не пробрасываем исключение - сообщение уже опубликовано, просто без ссылок
logger.error(f"Полная информация об исключении: {repr(timeout_e)}") # Это считается частичным успехом
if hasattr(timeout_e, 'request'): else:
logger.error(f"Запрос, вызвавший таймаут: {timeout_e.request.method} {timeout_e.request.url if hasattr(timeout_e.request, 'url') else 'N/A'}") # Текстовое сообщение - редактируем текст через requests
logger.error(f"Трассировка стека:\n{traceback.format_exc()}") edit_text_success = False
raise # Пробрасываем таймаут наверх try:
# Используем requests через asyncio.to_thread() для стабильной работы в gunicorn
edit_timeout = (10.0, 30.0) # (connect, read)
edit_text_response = await asyncio.to_thread(
_edit_text_sync,
edit_text_url,
CHANNEL_ID,
message_id,
final_text,
edit_timeout
)
if edit_text_response.status_code == 200: if edit_text_response.status_code == 200:
edit_result = edit_text_response.json() try:
if not edit_result.get('ok'): edit_result = edit_text_response.json()
log_telegram_error(edit_text_response, f"[Редактирование текста (fallback) для VK ID {vk_post_id}]") except Exception as json_error:
logger.error(f"Ошибка при парсинге JSON ответа при редактировании текста для записи VK ID {vk_post_id}: {json_error}")
logger.error(f"Текст ответа: {edit_text_response.text[:500]}")
raise Exception(f"Ошибка парсинга JSON ответа: {json_error}")
if edit_result.get('ok'):
edit_text_success = True
logger.info(f"Текст успешно отредактирован для записи VK ID {vk_post_id}")
else:
# Ошибка в ответе API
log_telegram_error(edit_text_response, f"[Редактирование текста для VK ID {vk_post_id}]")
error_code = edit_result.get('error_code') error_code = edit_result.get('error_code')
if error_code == 429: if error_code == 429:
retry_after = edit_result.get('parameters', {}).get('retry_after', 60) retry_after = edit_result.get('parameters', {}).get('retry_after', 60)
raise RetryAfterException(retry_after) raise RetryAfterException(retry_after)
raise Exception(f"API error: {edit_result.get('description', 'Unknown error')}") # Не критичная ошибка - сообщение уже опубликовано
logger.warning(f"Не удалось отредактировать текст для записи VK ID {vk_post_id}: {edit_result.get('description', 'Unknown error')}. Сообщение опубликовано, но без ссылок.")
else: else:
log_telegram_error(edit_text_response, f"[Редактирование текста (fallback) для VK ID {vk_post_id}]") log_telegram_error(edit_text_response, f"[Редактирование текста для VK ID {vk_post_id}]")
if edit_text_response.status_code == 429: if edit_text_response.status_code == 429:
retry_after = 60 retry_after = 60
try: try:
@@ -640,47 +738,26 @@ async def publish_to_tg(vk_post_id):
except: except:
pass pass
raise RetryAfterException(retry_after) raise RetryAfterException(retry_after)
raise Exception(f"HTTP {edit_text_response.status_code}: {edit_text_response.text}") # Не критичная ошибка - сообщение уже опубликовано
else: logger.warning(f"Не удалось отредактировать текст для записи VK ID {vk_post_id}: HTTP {edit_text_response.status_code}. Сообщение опубликовано, но без ссылок.")
edit_text_payload = { except RetryAfterException:
'chat_id': CHANNEL_ID, raise
'message_id': message_id, except Exception as e:
'text': final_text, # Проверяем, является ли это таймаутом
'parse_mode': 'HTML', is_timeout = (
'disable_web_page_preview': True isinstance(e, requests.exceptions.Timeout) or
} 'Timeout' in str(e) or
try: 'timeout' in str(e).lower() or
edit_text_response = await httpx_client.post(edit_text_url, json=edit_text_payload) 'disconnected' in str(e).lower()
except httpx.TimeoutException as e: )
logger.error(f"ТАЙМАУТ при редактировании текста для записи VK ID {vk_post_id}")
logger.error(f"Тип исключения: {type(e).__name__}") if is_timeout:
logger.error(f"Сообщение об ошибке: {str(e)}") logger.warning(f"Таймаут при редактировании текста для записи VK ID {vk_post_id}: {e}. Сообщение опубликовано, но без ссылок.")
logger.error(f"Полная информация об исключении: {repr(e)}") else:
if hasattr(e, 'request'): logger.warning(f"Не удалось отредактировать текст для записи VK ID {vk_post_id}: {e}. Сообщение опубликовано, но без ссылок.")
logger.error(f"Запрос, вызвавший таймаут: {e.request.method} {e.request.url if hasattr(e.request, 'url') else 'N/A'}")
logger.error(f"Трассировка стека:\n{traceback.format_exc()}") # Не пробрасываем исключение - сообщение уже опубликовано, просто без ссылок
raise # Пробрасываем таймаут наверх # Это считается частичным успехом
if edit_text_response.status_code == 200:
edit_result = edit_text_response.json()
if not edit_result.get('ok'):
log_telegram_error(edit_text_response, f"[Редактирование текста для VK ID {vk_post_id}]")
error_code = edit_result.get('error_code')
if error_code == 429:
retry_after = edit_result.get('parameters', {}).get('retry_after', 60)
raise RetryAfterException(retry_after)
raise Exception(f"API error: {edit_result.get('description', 'Unknown error')}")
else:
log_telegram_error(edit_text_response, f"[Редактирование текста для VK ID {vk_post_id}]")
if edit_text_response.status_code == 429:
retry_after = 60
try:
error_data = edit_text_response.json()
retry_after = error_data.get('parameters', {}).get('retry_after', 60)
except:
pass
raise RetryAfterException(retry_after)
raise Exception(f"HTTP {edit_text_response.status_code}: {edit_text_response.text}")
await asyncio.sleep(2) # задержка между обращениями к телеграм await asyncio.sleep(2) # задержка между обращениями к телеграм