[ВОЛК] переключение на отправку изображения файлом
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 re
import httpx import httpx
import traceback import traceback
import io
from datetime import datetime, timezone from datetime import datetime, timezone
from dotenv import load_dotenv from dotenv import load_dotenv
from formatter import get_event_text from formatter import get_event_text
@@ -105,6 +106,24 @@ def log_telegram_error(response, context=""):
except Exception as e: except Exception as e:
logger.error(f"{context} Ошибка при логировании информации об ошибке: {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): 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}")
@@ -151,6 +170,8 @@ async def publish_to_tg(vk_post_id):
# Переменная для отслеживания успешности публикации # Переменная для отслеживания успешности публикации
message_id = None message_id = None
publication_successful = False publication_successful = False
# Флаг, указывающий, было ли отправлено изображение (True) или текстовое сообщение (False)
message_has_image = False
try: try:
# Проверяем наличие организационного хештега # Проверяем наличие организационного хештега
@@ -212,44 +233,40 @@ async def publish_to_tg(vk_post_id):
logger.info(f"Длина URL изображения: {len(image_url)} символов") logger.info(f"Длина URL изображения: {len(image_url)} символов")
logger.info(f"Длина подписи: {len(text)} символов") logger.info(f"Длина подписи: {len(text)} символов")
photo_payload = { # Скачиваем изображение
'chat_id': CHANNEL_ID, try:
'photo': image_url, image_data = await download_image(image_url, httpx_client)
'caption': text, except Exception as download_error:
'parse_mode': 'HTML', logger.error(f"Ошибка при скачивании изображения для записи VK ID {vk_post_id}: {download_error}")
'disable_notification': PUBLISH_SILENTLY raise # Пробрасываем ошибку, чтобы перейти к fallback
}
# Логируем время начала запроса # Логируем время начала запроса
request_start_time = time.time() request_start_time = time.time()
logger.info(f"Начало запроса отправки фото в {datetime.now(timezone.utc).isoformat()}") logger.info(f"Начало запроса отправки фото в {datetime.now(timezone.utc).isoformat()}")
try: # Отправляем изображение как файл через multipart/form-data
photo_response = await httpx_client.post(send_photo_url, json=photo_payload) # Создаем BytesIO объект для файла
request_duration = time.time() - request_start_time image_file = io.BytesIO(image_data)
logger.info(f"Запрос отправки фото завершен за {request_duration:.2f} секунд") files = {
except httpx.TimeoutException as e: 'photo': ('image.jpg', image_file, 'image/jpeg')
request_duration = time.time() - request_start_time }
logger.error(f"ТАЙМАУТ при отправке фото для записи VK ID {vk_post_id}") data = {
logger.error(f"Время до таймаута: {request_duration:.2f} секунд") 'chat_id': CHANNEL_ID,
logger.error(f"Тип исключения: {type(e).__name__}") 'caption': text,
logger.error(f"Сообщение об ошибке: {str(e)}") 'parse_mode': 'HTML',
logger.error(f"Полная информация об исключении: {repr(e)}") 'disable_notification': str(PUBLISH_SILENTLY).lower()
logger.error(f"URL изображения: {image_url}") }
logger.error(f"Длина URL изображения: {len(image_url)} символов")
if hasattr(e, 'request'): photo_response = await httpx_client.post(send_photo_url, files=files, data=data)
logger.error(f"Запрос, вызвавший таймаут: {e.request.method} {e.request.url if hasattr(e.request, 'url') else 'N/A'}") request_duration = time.time() - request_start_time
# Пытаемся получить информацию о таймауте из исключения logger.info(f"Запрос отправки фото завершен за {request_duration:.2f} секунд")
if hasattr(e, 'timeout'):
logger.error(f"Настройки таймаута: {e.timeout}")
logger.error(f"Трассировка стека:\n{traceback.format_exc()}")
raise # Пробрасываем таймаут наверх
if photo_response.status_code == 200: if photo_response.status_code == 200:
photo_result = photo_response.json() photo_result = photo_response.json()
if photo_result.get('ok'): if photo_result.get('ok'):
message_data = photo_result.get('result', {}) message_data = photo_result.get('result', {})
message_id = message_data.get('message_id') message_id = message_data.get('message_id')
message_has_image = True # Успешно отправили изображение
else: else:
# Ошибка в ответе API - логируем полную информацию # Ошибка в ответе API - логируем полную информацию
log_telegram_error(photo_response, f"[Отправка фото для VK ID {vk_post_id}]") log_telegram_error(photo_response, f"[Отправка фото для VK ID {vk_post_id}]")
@@ -282,6 +299,7 @@ async def publish_to_tg(vk_post_id):
raise # Пробрасываем таймаут наверх raise # Пробрасываем таймаут наверх
except Exception as e: except Exception as e:
logger.warning(f"Не удалось отправить изображение для записи VK ID {vk_post_id}: {e}. Отправляем текстовое сообщение.") logger.warning(f"Не удалось отправить изображение для записи VK ID {vk_post_id}: {e}. Отправляем текстовое сообщение.")
message_has_image = False # Отправляем текстовое сообщение вместо изображения
text_payload = { text_payload = {
'chat_id': CHANNEL_ID, 'chat_id': CHANNEL_ID,
'text': text, '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}") raise Exception(f"HTTP {text_response.status_code}: {text_response.text}")
else: else:
# Если это организационное сообщение или нет изображения, отправляем текстовое сообщение # Если это организационное сообщение или нет изображения, отправляем текстовое сообщение
message_has_image = False # Отправляем текстовое сообщение
text_payload = { text_payload = {
'chat_id': CHANNEL_ID, 'chat_id': CHANNEL_ID,
'text': text, '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_caption_url = f"https://api.telegram.org/bot{BOT_TOKEN}/editMessageCaption"
edit_text_url = f"https://api.telegram.org/bot{BOT_TOKEN}/editMessageText" 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: try:
edit_caption_payload = { edit_caption_payload = {
'chat_id': CHANNEL_ID, 'chat_id': CHANNEL_ID,