📄📷 fix: Отправка фото + документа отдельными сообщениями

- Telegram не поддерживает фото+документ в одном сообщении
- Теперь отправляются 2 сообщения: документ + фото
- Исправлен UI — фото не очищается при выборе документа
- Исправлена сериализация chat_id (int → str)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Admin 2026-04-13 22:50:40 +04:00
parent 54e8cea522
commit 27729ab512
4 changed files with 32 additions and 19 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

View file

@ -358,8 +358,14 @@ class Scheduler:
# HTTP прокси # HTTP прокси
connector = aiohttp.TCPConnector() connector = aiohttp.TCPConnector()
# Отправляем пост в тему форума
message_id = None
# Telegram не поддерживает фото + документ в одном сообщении
# Поэтому отправляем их отдельными сообщениями
# 1. Сначала документ (если есть) — он главный
if post.document_file_id: if post.document_file_id:
# С документом - отправляем по file_id
url = f"{base_url}/sendDocument" url = f"{base_url}/sendDocument"
params = { params = {
'chat_id': ADMIN_CHAT_ID, 'chat_id': ADMIN_CHAT_ID,
@ -378,16 +384,16 @@ class Scheduler:
else: else:
error_text = await resp.text() error_text = await resp.text()
raise Exception(f"Telegram API {resp.status}: {error_text}") raise Exception(f"Telegram API {resp.status}: {error_text}")
elif post.photo_file_id:
# С фото - отправляем по file_id # 2. Потом фото (если есть) — отдельным сообщением
if post.photo_file_id:
url = f"{base_url}/sendPhoto" url = f"{base_url}/sendPhoto"
params = { params = {
'chat_id': ADMIN_CHAT_ID, 'chat_id': ADMIN_CHAT_ID,
'photo': post.photo_file_id, 'photo': post.photo_file_id,
'caption': post.text, 'caption': '📷 Приложение к посту' if post.document_file_id else post.text,
'parse_mode': 'HTML' 'parse_mode': 'HTML'
} }
# Не добавляем message_thread_id для general (1) — это общий чат
if post.topic_id and post.topic_id > 1: if post.topic_id and post.topic_id > 1:
params['message_thread_id'] = post.topic_id params['message_thread_id'] = post.topic_id
@ -395,19 +401,21 @@ class Scheduler:
async with http_session.post(url, json=params, timeout=30) as resp: async with http_session.post(url, json=params, timeout=30) as resp:
if resp.status == 200: if resp.status == 200:
result_data = await resp.json() result_data = await resp.json()
message_id = result_data['result']['message_id'] # message_id остаётся от документа или берём от фото
if not message_id:
message_id = result_data['result']['message_id']
else: else:
error_text = await resp.text() error_text = await resp.text()
raise Exception(f"Telegram API {resp.status}: {error_text}") logger.warning(f"Не удалось отправить фото: {error_text}")
else:
# Только текст # 3. Если ни документа ни фото — только текст
if not post.document_file_id and not post.photo_file_id:
url = f"{base_url}/sendMessage" url = f"{base_url}/sendMessage"
params = { params = {
'chat_id': ADMIN_CHAT_ID, 'chat_id': ADMIN_CHAT_ID,
'text': post.text, 'text': post.text,
'parse_mode': 'HTML' 'parse_mode': 'HTML'
} }
# Не добавляем message_thread_id для general (1) — это общий чат
if post.topic_id and post.topic_id > 1: if post.topic_id and post.topic_id > 1:
params['message_thread_id'] = post.topic_id params['message_thread_id'] = post.topic_id
@ -428,6 +436,7 @@ class Scheduler:
for user in users: for user in users:
try: try:
# Документ + фото отдельными сообщениями
if post.document_file_id: if post.document_file_id:
url = f"{base_url}/sendDocument" url = f"{base_url}/sendDocument"
params = { params = {

View file

@ -788,11 +788,17 @@ async def api_create_scheduled_post(
# Парсим время # Парсим время
scheduled_dt = datetime.fromisoformat(scheduled_time) scheduled_dt = datetime.fromisoformat(scheduled_time)
# Если есть timezone - конвертируем в UTC # Если нет timezone — считаем что это локальное время (UTC+4)
if scheduled_dt.tzinfo is not None: # и конвертируем в UTC для scheduler
if scheduled_dt.tzinfo is None:
from datetime import timezone as tz from datetime import timezone as tz
local_tz = tz(timedelta(hours=4)) # Ульяновск UTC+4
scheduled_dt = scheduled_dt.replace(tzinfo=local_tz) # Помечаем как локальное
scheduled_dt = scheduled_dt.astimezone(tz.utc).replace(tzinfo=None) # Конвертируем в UTC
logger.info(f"⏰ Время конвертирована из локального в UTC: {scheduled_dt}")
else:
scheduled_dt = scheduled_dt.astimezone(tz.utc).replace(tzinfo=None) scheduled_dt = scheduled_dt.astimezone(tz.utc).replace(tzinfo=None)
logger.info(f"⏰ Время конвертирована в UTC: {scheduled_dt}") logger.info(f"⏰ Время уже с timezone, конвертирована в UTC: {scheduled_dt}")
# Проверяем что время в будущем (по UTC) # Проверяем что время в будущем (по UTC)
if scheduled_dt < datetime.utcnow(): if scheduled_dt < datetime.utcnow():
@ -816,7 +822,7 @@ async def api_create_scheduled_post(
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
url = f"https://api.telegram.org/bot{config.BOT_TOKEN}/sendPhoto" url = f"https://api.telegram.org/bot{config.BOT_TOKEN}/sendPhoto"
data = aiohttp.FormData() data = aiohttp.FormData()
data.add_field('chat_id', config.ADMIN_USER_ID) data.add_field('chat_id', str(config.ADMIN_USER_ID))
data.add_field('photo', open(temp_path, 'rb'), filename=photo.filename) data.add_field('photo', open(temp_path, 'rb'), filename=photo.filename)
data.add_field('caption', 'Preview') data.add_field('caption', 'Preview')
@ -851,8 +857,8 @@ async def api_create_scheduled_post(
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
url = f"https://api.telegram.org/bot{config.BOT_TOKEN}/sendDocument" url = f"https://api.telegram.org/bot{config.BOT_TOKEN}/sendDocument"
data = aiohttp.FormData() data = aiohttp.FormData()
data.add_field('chat_id', config.ADMIN_USER_ID) data.add_field('chat_id', str(config.ADMIN_USER_ID))
with open(temp_path, 'rb') as doc_file: with open(temp_path, 'rb') as doc_file:
data.add_field('document', doc_file, filename=document.filename) data.add_field('document', doc_file, filename=document.filename)
data.add_field('caption', 'Document preview') data.add_field('caption', 'Document preview')

View file

@ -163,9 +163,7 @@
if (file) { if (file) {
nameSpan.textContent = file.name; nameSpan.textContent = file.name;
preview.style.display = 'block'; preview.style.display = 'block';
// Очищаем фото если выбран документ // НЕ очищаем фото — можно прикрепить и то и другое
document.getElementById('postPhoto').value = '';
document.getElementById('preview').style.display = 'none';
} else { } else {
preview.style.display = 'none'; preview.style.display = 'none';
} }