[ZILANT] отправлять картинки при посте событий как файл, а не как URL; делаем как при репосте ВК постов
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -20,4 +20,4 @@ alwaysApply: true
|
|||||||
|
|
||||||
5. Не поднимать версию за косметические правки без изменения поведения.
|
5. Не поднимать версию за косметические правки без изменения поведения.
|
||||||
|
|
||||||
Текущая базовая версия: **1.0.2**.
|
Текущая базовая версия: **1.0.3**.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Zilant / VOLK — Telegram poster и подписки
|
# Zilant / VOLK — Telegram poster и подписки
|
||||||
|
|
||||||
**Версия:** 1.0.2 ([`VERSION`](VERSION))
|
**Версия:** 1.0.3 ([`VERSION`](VERSION))
|
||||||
|
|
||||||
Автоматическая публикация постов и событий в Telegram-канал, веб-редактор, бот подписок с сезонными deep link.
|
Автоматическая публикация постов и событий в Telegram-канал, веб-редактор, бот подписок с сезонными deep link.
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,12 @@
|
|||||||
Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/).
|
Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/).
|
||||||
Версия проекта — в файле [`VERSION`](../VERSION) (SemVer).
|
Версия проекта — в файле [`VERSION`](../VERSION) (SemVer).
|
||||||
|
|
||||||
|
## [1.0.3] - 2026-08-30
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Публикация событий (`evtg_publish.py`): картинка скачивается на сервере и уходит в `sendPhoto` файлом, как у постов VK. Раньше в Bot API передавался URL (`http://reg.konvent.ru/...`), Telegram не мог его забрать (`failed to get HTTP URL content`) и пост уходил текстом.
|
||||||
|
|
||||||
## [1.0.2] - 2026-08-30
|
## [1.0.2] - 2026-08-30
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
+48
-5
@@ -6,6 +6,7 @@ import time
|
|||||||
import html
|
import html
|
||||||
import httpx
|
import httpx
|
||||||
from datetime import datetime, timezone, timedelta
|
from datetime import datetime, timezone, timedelta
|
||||||
|
from urllib.parse import urlparse, unquote
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from formatter import get_event_text
|
from formatter import get_event_text
|
||||||
from telegram_relay import bot_api_method_url
|
from telegram_relay import bot_api_method_url
|
||||||
@@ -92,6 +93,39 @@ def prepare_text(text):
|
|||||||
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def _filename_from_image_url(image_url):
|
||||||
|
path = unquote(urlparse(image_url).path)
|
||||||
|
name = os.path.basename(path) or "image.jpg"
|
||||||
|
if "." not in name:
|
||||||
|
name = f"{name}.jpg"
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
async def download_image(image_url, httpx_client):
|
||||||
|
"""Скачивает картинку самим сервером (как tg_publish), не отдаёт URL Telegram.
|
||||||
|
|
||||||
|
sendPhoto с photo=http://... Telegram качает сам и часто отвечает
|
||||||
|
«failed to get HTTP URL content» (http, недоступность с DC Telegram).
|
||||||
|
"""
|
||||||
|
logger.info(f"Скачивание изображения: {image_url}")
|
||||||
|
started = 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()
|
||||||
|
image_data = response.content
|
||||||
|
content_type = (response.headers.get("content-type") or "image/jpeg").split(";")[0].strip()
|
||||||
|
if not content_type.startswith("image/"):
|
||||||
|
content_type = "image/jpeg"
|
||||||
|
logger.info(
|
||||||
|
f"Изображение скачано за {time.time() - started:.2f} с, "
|
||||||
|
f"размер: {len(image_data)} байт, type={content_type}"
|
||||||
|
)
|
||||||
|
return image_data, content_type, _filename_from_image_url(image_url)
|
||||||
|
|
||||||
|
|
||||||
async def tg_post_event(httpx_client, event_data):
|
async def tg_post_event(httpx_client, event_data):
|
||||||
"""Публикация одного события"""
|
"""Публикация одного события"""
|
||||||
try:
|
try:
|
||||||
@@ -197,15 +231,24 @@ async def tg_post_event(httpx_client, event_data):
|
|||||||
|
|
||||||
if has_image:
|
if has_image:
|
||||||
try:
|
try:
|
||||||
logger.info(f"Попытка отправки фото для события {event_data['number']}: chat_id={CHANNEL_ID}, caption_length={len(initial_text)}, image_url_length={len(image_url)}")
|
logger.info(
|
||||||
photo_payload = {
|
f"Попытка отправки фото для события {event_data['number']}: "
|
||||||
|
f"chat_id={CHANNEL_ID}, caption_length={len(initial_text)}, "
|
||||||
|
f"image_url={image_url}"
|
||||||
|
)
|
||||||
|
image_data, content_type, filename = await download_image(image_url, httpx_client)
|
||||||
|
photo_data = {
|
||||||
'chat_id': CHANNEL_ID,
|
'chat_id': CHANNEL_ID,
|
||||||
'photo': image_url,
|
|
||||||
'caption': initial_text,
|
'caption': initial_text,
|
||||||
'parse_mode': 'HTML',
|
'parse_mode': 'HTML',
|
||||||
'disable_notification': PUBLISH_SILENTLY
|
'disable_notification': 'true' if PUBLISH_SILENTLY else 'false',
|
||||||
}
|
}
|
||||||
photo_response = await httpx_client.post(send_photo_url, json=photo_payload)
|
photo_files = {
|
||||||
|
'photo': (filename, image_data, content_type),
|
||||||
|
}
|
||||||
|
photo_response = await httpx_client.post(
|
||||||
|
send_photo_url, data=photo_data, files=photo_files
|
||||||
|
)
|
||||||
|
|
||||||
if photo_response.status_code == 200:
|
if photo_response.status_code == 200:
|
||||||
photo_result = photo_response.json()
|
photo_result = photo_response.json()
|
||||||
|
|||||||
Reference in New Issue
Block a user