This commit is contained in:
+170
-93
@@ -193,6 +193,81 @@ def _send_photo_sync(send_photo_url, image_data, caption, chat_id, disable_notif
|
||||
# Пробрасываем остальные исключения как есть
|
||||
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):
|
||||
"""Публикация одной записи по 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 для определения типа сообщения
|
||||
# Если было отправлено изображение, редактируем caption, иначе - текст
|
||||
if message_has_image:
|
||||
# Сообщение с изображением - редактируем caption
|
||||
# Сообщение с изображением - редактируем caption через requests
|
||||
edit_caption_success = False
|
||||
try:
|
||||
edit_caption_payload = {
|
||||
'chat_id': CHANNEL_ID,
|
||||
'message_id': message_id,
|
||||
'caption': final_text,
|
||||
'parse_mode': 'HTML'
|
||||
}
|
||||
try:
|
||||
edit_caption_response = await httpx_client.post(edit_caption_url, json=edit_caption_payload)
|
||||
except httpx.TimeoutException as e:
|
||||
logger.error(f"ТАЙМАУТ при редактировании подписи для записи VK ID {vk_post_id}")
|
||||
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 # Пробрасываем таймаут наверх
|
||||
# Используем requests через asyncio.to_thread() для стабильной работы в gunicorn
|
||||
edit_timeout = (10.0, 30.0) # (connect, read)
|
||||
|
||||
edit_caption_response = await asyncio.to_thread(
|
||||
_edit_caption_sync,
|
||||
edit_caption_url,
|
||||
CHANNEL_ID,
|
||||
message_id,
|
||||
final_text,
|
||||
edit_timeout
|
||||
)
|
||||
|
||||
if edit_caption_response.status_code == 200:
|
||||
edit_result = edit_caption_response.json()
|
||||
if not edit_result.get('ok'):
|
||||
try:
|
||||
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}]")
|
||||
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')}")
|
||||
# Не критичная ошибка - сообщение уже опубликовано
|
||||
logger.warning(f"Не удалось отредактировать caption для записи VK ID {vk_post_id}: {edit_result.get('description', 'Unknown error')}. Сообщение опубликовано, но без ссылок.")
|
||||
else:
|
||||
log_telegram_error(edit_caption_response, f"[Редактирование подписи для VK ID {vk_post_id}]")
|
||||
if edit_caption_response.status_code == 429:
|
||||
@@ -588,50 +672,64 @@ async def publish_to_tg(vk_post_id):
|
||||
except:
|
||||
pass
|
||||
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:
|
||||
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:
|
||||
logger.warning(f"Не удалось отредактировать подпись для записи VK ID {vk_post_id}: {e}. Пробуем отредактировать текстовое сообщение.")
|
||||
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 timeout_e:
|
||||
logger.error(f"ТАЙМАУТ при редактировании текста (fallback) для записи VK ID {vk_post_id}")
|
||||
logger.error(f"Тип исключения: {type(timeout_e).__name__}")
|
||||
logger.error(f"Сообщение об ошибке: {str(timeout_e)}")
|
||||
logger.error(f"Полная информация об исключении: {repr(timeout_e)}")
|
||||
if hasattr(timeout_e, 'request'):
|
||||
logger.error(f"Запрос, вызвавший таймаут: {timeout_e.request.method} {timeout_e.request.url if hasattr(timeout_e.request, 'url') else 'N/A'}")
|
||||
logger.error(f"Трассировка стека:\n{traceback.format_exc()}")
|
||||
raise # Пробрасываем таймаут наверх
|
||||
# Проверяем, является ли это таймаутом
|
||||
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"Таймаут при редактировании 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:
|
||||
# Используем 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:
|
||||
edit_result = edit_text_response.json()
|
||||
if not edit_result.get('ok'):
|
||||
log_telegram_error(edit_text_response, f"[Редактирование текста (fallback) для VK ID {vk_post_id}]")
|
||||
try:
|
||||
edit_result = edit_text_response.json()
|
||||
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')
|
||||
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')}")
|
||||
# Не критичная ошибка - сообщение уже опубликовано
|
||||
logger.warning(f"Не удалось отредактировать текст для записи VK ID {vk_post_id}: {edit_result.get('description', 'Unknown error')}. Сообщение опубликовано, но без ссылок.")
|
||||
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:
|
||||
retry_after = 60
|
||||
try:
|
||||
@@ -640,47 +738,26 @@ async def publish_to_tg(vk_post_id):
|
||||
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:
|
||||
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}")
|
||||
# Не критичная ошибка - сообщение уже опубликовано
|
||||
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) # задержка между обращениями к телеграм
|
||||
|
||||
|
||||
Reference in New Issue
Block a user