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

This commit is contained in:
2026-01-10 17:02:58 +03:00
parent 2f0c2766aa
commit f9aa9dfb29
+164 -87
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:
try:
edit_result = edit_caption_response.json() edit_result = edit_caption_response.json()
if not edit_result.get('ok'): 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,88 +672,62 @@ 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 )
}
if is_timeout:
logger.warning(f"Таймаут при редактировании caption для записи VK ID {vk_post_id}: {e}. Сообщение опубликовано, но без ссылок.")
else:
logger.warning(f"Не удалось отредактировать caption для записи VK ID {vk_post_id}: {e}. Сообщение опубликовано, но без ссылок.")
# Не пробрасываем исключение - сообщение уже опубликовано, просто без ссылок
# Это считается частичным успехом
else:
# Текстовое сообщение - редактируем текст через requests
edit_text_success = False
try: try:
edit_text_response = await httpx_client.post(edit_text_url, json=edit_text_payload) # Используем requests через asyncio.to_thread() для стабильной работы в gunicorn
except httpx.TimeoutException as timeout_e: edit_timeout = (10.0, 30.0) # (connect, read)
logger.error(f"ТАЙМАУТ при редактировании текста (fallback) для записи VK ID {vk_post_id}")
logger.error(f"Тип исключения: {type(timeout_e).__name__}") edit_text_response = await asyncio.to_thread(
logger.error(f"Сообщение об ошибке: {str(timeout_e)}") _edit_text_sync,
logger.error(f"Полная информация об исключении: {repr(timeout_e)}") edit_text_url,
if hasattr(timeout_e, 'request'): CHANNEL_ID,
logger.error(f"Запрос, вызвавший таймаут: {timeout_e.request.method} {timeout_e.request.url if hasattr(timeout_e.request, 'url') else 'N/A'}") message_id,
logger.error(f"Трассировка стека:\n{traceback.format_exc()}") final_text,
raise # Пробрасываем таймаут наверх edit_timeout
)
if edit_text_response.status_code == 200: if edit_text_response.status_code == 200:
try:
edit_result = edit_text_response.json() edit_result = edit_text_response.json()
if not edit_result.get('ok'): except Exception as json_error:
log_telegram_error(edit_text_response, f"[Редактирование текста (fallback) для VK ID {vk_post_id}]") logger.error(f"Ошибка при парсинге JSON ответа при редактировании текста для записи VK ID {vk_post_id}: {json_error}")
error_code = edit_result.get('error_code') logger.error(f"Текст ответа: {edit_text_response.text[:500]}")
if error_code == 429: raise Exception(f"Ошибка парсинга JSON ответа: {json_error}")
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"[Редактирование текста (fallback) для 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}")
else:
edit_text_payload = {
'chat_id': CHANNEL_ID,
'message_id': message_id,
'text': final_text,
'parse_mode': 'HTML',
'disable_web_page_preview': True
}
try:
edit_text_response = await httpx_client.post(edit_text_url, json=edit_text_payload)
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 # Пробрасываем таймаут наверх
if edit_text_response.status_code == 200: if edit_result.get('ok'):
edit_result = edit_text_response.json() edit_text_success = True
if not edit_result.get('ok'): logger.info(f"Текст успешно отредактирован для записи VK ID {vk_post_id}")
else:
# Ошибка в ответе API
log_telegram_error(edit_text_response, f"[Редактирование текста для VK ID {vk_post_id}]") 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"[Редактирование текста для 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:
@@ -680,7 +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}") # Не критичная ошибка - сообщение уже опубликовано
logger.warning(f"Не удалось отредактировать текст для записи VK ID {vk_post_id}: HTTP {edit_text_response.status_code}. Сообщение опубликовано, но без ссылок.")
except RetryAfterException:
raise
except Exception as e:
# Проверяем, является ли это таймаутом
is_timeout = (
isinstance(e, requests.exceptions.Timeout) or
'Timeout' in str(e) or
'timeout' in str(e).lower() or
'disconnected' in str(e).lower()
)
if is_timeout:
logger.warning(f"Таймаут при редактировании текста для записи VK ID {vk_post_id}: {e}. Сообщение опубликовано, но без ссылок.")
else:
logger.warning(f"Не удалось отредактировать текст для записи VK ID {vk_post_id}: {e}. Сообщение опубликовано, но без ссылок.")
# Не пробрасываем исключение - сообщение уже опубликовано, просто без ссылок
# Это считается частичным успехом
await asyncio.sleep(2) # задержка между обращениями к телеграм await asyncio.sleep(2) # задержка между обращениями к телеграм