[ВОЛК] переключение на отправку изображения файлом
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-01-10 15:26:10 +03:00
parent 8556b1f508
commit 5b2d09f4be
+50 -28
View File
@@ -8,6 +8,7 @@ import json
import re
import httpx
import traceback
import io
from datetime import datetime, timezone
from dotenv import load_dotenv
from formatter import get_event_text
@@ -105,6 +106,24 @@ def log_telegram_error(response, context=""):
except Exception as e:
logger.error(f"{context} Ошибка при логировании информации об ошибке: {e}")
async def download_image(image_url, httpx_client):
"""Скачивает изображение по URL и возвращает его содержимое в виде bytes"""
try:
logger.info(f"Скачивание изображения: {image_url}")
download_start_time = time.time()
response = await httpx_client.get(image_url, timeout=httpx.Timeout(connect=10.0, read=60.0, write=30.0, pool=10.0))
response.raise_for_status()
download_duration = time.time() - download_start_time
image_data = response.content
logger.info(f"Изображение скачано за {download_duration:.2f} секунд, размер: {len(image_data)} байт")
return image_data
except Exception as e:
logger.error(f"Ошибка при скачивании изображения {image_url}: {e}")
raise
async def publish_to_tg(vk_post_id):
"""Публикация одной записи по VK post ID"""
logger.info(f"Запуск публикации для записи VK ID {vk_post_id}")
@@ -151,6 +170,8 @@ async def publish_to_tg(vk_post_id):
# Переменная для отслеживания успешности публикации
message_id = None
publication_successful = False
# Флаг, указывающий, было ли отправлено изображение (True) или текстовое сообщение (False)
message_has_image = False
try:
# Проверяем наличие организационного хештега
@@ -212,44 +233,40 @@ async def publish_to_tg(vk_post_id):
logger.info(f"Длина URL изображения: {len(image_url)} символов")
logger.info(f"Длина подписи: {len(text)} символов")
photo_payload = {
'chat_id': CHANNEL_ID,
'photo': image_url,
'caption': text,
'parse_mode': 'HTML',
'disable_notification': PUBLISH_SILENTLY
}
# Скачиваем изображение
try:
image_data = await download_image(image_url, httpx_client)
except Exception as download_error:
logger.error(f"Ошибка при скачивании изображения для записи VK ID {vk_post_id}: {download_error}")
raise # Пробрасываем ошибку, чтобы перейти к fallback
# Логируем время начала запроса
request_start_time = time.time()
logger.info(f"Начало запроса отправки фото в {datetime.now(timezone.utc).isoformat()}")
try:
photo_response = await httpx_client.post(send_photo_url, json=photo_payload)
request_duration = time.time() - request_start_time
logger.info(f"Запрос отправки фото завершен за {request_duration:.2f} секунд")
except httpx.TimeoutException as e:
request_duration = time.time() - request_start_time
logger.error(f"ТАЙМАУТ при отправке фото для записи VK ID {vk_post_id}")
logger.error(f"Время до таймаута: {request_duration:.2f} секунд")
logger.error(f"Тип исключения: {type(e).__name__}")
logger.error(f"Сообщение об ошибке: {str(e)}")
logger.error(f"Полная информация об исключении: {repr(e)}")
logger.error(f"URL изображения: {image_url}")
logger.error(f"Длина URL изображения: {len(image_url)} символов")
if hasattr(e, 'request'):
logger.error(f"Запрос, вызвавший таймаут: {e.request.method} {e.request.url if hasattr(e.request, 'url') else 'N/A'}")
# Пытаемся получить информацию о таймауте из исключения
if hasattr(e, 'timeout'):
logger.error(f"Настройки таймаута: {e.timeout}")
logger.error(f"Трассировка стека:\n{traceback.format_exc()}")
raise # Пробрасываем таймаут наверх
# Отправляем изображение как файл через multipart/form-data
# Создаем BytesIO объект для файла
image_file = io.BytesIO(image_data)
files = {
'photo': ('image.jpg', image_file, 'image/jpeg')
}
data = {
'chat_id': CHANNEL_ID,
'caption': text,
'parse_mode': 'HTML',
'disable_notification': str(PUBLISH_SILENTLY).lower()
}
photo_response = await httpx_client.post(send_photo_url, files=files, data=data)
request_duration = time.time() - request_start_time
logger.info(f"Запрос отправки фото завершен за {request_duration:.2f} секунд")
if photo_response.status_code == 200:
photo_result = photo_response.json()
if photo_result.get('ok'):
message_data = photo_result.get('result', {})
message_id = message_data.get('message_id')
message_has_image = True # Успешно отправили изображение
else:
# Ошибка в ответе API - логируем полную информацию
log_telegram_error(photo_response, f"[Отправка фото для VK ID {vk_post_id}]")
@@ -282,6 +299,7 @@ async def publish_to_tg(vk_post_id):
raise # Пробрасываем таймаут наверх
except Exception as e:
logger.warning(f"Не удалось отправить изображение для записи VK ID {vk_post_id}: {e}. Отправляем текстовое сообщение.")
message_has_image = False # Отправляем текстовое сообщение вместо изображения
text_payload = {
'chat_id': CHANNEL_ID,
'text': text,
@@ -326,6 +344,7 @@ async def publish_to_tg(vk_post_id):
raise Exception(f"HTTP {text_response.status_code}: {text_response.text}")
else:
# Если это организационное сообщение или нет изображения, отправляем текстовое сообщение
message_has_image = False # Отправляем текстовое сообщение
text_payload = {
'chat_id': CHANNEL_ID,
'text': text,
@@ -388,7 +407,10 @@ async def publish_to_tg(vk_post_id):
edit_caption_url = f"https://api.telegram.org/bot{BOT_TOKEN}/editMessageCaption"
edit_text_url = f"https://api.telegram.org/bot{BOT_TOKEN}/editMessageText"
if has_image and not is_org_message:
# Используем message_has_image для определения типа сообщения
# Если было отправлено изображение, редактируем caption, иначе - текст
if message_has_image:
# Сообщение с изображением - редактируем caption
try:
edit_caption_payload = {
'chat_id': CHANNEL_ID,