Initial commit: UMB Telegram Bot
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
import logging
|
||||
|
||||
from aiogram import Router, F
|
||||
from aiogram.types import Message
|
||||
from aiogram.filters import Command
|
||||
|
||||
from bot.utils.ai_client import ask_ai
|
||||
from bot.utils.database import (
|
||||
add_context_message,
|
||||
get_user_context,
|
||||
is_ai_blocked,
|
||||
block_user_from_ai,
|
||||
unblock_user_from_ai,
|
||||
get_chat_users,
|
||||
clear_dialogue,
|
||||
clear_user_context,
|
||||
get_or_create_dialogue,
|
||||
increment_dialogue_count,
|
||||
save_chat_user,
|
||||
)
|
||||
from bot.utils.memory import save_summary_with_embedding, find_relevant_summaries, generate_summary
|
||||
from config import AI_BLOCK_DEFAULT_DURATION, AI_CONTEXT_LIMIT, AI_DIALOGUE_LIMIT, AI_PHASE2_LIMIT
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
def extract_username(text: str) -> str | None:
|
||||
parts = text.split()
|
||||
for part in parts:
|
||||
if part.startswith("@"):
|
||||
return part[1:]
|
||||
if part.startswith("id") and part[2:].isdigit():
|
||||
return part[2:]
|
||||
return None
|
||||
|
||||
|
||||
async def get_user_id_by_username(message: Message, username: str) -> int | None:
|
||||
try:
|
||||
username_clean = username.lstrip("@")
|
||||
admins = await message.bot.get_chat_administrators(message.chat.id)
|
||||
for admin in admins:
|
||||
if admin.user.username and admin.user.username.lower() == username_clean.lower():
|
||||
return admin.user.id
|
||||
if str(admin.user.id) == username_clean:
|
||||
return admin.user.id
|
||||
|
||||
if username_clean.isdigit():
|
||||
return int(username_clean)
|
||||
except Exception as e:
|
||||
logger.error("Error resolving username %s: %s", username, e)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
async def _is_creator(message: Message) -> bool:
|
||||
if message.from_user.id == message.chat.id:
|
||||
return True
|
||||
try:
|
||||
admins = await message.bot.get_chat_administrators(message.chat.id)
|
||||
for admin in admins:
|
||||
if admin.status == "creator" and admin.user.id == message.from_user.id:
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error("Error checking creator: %s", e)
|
||||
return False
|
||||
|
||||
|
||||
async def _not_creator(message: Message) -> bool:
|
||||
return not await _is_creator(message)
|
||||
|
||||
|
||||
@router.message(Command("ai"))
|
||||
async def cmd_ai(message: Message):
|
||||
user_id = message.from_user.id
|
||||
chat_id = message.chat.id
|
||||
|
||||
await save_chat_user(user_id, chat_id, message.from_user.username, message.from_user.full_name or "")
|
||||
|
||||
if await is_ai_blocked(user_id, chat_id):
|
||||
await message.answer("Тебе временно недоступен AI. Обратись к владельцу чата.", parse_mode=None)
|
||||
return
|
||||
|
||||
text_parts = message.text.split(maxsplit=1)
|
||||
has_direct_question = len(text_parts) > 1 and text_parts[1].strip()
|
||||
|
||||
if message.reply_to_message:
|
||||
target_text = message.reply_to_message.text or message.reply_to_message.caption
|
||||
if not target_text:
|
||||
await message.answer("Могу работать только с текстовыми сообщениями.", parse_mode=None)
|
||||
return
|
||||
prompt = f"Проанализируй это сообщение:\n\n{target_text}"
|
||||
context_text = target_text
|
||||
elif message.forward_from and (message.forward_from.text or message.forward_from.caption):
|
||||
target_text = message.forward_from.text or message.forward_from.caption
|
||||
prompt = f"Проанализируй это сообщение:\n\n{target_text}"
|
||||
context_text = target_text
|
||||
elif has_direct_question:
|
||||
prompt = text_parts[1].strip()
|
||||
context_text = prompt
|
||||
else:
|
||||
await _start_dialogue(message)
|
||||
return
|
||||
|
||||
status_msg = await message.reply("Думаю...", parse_mode=None)
|
||||
|
||||
context = await get_user_context(user_id, chat_id, AI_CONTEXT_LIMIT)
|
||||
|
||||
try:
|
||||
relevant = await find_relevant_summaries(user_id, chat_id, prompt, top_k=2)
|
||||
if relevant:
|
||||
summary_text = "Из прошлых диалогов:\n" + "\n---\n".join(relevant)
|
||||
context.insert(0, {"role": "system", "content": summary_text})
|
||||
except Exception as e:
|
||||
logger.error("Error fetching relevant summaries: %s", e)
|
||||
|
||||
async def update_status(text: str):
|
||||
try:
|
||||
await status_msg.edit_text(text, parse_mode=None)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
response = await ask_ai(prompt, context, status_callback=update_status)
|
||||
|
||||
if not response:
|
||||
response = "Не удалось получить ответ от AI."
|
||||
|
||||
await add_context_message(user_id, chat_id, context_text, "user")
|
||||
await add_context_message(user_id, chat_id, response, "assistant")
|
||||
|
||||
try:
|
||||
await status_msg.edit_text(response, parse_mode="HTML", disable_web_page_preview=True)
|
||||
except Exception:
|
||||
await status_msg.edit_text(response, parse_mode=None, disable_web_page_preview=True)
|
||||
|
||||
|
||||
async def _start_dialogue(message: Message):
|
||||
user_id = message.from_user.id
|
||||
chat_id = message.chat.id
|
||||
|
||||
dialogue = await get_or_create_dialogue(user_id, chat_id)
|
||||
|
||||
if not dialogue["is_active"]:
|
||||
remaining = int(dialogue["blocked_until"] - __import__("time").time()) if dialogue["blocked_until"] else 0
|
||||
mins = remaining // 60
|
||||
await message.answer(
|
||||
f"Твой лимит диалога исчерпан. Попробуй через {mins} мин.",
|
||||
parse_mode=None,
|
||||
)
|
||||
return
|
||||
|
||||
if dialogue["phase"] == 2 and dialogue["msg_count"] >= AI_PHASE2_LIMIT:
|
||||
await message.answer("Твой лимит исчерпан.", parse_mode=None)
|
||||
return
|
||||
|
||||
await message.answer(
|
||||
"💬 Диалог начат! Отвечай на мои сообщения, чтобы продолжать.\n"
|
||||
"Отправь /aiclear чтобы завершить.",
|
||||
parse_mode=None,
|
||||
)
|
||||
|
||||
|
||||
@router.message(Command("aiuser"))
|
||||
async def cmd_aiuser(message: Message):
|
||||
if await _not_creator(message):
|
||||
return
|
||||
|
||||
try:
|
||||
users = await get_chat_users(message.chat.id)
|
||||
|
||||
lines = []
|
||||
for i, u in enumerate(users, 1):
|
||||
username_str = f"@{u['username']}" if u.get("username") else "NO DATA"
|
||||
lines.append(f"{i}. {u['full_name']} | {username_str} | {u['user_id']}")
|
||||
|
||||
text = "\n".join(lines) if lines else "Нет данных о пользователях."
|
||||
|
||||
await message.bot.send_message(
|
||||
message.from_user.id,
|
||||
f"📋 Список пользователей чата ({len(users)}):\n\n{text}",
|
||||
parse_mode=None,
|
||||
)
|
||||
|
||||
msg = await message.answer("✅ Список отправлен в ЛС.", parse_mode=None)
|
||||
except Exception as e:
|
||||
logger.error("aiuser error: %s", e)
|
||||
await message.answer("Не удалось получить список пользователей. Возможно, у бота нет доступа.", parse_mode=None)
|
||||
|
||||
|
||||
@router.message(Command("aiclear"))
|
||||
async def cmd_aiclear(message: Message):
|
||||
user_id = message.from_user.id
|
||||
chat_id = message.chat.id
|
||||
|
||||
try:
|
||||
context_messages = await get_user_context(user_id, chat_id, limit=50)
|
||||
cnt = len(context_messages) if context_messages else 0
|
||||
if context_messages and cnt >= 2:
|
||||
status_msg = await message.answer("Сохраняю выжимку диалога...", parse_mode=None)
|
||||
summary = await generate_summary(context_messages)
|
||||
if summary:
|
||||
await save_summary_with_embedding(user_id, chat_id, summary)
|
||||
await clear_user_context(user_id, chat_id)
|
||||
await status_msg.edit_text("✅ Выжимка сохранена. Диалог очищен.", parse_mode=None)
|
||||
logger.info("aiclear | user=%d chat=%d context=%d summary=%d emb=saved", user_id, chat_id, cnt, len(summary))
|
||||
else:
|
||||
logger.warning("aiclear | user=%d chat=%d context=%d summary=None", user_id, chat_id, cnt)
|
||||
else:
|
||||
logger.warning("aiclear | user=%d chat=%d not enough context (%d < 2)", user_id, chat_id, cnt)
|
||||
except Exception as e:
|
||||
logger.error("aiclear summary error: %s", e)
|
||||
|
||||
await clear_dialogue(user_id, chat_id)
|
||||
|
||||
from bot.utils.database import unblock_user_from_ai
|
||||
await unblock_user_from_ai(user_id, chat_id)
|
||||
|
||||
await message.answer("✅ Диалог очищен. Можешь начать новый.", parse_mode=None)
|
||||
|
||||
|
||||
@router.message(Command("aino"))
|
||||
async def cmd_aino(message: Message):
|
||||
if await _not_creator(message):
|
||||
return
|
||||
|
||||
username = extract_username(message.text)
|
||||
if not username:
|
||||
await message.answer("Укажи пользователя: /aino @username или /aino id<UID>", parse_mode=None)
|
||||
return
|
||||
|
||||
target_user_id = await get_user_id_by_username(message, username)
|
||||
if not target_user_id:
|
||||
await message.answer(f"Не удалось найти пользователя {username}.", parse_mode=None)
|
||||
return
|
||||
|
||||
await block_user_from_ai(target_user_id, message.chat.id, message.from_user.id, AI_BLOCK_DEFAULT_DURATION)
|
||||
await message.answer(f"Пользователь {username} заблокирован от AI на 24 часа.", parse_mode=None)
|
||||
|
||||
|
||||
@router.message(Command("aiyes"))
|
||||
async def cmd_aiyes(message: Message):
|
||||
if await _not_creator(message):
|
||||
return
|
||||
|
||||
username = extract_username(message.text)
|
||||
if not username:
|
||||
await message.answer("Укажи пользователя: /aiyes @username или /aiyes id<UID>", parse_mode=None)
|
||||
return
|
||||
|
||||
target_user_id = await get_user_id_by_username(message, username)
|
||||
if not target_user_id:
|
||||
await message.answer(f"Не удалось найти пользователя {username}.", parse_mode=None)
|
||||
return
|
||||
|
||||
unblocked = await unblock_user_from_ai(target_user_id, message.chat.id)
|
||||
if unblocked:
|
||||
await message.answer(f"Пользователь {username} разблокирован для AI.", parse_mode=None)
|
||||
else:
|
||||
await message.answer(f"Пользователь {username} не был заблокирован от AI.", parse_mode=None)
|
||||
@@ -0,0 +1,170 @@
|
||||
import logging
|
||||
import time
|
||||
|
||||
from aiogram import Router, F
|
||||
from aiogram.types import Message
|
||||
|
||||
from bot.utils.ai_client import ask_ai
|
||||
from bot.utils.database import (
|
||||
add_context_message,
|
||||
get_user_context,
|
||||
get_or_create_dialogue,
|
||||
increment_dialogue_count,
|
||||
reset_dialogue_to_phase2,
|
||||
block_dialogue,
|
||||
is_ai_blocked,
|
||||
save_chat_user,
|
||||
clear_user_context,
|
||||
)
|
||||
from bot.utils.memory import find_relevant_summaries, save_summary_with_embedding, generate_summary
|
||||
from config import AI_CONTEXT_LIMIT, AI_DIALOGUE_LIMIT, AI_PHASE2_LIMIT, AI_COOLDOWN
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
@router.message(F.text, F.reply_to_message.as_("replied"))
|
||||
async def handle_dialogue_reply(message: Message, replied: Message):
|
||||
user_id = message.from_user.id
|
||||
chat_id = message.chat.id
|
||||
|
||||
await save_chat_user(user_id, chat_id, message.from_user.username, message.from_user.full_name or "")
|
||||
|
||||
if await is_ai_blocked(user_id, chat_id):
|
||||
return
|
||||
|
||||
replied_from_bot = replied.from_user and replied.from_user.is_bot
|
||||
|
||||
if not replied_from_bot:
|
||||
return
|
||||
|
||||
dialogue = await get_or_create_dialogue(user_id, chat_id)
|
||||
|
||||
if not dialogue["is_active"]:
|
||||
return
|
||||
|
||||
count_result = await increment_dialogue_count(user_id, chat_id)
|
||||
current_count = count_result["msg_count"]
|
||||
phase = dialogue["phase"]
|
||||
|
||||
logger.info("Dialogue msg | user=%d chat=%d phase=%d count=%d", user_id, chat_id, phase, current_count)
|
||||
|
||||
if phase == 1:
|
||||
limit = AI_DIALOGUE_LIMIT
|
||||
else:
|
||||
limit = AI_PHASE2_LIMIT
|
||||
|
||||
if current_count > limit:
|
||||
if phase == 1:
|
||||
logger.info("Dialogue phase1→2 | user=%d chat=%d msg_count=%d", user_id, chat_id, current_count)
|
||||
await _transition_to_phase2(message, user_id, chat_id)
|
||||
else:
|
||||
logger.info("Dialogue ended | user=%d chat=%d msg_count=%d", user_id, chat_id, current_count)
|
||||
await _end_dialogue(message, user_id, chat_id)
|
||||
return
|
||||
|
||||
if phase == 1:
|
||||
context_limit = AI_DIALOGUE_LIMIT
|
||||
else:
|
||||
context_limit = AI_PHASE2_LIMIT
|
||||
|
||||
context_messages = await get_user_context(user_id, chat_id, limit=context_limit)
|
||||
|
||||
extra_context = ""
|
||||
try:
|
||||
relevant = await find_relevant_summaries(user_id, chat_id, message.text or "", top_k=2)
|
||||
if relevant:
|
||||
extra_context = "\n\nИз прошлых диалогов:\n" + "\n---\n".join(relevant[:2])
|
||||
except Exception as e:
|
||||
logger.error("Error fetching relevant summaries: %s", e)
|
||||
|
||||
remaining = limit - current_count
|
||||
warning = ""
|
||||
if remaining <= 5:
|
||||
warning = f"\n\n⚠️ Осталось {remaining} сообщений в этом диалоге."
|
||||
|
||||
system_prompt_extra = ""
|
||||
if extra_context:
|
||||
system_prompt_extra += extra_context
|
||||
if warning:
|
||||
system_prompt_extra += warning
|
||||
|
||||
message_text = message.text or ""
|
||||
if system_prompt_extra:
|
||||
message_text += "\n\n(Контекст)" + system_prompt_extra
|
||||
|
||||
status_msg = await message.answer("✍️", parse_mode=None)
|
||||
|
||||
async def update_status(text: str):
|
||||
try:
|
||||
await status_msg.edit_text(text, parse_mode=None)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
response = await ask_ai(
|
||||
message_text,
|
||||
context_messages,
|
||||
status_callback=update_status,
|
||||
)
|
||||
|
||||
if not response:
|
||||
response = "Не могу ответить сейчас."
|
||||
|
||||
await add_context_message(user_id, chat_id, message.text or "", "user")
|
||||
await add_context_message(user_id, chat_id, response, "assistant")
|
||||
|
||||
try:
|
||||
await message.reply(response, parse_mode="HTML", disable_web_page_preview=True)
|
||||
except Exception:
|
||||
await message.reply(response, parse_mode=None, disable_web_page_preview=True)
|
||||
|
||||
try:
|
||||
await status_msg.delete()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if remaining <= 3 and remaining > 0:
|
||||
try:
|
||||
warn_msg = await message.reply(
|
||||
f"⚠️ Осталось {remaining} сообщений. Память почти заполнена.",
|
||||
parse_mode=None,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
async def _transition_to_phase2(message: Message, user_id: int, chat_id: int):
|
||||
status_msg = await message.answer("Сохраняю выжимку диалога...", parse_mode=None)
|
||||
|
||||
try:
|
||||
context = await get_user_context(user_id, chat_id, limit=AI_DIALOGUE_LIMIT)
|
||||
if context and len(context) >= 4:
|
||||
summary = await generate_summary(context)
|
||||
if summary:
|
||||
await save_summary_with_embedding(user_id, chat_id, summary)
|
||||
await clear_user_context(user_id, chat_id)
|
||||
except Exception as e:
|
||||
logger.error("Error saving summary: %s", e)
|
||||
|
||||
await reset_dialogue_to_phase2(user_id, chat_id)
|
||||
|
||||
try:
|
||||
await status_msg.edit_text("✅ Начинаю новую сессию (осталось 20 сообщений).", parse_mode=None)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
async def _end_dialogue(message: Message, user_id: int, chat_id: int):
|
||||
context = await get_user_context(user_id, chat_id, limit=AI_PHASE2_LIMIT)
|
||||
if context and len(context) >= 4:
|
||||
try:
|
||||
summary = await generate_summary(context)
|
||||
if summary:
|
||||
await save_summary_with_embedding(user_id, chat_id, summary)
|
||||
except Exception as e:
|
||||
logger.error("Error saving final summary: %s", e)
|
||||
|
||||
await block_dialogue(user_id, chat_id, AI_COOLDOWN)
|
||||
|
||||
await message.reply("Твой лимит исчерпан. Возвращайся через час.", parse_mode=None)
|
||||
@@ -0,0 +1,29 @@
|
||||
from aiogram import Router, F
|
||||
from aiogram.types import Message
|
||||
from aiogram.filters import Command
|
||||
|
||||
from bot.utils.layout_converter import convert_layout
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
@router.message(Command("start"))
|
||||
async def cmd_start(message: Message):
|
||||
await message.answer(
|
||||
f"Привет, {message.from_user.full_name}!\n"
|
||||
"Я бот для модерации стикеров/GIF и восстановления раскладки.\n"
|
||||
"Используй /res, ответив на сообщение с неправильной раскладкой.",
|
||||
parse_mode=None,
|
||||
)
|
||||
|
||||
|
||||
@router.message(Command("res"))
|
||||
async def cmd_res(message: Message):
|
||||
if message.reply_to_message and message.reply_to_message.text:
|
||||
restored_text = convert_layout(message.reply_to_message.text)
|
||||
await message.answer(restored_text, parse_mode=None)
|
||||
else:
|
||||
await message.answer(
|
||||
"Пожалуйста, ответьте командой /res на сообщение с текстом.",
|
||||
parse_mode=None,
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
from aiogram import Router, F
|
||||
from aiogram.types import Message
|
||||
|
||||
from bot.utils.database import add_sticker_message, ban_user_stickers, is_user_sticker_banned
|
||||
from config import MODERATION_LIMIT
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
@router.message(F.sticker | F.animation)
|
||||
async def handle_sticker_or_gif(message: Message):
|
||||
user_id = message.from_user.id
|
||||
chat_id = message.chat.id
|
||||
|
||||
if await is_user_sticker_banned(user_id, chat_id):
|
||||
await message.delete()
|
||||
return
|
||||
|
||||
count = await add_sticker_message(user_id, chat_id)
|
||||
|
||||
if count >= MODERATION_LIMIT:
|
||||
await ban_user_stickers(user_id, chat_id)
|
||||
await message.answer(
|
||||
f"⚠️ {message.from_user.full_name}, вы превысили лимит стикеров/GIF!\n"
|
||||
"Отправка стикеров и GIF ограничена на 5 минут.",
|
||||
parse_mode=None,
|
||||
)
|
||||
@@ -0,0 +1,36 @@
|
||||
import logging
|
||||
|
||||
from aiogram import Router, F
|
||||
from aiogram.types import Message
|
||||
|
||||
from bot.utils.voice import download_voice, convert_to_wav, transcribe_audio, normalize_text, cleanup_files
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
@router.message(F.voice)
|
||||
async def handle_voice(message: Message):
|
||||
status_msg = await message.answer("🎤 Распознаю речь...")
|
||||
|
||||
ogg_path = None
|
||||
wav_path = None
|
||||
|
||||
try:
|
||||
ogg_path = await download_voice(message.bot, message.voice.file_id)
|
||||
wav_path = await convert_to_wav(ogg_path)
|
||||
text = await transcribe_audio(wav_path)
|
||||
|
||||
if not text:
|
||||
await status_msg.edit_text("❌ Не удалось распознать речь.")
|
||||
return
|
||||
|
||||
text = normalize_text(text)
|
||||
await status_msg.edit_text(f"📝 {text}")
|
||||
|
||||
except Exception as e:
|
||||
logger.exception("Voice processing error")
|
||||
await status_msg.edit_text("❌ Ошибка при обработке голосового сообщения.")
|
||||
finally:
|
||||
await cleanup_files(ogg_path, wav_path)
|
||||
@@ -0,0 +1,12 @@
|
||||
from aiogram import Router
|
||||
from aiogram.types import Message
|
||||
from aiogram.filters import Command
|
||||
|
||||
from bot.utils.weather import get_weather
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
@router.message(Command("weather"))
|
||||
async def cmd_weather(message: Message):
|
||||
await get_weather(message)
|
||||
@@ -0,0 +1,103 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
|
||||
from aiogram import Router
|
||||
from aiogram.filters import Command
|
||||
from aiogram.types import Message, FSInputFile
|
||||
|
||||
from bot.utils.database import get_file_id, save_file_id
|
||||
from bot.utils.yadisk_download import download_yandex_file
|
||||
from bot.utils.s3_client import upload_file
|
||||
|
||||
router = Router()
|
||||
logger = logging.getLogger("yadisk")
|
||||
|
||||
MAX_TELEGRAM_FILE_SIZE = 50 * 1024 * 1024
|
||||
|
||||
|
||||
def is_valid_yandex_public_link(value: str) -> bool:
|
||||
value = value.strip().lower()
|
||||
return (
|
||||
value.startswith("https://disk.yandex.")
|
||||
or value.startswith("http://disk.yandex.")
|
||||
or value.startswith("https://yadi.sk/")
|
||||
or value.startswith("http://yadi.sk/")
|
||||
)
|
||||
|
||||
|
||||
@router.message(Command("ydf"))
|
||||
async def yandex_download_handler(message: Message):
|
||||
text = (message.text or "").strip()
|
||||
parts = text.split(maxsplit=1)
|
||||
|
||||
if len(parts) < 2 or not parts[1].strip():
|
||||
await message.answer("Укажите ссылку: /ydf https://yadi.sk/...")
|
||||
return
|
||||
|
||||
url = parts[1].strip()
|
||||
|
||||
if url.startswith("(new)"):
|
||||
url = url.replace("(new)", "", 1).strip()
|
||||
if not url:
|
||||
await message.answer("После (new) укажите ссылку.")
|
||||
return
|
||||
await save_file_id(url, None)
|
||||
|
||||
if not is_valid_yandex_public_link(url):
|
||||
await message.answer("Это не похоже на публичную ссылку Яндекс.Диска.")
|
||||
return
|
||||
|
||||
cached_id = await get_file_id(url)
|
||||
if cached_id:
|
||||
await message.answer_document(
|
||||
cached_id,
|
||||
caption="Файл из кеша Telegram. /ydf (new) [ссылка] для обновления.",
|
||||
)
|
||||
return
|
||||
|
||||
status_msg = await message.answer("Начинаю загрузку с Яндекс.Диска...")
|
||||
last_text = "Начинаю загрузку с Яндекс.Диска..."
|
||||
|
||||
async def progress(downloaded: int, total: int):
|
||||
nonlocal last_text
|
||||
if total <= 0:
|
||||
return
|
||||
pct = int(downloaded / total * 100)
|
||||
new_text = f"Загрузка: {pct}%"
|
||||
if new_text != last_text and pct % 5 == 0:
|
||||
try:
|
||||
await status_msg.edit_text(new_text)
|
||||
last_text = new_text
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
file_path = await download_yandex_file(url, progress_callback=progress)
|
||||
except Exception as exc:
|
||||
logger.exception("Ошибка скачивания с Яндекс.Диска")
|
||||
await status_msg.edit_text("Не удалось скачать файл. Проверьте ссылку.")
|
||||
return
|
||||
|
||||
try:
|
||||
file_size = os.path.getsize(file_path)
|
||||
|
||||
if file_size < MAX_TELEGRAM_FILE_SIZE:
|
||||
doc = FSInputFile(file_path)
|
||||
sent = await message.answer_document(doc)
|
||||
if sent.document and sent.document.file_id:
|
||||
await save_file_id(url, sent.document.file_id)
|
||||
await status_msg.delete()
|
||||
else:
|
||||
await status_msg.edit_text("Файл больше 50MB, загружаю в облако...")
|
||||
s3_url = await asyncio.to_thread(upload_file, file_path)
|
||||
if s3_url:
|
||||
await message.answer(f"Файл доступен для скачивания: {s3_url}")
|
||||
await status_msg.delete()
|
||||
else:
|
||||
await status_msg.edit_text("Не удалось загрузить файл в облако.")
|
||||
finally:
|
||||
try:
|
||||
os.remove(file_path)
|
||||
except OSError:
|
||||
pass
|
||||
Reference in New Issue
Block a user