feat(telegram client): adjust format of displayed text and etc

-
pull/2/head
TBS093A 2025-05-14 16:47:19 +02:00
parent bdb909bd48
commit 780669b958
1 changed files with 170 additions and 28 deletions

View File

@ -4,7 +4,7 @@ from telegram import Update
from telegram.ext import ContextTypes
from telegram.constants import ParseMode
from .youtube_utils import extract_youtube_urls, extract_video_id, get_transcript
from .openai_utils import summarize_long_text
from .openai_utils import summarize_long_text, chunk_text, summarize_text
from .db import save_video_summary, check_if_url_exists
from .config import TRANSCRIPT_LANGUAGES
@ -40,7 +40,8 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
# Opcjonalnie: sprawdź, czy już istnieje w bazie, zanim zaczniesz przetwarzać
# if await check_if_url_exists(url):
# logger.info(f"URL {url} już istnieje w bazie danych. Pomijam.")
# await context.bot.send_message(
# await safe_send_message(
# context.bot,
# chat_id=chat_id,
# text=f"Informacje o filmie {url} są już w bazie.",
# disable_web_page_preview=True
@ -63,7 +64,8 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
title = f"Film YouTube {video_id}" # Użyj zastępczego tytułu
except Exception as e:
logger.warning(f"Nie udało się pobrać transkrypcji dla ID filmu: {video_id}: {str(e)}")
await context.bot.send_message(
await safe_send_message(
context.bot,
chat_id=chat_id,
text=f"Nie udało się pobrać transkrypcji dla filmu: {url}",
disable_web_page_preview=True
@ -71,40 +73,125 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
processed_urls_in_message.add(url)
continue # Potrzebujemy transkrypcji do streszczenia
await context.bot.send_chat_action(chat_id=chat_id, action='typing')
# Informuj o rozpoczęciu przetwarzania
await safe_send_message(
context.bot,
chat_id=chat_id,
text=f"*Rozpoczynam przetwarzanie filmu:*\n{escape_markdown_v2(title)}\n\n*Link:* {escape_markdown_v2(url)}",
parse_mode=ParseMode.MARKDOWN_V2,
disable_web_page_preview=True
)
# Wygeneruj streszczenie
summary = await summarize_long_text(transcript)
if not summary:
logger.error(f"Nie udało się wygenerować streszczenia dla ID filmu: {video_id}")
await context.bot.send_message(
# Podziel tekst na fragmenty do przetworzenia
chunks = await chunk_text(transcript)
all_summaries = []
combined_summary = ""
# Dla każdego fragmentu, wygeneruj i wyślij streszczenie
for i, chunk in enumerate(chunks):
await context.bot.send_chat_action(chat_id=chat_id, action='typing')
progress_msg = f"Przetwarzanie fragmentu {i+1}/{len(chunks)}..."
logger.info(progress_msg)
# Jeśli jest więcej niż jeden fragment, powiadom użytkownika o postępie
if len(chunks) > 1 and i == 0:
await safe_send_message(
context.bot,
chat_id=chat_id,
text=f"Film jest długi, podzielono transkrypcję na {len(chunks)} części. Przetwarzam każdą z nich...",
disable_web_page_preview=True
)
try:
# Generuj streszczenie fragmentu
partial_summary = await summarize_text(
chunk,
is_partial=len(chunks) > 1,
part_num=i+1,
total_parts=len(chunks)
)
all_summaries.append(partial_summary)
# Jeśli mamy więcej niż jeden fragment, wysyłaj postęp na bieżąco
if len(chunks) > 1:
part_header = f"*Streszczenie - część {i+1}/{len(chunks)}:*\n\n"
response_text = f"{part_header}{escape_markdown_v2(partial_summary)}"
await send_long_message(
context.bot,
chat_id=chat_id,
text=response_text,
parse_mode=ParseMode.MARKDOWN_V2,
disable_web_page_preview=True
)
except Exception as e:
logger.error(f"Błąd podczas streszczania fragmentu {i+1}: {str(e)}")
await safe_send_message(
context.bot,
chat_id=chat_id,
text=f"Wystąpił błąd podczas przetwarzania fragmentu {i+1}/{len(chunks)}: {str(e)}",
disable_web_page_preview=True
)
# Jeśli mamy więcej niż 3 fragmenty, generuj końcowe streszczenie
if len(chunks) > 3:
await context.bot.send_chat_action(chat_id=chat_id, action='typing')
await safe_send_message(
context.bot,
chat_id=chat_id,
text=f"Nie udało się wygenerować streszczenia dla filmu: {title} ({url})",
text="Tworzę ostateczne streszczenie łączące wszystkie fragmenty...",
disable_web_page_preview=True
)
processed_urls_in_message.add(url)
continue
# Zapisz do bazy danych
saved = await save_video_summary(url, title, transcript, summary)
if saved:
logger.info(f"Pomyślnie przetworzono i zapisano film: {title} ({url})")
response_text = (
f"*Przetworzono film:* {escape_markdown_v2(title)}\n\n"
f"*Link:* {escape_markdown_v2(url)}\n\n"
f"*Streszczenie:*\n{escape_markdown_v2(summary)}"
)
# Użyj funkcji do wysyłania długich wiadomości
try:
# Połącz wszystkie częściowe streszczenia
combined_text = "\n\n".join(all_summaries)
combined_summary = await summarize_text(
combined_text,
is_final_summary=True
)
except Exception as e:
logger.error(f"Błąd podczas generowania końcowego streszczenia: {str(e)}")
# Jeśli końcowe streszczenie się nie powiedzie, użyj połączonych częściowych
combined_summary = "\n\n".join(all_summaries)
await safe_send_message(
context.bot,
chat_id=chat_id,
text="Nie udało się wygenerować końcowego streszczenia. Wyświetlam połączone częściowe streszczenia.",
disable_web_page_preview=True
)
else:
# Dla mniejszej liczby fragmentów po prostu połącz streszczenia
combined_summary = "\n\n".join(all_summaries)
# Zapisz pełne streszczenie do bazy danych
saved = await save_video_summary(url, title, transcript, combined_summary)
# Wyślij końcowe streszczenie, jeśli były więcej niż 3 fragmenty
if len(chunks) > 3:
final_text = f"*Ostateczne streszczenie filmu:*\n*{escape_markdown_v2(title)}*\n\n{escape_markdown_v2(combined_summary)}"
await send_long_message(
context.bot,
chat_id=chat_id,
text=response_text,
text=final_text,
parse_mode=ParseMode.MARKDOWN_V2,
disable_web_page_preview=True
)
# Podsumowanie procesu
if saved:
logger.info(f"Pomyślnie przetworzono i zapisano film: {title} ({url})")
if len(chunks) <= 3: # Nie wysyłaj podsumowania ponownie dla dłuższych filmów
await safe_send_message(
context.bot,
chat_id=chat_id,
text=f"Pomyślnie zapisano streszczenie filmu w bazie danych: {title}",
disable_web_page_preview=True
)
else:
logger.error(f"Nie udało się zapisać danych do bazy dla filmu: {title} ({url})")
await context.bot.send_message(
await safe_send_message(
context.bot,
chat_id=chat_id,
text=f"Wystąpił błąd podczas zapisywania danych dla filmu: {title} ({url})",
disable_web_page_preview=True
@ -115,8 +202,61 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
# Funkcja pomocnicza do escape'owania znaków specjalnych MarkdownV2
def escape_markdown_v2(text: str) -> str:
"""Ucieka znaki specjalne dla parsowania Telegram MarkdownV2."""
escape_chars = r'_*[]()~`>#+-=|{}.!'
return re.sub(f'([{re.escape(escape_chars)}])', r'\\\1', text)
if not text:
return ""
# Pełna lista znaków specjalnych w Telegram MarkdownV2
escape_chars = r'-'
# Zamieniamy każdy znak specjalny na jego wersję z dwoma backslashami przed nim
return re.sub(f'([{re.escape(escape_chars)}])', r'\1', text)
# Ulepszona funkcja do bezpiecznego wysyłania wiadomości
async def safe_send_message(bot, chat_id, text, parse_mode=None, disable_web_page_preview=False, max_retries=3):
"""
Bezpiecznie wysyła wiadomość, obsługując błędy związane z formatowaniem.
Args:
bot: Instancja bota Telegram
chat_id: ID czatu
text: Tekst do wysłania
parse_mode: Tryb parsowania (None, HTML, Markdown, MarkdownV2)
disable_web_page_preview: Czy wyłączyć podgląd linków
max_retries: Maksymalna liczba prób
Returns:
Obiekt wysłanej wiadomości lub None w przypadku błędu
"""
# Próbuj wysłać z formatowaniem
for attempt in range(max_retries):
try:
return await bot.send_message(
chat_id=chat_id,
text=text,
parse_mode=parse_mode,
disable_web_page_preview=disable_web_page_preview
)
except Exception as e:
logger.warning(f"Błąd wysyłania wiadomości (próba {attempt+1}/{max_retries}): {str(e)}")
error_msg = str(e).lower()
# Jeśli jest to błąd parsowania Markdown
if "parse" in error_msg and "entities" in error_msg:
# Spróbuj ponownie bez formatowania
if attempt == max_retries - 1:
logger.info("Wysyłam wiadomość bez formatowania")
try:
return await bot.send_message(
chat_id=chat_id,
text=text,
parse_mode=None, # Bez formatowania
disable_web_page_preview=disable_web_page_preview
)
except Exception as e2:
logger.error(f"Nie udało się wysłać wiadomości nawet bez formatowania: {str(e2)}")
return None
return None
# Funkcja do dzielenia długich wiadomości
async def send_long_message(bot, chat_id, text, parse_mode=None, disable_web_page_preview=False):
@ -135,7 +275,8 @@ async def send_long_message(bot, chat_id, text, parse_mode=None, disable_web_pag
if len(text) <= max_length:
# Jeśli wiadomość nie przekracza limitu, wyślij ją normalnie
return await bot.send_message(
return await safe_send_message(
bot,
chat_id=chat_id,
text=text,
parse_mode=parse_mode,
@ -150,7 +291,8 @@ async def send_long_message(bot, chat_id, text, parse_mode=None, disable_web_pag
# Wyślij części wiadomości
for i, part in enumerate(parts):
part_header = f"*Część {i+1}/{len(parts)}*\n\n" if parse_mode == ParseMode.MARKDOWN_V2 else f"Część {i+1}/{len(parts)}\n\n"
await bot.send_message(
await safe_send_message(
bot,
chat_id=chat_id,
text=part_header + part,
parse_mode=parse_mode,