Spaces:
Running
Running
Add more debug logging and remove nest_asyncio
Browse files
main.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
# main.py (Revised for Hugging Face -
|
2 |
import os
|
3 |
import re
|
4 |
import logging
|
@@ -13,7 +13,8 @@ from telegram.ext import (
|
|
13 |
MessageHandler,
|
14 |
filters,
|
15 |
ContextTypes,
|
16 |
-
CallbackQueryHandler
|
|
|
17 |
)
|
18 |
from telegram.constants import ParseMode # Import ParseMode explicitly
|
19 |
|
@@ -26,18 +27,17 @@ _apify_token_exists = bool(os.environ.get('APIFY_API_TOKEN'))
|
|
26 |
if _apify_token_exists:
|
27 |
from apify_client import ApifyClient
|
28 |
else:
|
29 |
-
ApifyClient = None
|
30 |
|
31 |
-
#
|
32 |
-
import nest_asyncio
|
33 |
-
nest_asyncio.apply()
|
34 |
|
35 |
# --- Logging Setup ---
|
36 |
logging.basicConfig(
|
37 |
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
38 |
-
level=logging.DEBUG # Keep DEBUG
|
39 |
)
|
40 |
-
# Reduce noise from libraries
|
41 |
logging.getLogger("httpx").setLevel(logging.WARNING)
|
42 |
if ApifyClient: logging.getLogger("apify_client").setLevel(logging.WARNING)
|
43 |
logging.getLogger("telegram.ext").setLevel(logging.DEBUG)
|
@@ -47,17 +47,14 @@ logging.getLogger('gunicorn.error').setLevel(logging.INFO)
|
|
47 |
logger = logging.getLogger(__name__)
|
48 |
logger.info("Logging configured (DEBUG level).")
|
49 |
|
50 |
-
# --- Environment Variable Loading
|
51 |
logger.info("Attempting to load secrets from environment variables...")
|
52 |
-
|
53 |
def get_secret(secret_name):
|
54 |
-
"""Reads secret and logs the attempt and result."""
|
55 |
logger.debug(f"Attempting to read secret: {secret_name}")
|
56 |
value = os.environ.get(secret_name)
|
57 |
-
if value:
|
58 |
-
|
59 |
-
else:
|
60 |
-
logger.warning(f"Secret '{secret_name}': Not Found")
|
61 |
return value
|
62 |
|
63 |
TELEGRAM_TOKEN = get_secret('TELEGRAM_TOKEN')
|
@@ -65,11 +62,11 @@ OPENROUTER_API_KEY = get_secret('OPENROUTER_API_KEY')
|
|
65 |
URLTOTEXT_API_KEY = get_secret('URLTOTEXT_API_KEY')
|
66 |
SUPADATA_API_KEY = get_secret('SUPADATA_API_KEY')
|
67 |
APIFY_API_TOKEN = get_secret('APIFY_API_TOKEN')
|
68 |
-
|
69 |
logger.info("Secret loading attempt finished.")
|
70 |
|
71 |
# --- Bot Logic Functions ---
|
72 |
# [PASTE ALL YOUR BOT LOGIC FUNCTIONS HERE - FROM is_youtube_url to generate_summary]
|
|
|
73 |
# Helper Functions
|
74 |
def is_youtube_url(url):
|
75 |
"""Checks if the URL is a valid YouTube video or shorts URL."""
|
@@ -526,6 +523,7 @@ Here is the text to summarise:"""
|
|
526 |
return "Sorry, an unexpected error occurred while generating the summary."
|
527 |
|
528 |
|
|
|
529 |
# --- Telegram Bot Handlers (Command, Message, CallbackQuery) ---
|
530 |
|
531 |
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
@@ -591,7 +589,13 @@ async def handle_summary_type_callback(update: Update, context: ContextTypes.DEF
|
|
591 |
"""Handles button presses for summary type selection."""
|
592 |
query = update.callback_query
|
593 |
if not query: return
|
594 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
595 |
|
596 |
summary_type = query.data
|
597 |
user = update.effective_user or query.from_user # Get user info
|
@@ -640,9 +644,10 @@ async def handle_summary_type_callback(update: Update, context: ContextTypes.DEF
|
|
640 |
try:
|
641 |
# Edit the message to show processing status
|
642 |
await query.edit_message_text(processing_message)
|
|
|
643 |
except Exception as e:
|
644 |
# If editing fails (e.g., message too old), send a new status message
|
645 |
-
logger.warning(f"Could not edit original message: {e}
|
646 |
try:
|
647 |
message_to_delete_later = await context.bot.send_message(chat_id=user.id, text=processing_message)
|
648 |
except Exception as send_err:
|
@@ -657,6 +662,7 @@ async def handle_summary_type_callback(update: Update, context: ContextTypes.DEF
|
|
657 |
|
658 |
try:
|
659 |
# Show "typing..." status in Telegram chat
|
|
|
660 |
await context.bot.send_chat_action(chat_id=user.id, action='typing')
|
661 |
|
662 |
# --- Content Fetching Logic ---
|
@@ -664,6 +670,7 @@ async def handle_summary_type_callback(update: Update, context: ContextTypes.DEF
|
|
664 |
video_id = extract_youtube_id(url)
|
665 |
if video_id:
|
666 |
# Fetch YT transcript using the function with fallbacks
|
|
|
667 |
content = await get_youtube_transcript(
|
668 |
video_id,
|
669 |
url, # Pass full URL for Apify
|
@@ -672,6 +679,7 @@ async def handle_summary_type_callback(update: Update, context: ContextTypes.DEF
|
|
672 |
)
|
673 |
# Set feedback message only if content fetching failed
|
674 |
user_feedback_message = None if content else "Sorry, I couldn't get the transcript for that YouTube video using any available method (unavailable/private/no captions?)."
|
|
|
675 |
else:
|
676 |
user_feedback_message = "Sorry, I couldn't understand that YouTube URL format."
|
677 |
else: # Website Logic (Requests/BS4 -> URLToText API)
|
@@ -712,6 +720,7 @@ async def handle_summary_type_callback(update: Update, context: ContextTypes.DEF
|
|
712 |
logger.warning(f"Summary generation failed or returned error: {summary}")
|
713 |
else:
|
714 |
# Send the successful summary
|
|
|
715 |
await context.bot.send_message(
|
716 |
chat_id=user.id,
|
717 |
text=summary,
|
@@ -727,6 +736,7 @@ async def handle_summary_type_callback(update: Update, context: ContextTypes.DEF
|
|
727 |
|
728 |
# --- Send Feedback if any step failed ---
|
729 |
if user_feedback_message and not success:
|
|
|
730 |
await context.bot.send_message(chat_id=user.id, text=user_feedback_message)
|
731 |
|
732 |
except Exception as e:
|
@@ -741,12 +751,14 @@ async def handle_summary_type_callback(update: Update, context: ContextTypes.DEF
|
|
741 |
finally:
|
742 |
# --- Cleanup ---
|
743 |
# Delete the "Processing..." status message or the original message with buttons
|
|
|
744 |
try:
|
745 |
if message_to_delete_later: # If we sent a separate status message
|
746 |
await context.bot.delete_message(chat_id=user.id, message_id=message_to_delete_later.message_id)
|
|
|
747 |
elif query: # Otherwise, delete the original message with the buttons
|
748 |
-
# We might have already edited it, but deleting ensures cleanup
|
749 |
await query.delete_message()
|
|
|
750 |
except Exception as del_e:
|
751 |
# Log if deletion fails, but don't let it stop anything
|
752 |
logger.warning(f"Could not delete status/button message: {del_e}")
|
@@ -756,33 +768,8 @@ async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> N
|
|
756 |
"""Log Errors caused by Updates."""
|
757 |
# Log the error and traceback
|
758 |
logger.error(f"Exception while handling an update: {context.error}", exc_info=context.error)
|
|
|
759 |
|
760 |
-
# Optionally send a notification to developer chat ID (replace with your actual ID)
|
761 |
-
# developer_chat_id = 12345678
|
762 |
-
# try:
|
763 |
-
# # Extract user/chat info if available
|
764 |
-
# chat_info = "N/A"
|
765 |
-
# user_info = "N/A"
|
766 |
-
# update_details = "N/A"
|
767 |
-
# if isinstance(update, Update) and update.effective_chat:
|
768 |
-
# chat_info = f"Chat: {update.effective_chat.id} ({update.effective_chat.type})"
|
769 |
-
# if isinstance(update, Update) and update.effective_user:
|
770 |
-
# user_info = f"User: {update.effective_user.id} (@{update.effective_user.username})"
|
771 |
-
# if update:
|
772 |
-
# update_details = f"Update Type: {update.__class__.__name__}"
|
773 |
-
|
774 |
-
# tb_list = traceback.format_exception(None, context.error, context.error.__traceback__)
|
775 |
-
# tb_string = "".join(tb_list)
|
776 |
-
# # Keep message reasonably short
|
777 |
-
# error_message = (
|
778 |
-
# f"🚨 Bot Error 🚨\n"
|
779 |
-
# f"{chat_info}\n{user_info}\n{update_details}\n"
|
780 |
-
# f"Error: {context.error}\n\n"
|
781 |
-
# f"Traceback (last part):\n```\n{tb_string[-1000:]}\n```" # Limit traceback length
|
782 |
-
# )
|
783 |
-
# await context.bot.send_message(chat_id=developer_chat_id, text=error_message[:4096]) # Limit msg len
|
784 |
-
# except Exception as e:
|
785 |
-
# logger.error(f"Failed to send error notification to developer: {e}")
|
786 |
|
787 |
# --- Bot Application Setup Function ---
|
788 |
async def setup_bot():
|
@@ -812,8 +799,6 @@ async def setup_bot():
|
|
812 |
|
813 |
# --- Run Setup and Store Application Instance ---
|
814 |
logger.info("Running bot setup...")
|
815 |
-
# Run the async setup function and store the application instance globally
|
816 |
-
# This runs once when the script/module is first loaded by Gunicorn worker
|
817 |
ptb_app = asyncio.run(setup_bot())
|
818 |
logger.info(f"Bot setup finished. Application instance: {'OK' if ptb_app else 'Failed'}")
|
819 |
|
|
|
1 |
+
# main.py (Revised for Hugging Face - Fix Event Loop Issue)
|
2 |
import os
|
3 |
import re
|
4 |
import logging
|
|
|
13 |
MessageHandler,
|
14 |
filters,
|
15 |
ContextTypes,
|
16 |
+
CallbackQueryHandler,
|
17 |
+
ApplicationBuilder # Import ApplicationBuilder
|
18 |
)
|
19 |
from telegram.constants import ParseMode # Import ParseMode explicitly
|
20 |
|
|
|
27 |
if _apify_token_exists:
|
28 |
from apify_client import ApifyClient
|
29 |
else:
|
30 |
+
ApifyClient = None
|
31 |
|
32 |
+
# NO nest_asyncio needed here usually when using native async framework integration
|
33 |
+
# import nest_asyncio
|
34 |
+
# nest_asyncio.apply()
|
35 |
|
36 |
# --- Logging Setup ---
|
37 |
logging.basicConfig(
|
38 |
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
39 |
+
level=logging.DEBUG # Keep DEBUG
|
40 |
)
|
|
|
41 |
logging.getLogger("httpx").setLevel(logging.WARNING)
|
42 |
if ApifyClient: logging.getLogger("apify_client").setLevel(logging.WARNING)
|
43 |
logging.getLogger("telegram.ext").setLevel(logging.DEBUG)
|
|
|
47 |
logger = logging.getLogger(__name__)
|
48 |
logger.info("Logging configured (DEBUG level).")
|
49 |
|
50 |
+
# --- Environment Variable Loading ---
|
51 |
logger.info("Attempting to load secrets from environment variables...")
|
52 |
+
# (Keep the get_secret function and secret loading as before)
|
53 |
def get_secret(secret_name):
|
|
|
54 |
logger.debug(f"Attempting to read secret: {secret_name}")
|
55 |
value = os.environ.get(secret_name)
|
56 |
+
if value: logger.info(f"Secret '{secret_name}': Found (Value length: {len(value)})")
|
57 |
+
else: logger.warning(f"Secret '{secret_name}': Not Found")
|
|
|
|
|
58 |
return value
|
59 |
|
60 |
TELEGRAM_TOKEN = get_secret('TELEGRAM_TOKEN')
|
|
|
62 |
URLTOTEXT_API_KEY = get_secret('URLTOTEXT_API_KEY')
|
63 |
SUPADATA_API_KEY = get_secret('SUPADATA_API_KEY')
|
64 |
APIFY_API_TOKEN = get_secret('APIFY_API_TOKEN')
|
|
|
65 |
logger.info("Secret loading attempt finished.")
|
66 |
|
67 |
# --- Bot Logic Functions ---
|
68 |
# [PASTE ALL YOUR BOT LOGIC FUNCTIONS HERE - FROM is_youtube_url to generate_summary]
|
69 |
+
# --- [ Ensure all functions from previous main.py are here ] ---
|
70 |
# Helper Functions
|
71 |
def is_youtube_url(url):
|
72 |
"""Checks if the URL is a valid YouTube video or shorts URL."""
|
|
|
523 |
return "Sorry, an unexpected error occurred while generating the summary."
|
524 |
|
525 |
|
526 |
+
|
527 |
# --- Telegram Bot Handlers (Command, Message, CallbackQuery) ---
|
528 |
|
529 |
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|
|
589 |
"""Handles button presses for summary type selection."""
|
590 |
query = update.callback_query
|
591 |
if not query: return
|
592 |
+
# --- Acknowledge callback query ---
|
593 |
+
try:
|
594 |
+
await query.answer() # Acknowledge button press immediately
|
595 |
+
logger.debug(f"Callback query {query.id} answered.")
|
596 |
+
except Exception as e:
|
597 |
+
logger.error(f"Failed to answer callback query {query.id}: {e}", exc_info=True)
|
598 |
+
# Proceed anyway, but logging the error is important
|
599 |
|
600 |
summary_type = query.data
|
601 |
user = update.effective_user or query.from_user # Get user info
|
|
|
644 |
try:
|
645 |
# Edit the message to show processing status
|
646 |
await query.edit_message_text(processing_message)
|
647 |
+
logger.debug(f"Edited message for query {query.id} to show processing status.")
|
648 |
except Exception as e:
|
649 |
# If editing fails (e.g., message too old), send a new status message
|
650 |
+
logger.warning(f"Could not edit original message for query {query.id}: {e}. Sending new status message.")
|
651 |
try:
|
652 |
message_to_delete_later = await context.bot.send_message(chat_id=user.id, text=processing_message)
|
653 |
except Exception as send_err:
|
|
|
662 |
|
663 |
try:
|
664 |
# Show "typing..." status in Telegram chat
|
665 |
+
logger.debug(f"Sending 'typing' action for chat {user.id}")
|
666 |
await context.bot.send_chat_action(chat_id=user.id, action='typing')
|
667 |
|
668 |
# --- Content Fetching Logic ---
|
|
|
670 |
video_id = extract_youtube_id(url)
|
671 |
if video_id:
|
672 |
# Fetch YT transcript using the function with fallbacks
|
673 |
+
logger.info(f"Fetching YouTube transcript for video_id: {video_id}")
|
674 |
content = await get_youtube_transcript(
|
675 |
video_id,
|
676 |
url, # Pass full URL for Apify
|
|
|
679 |
)
|
680 |
# Set feedback message only if content fetching failed
|
681 |
user_feedback_message = None if content else "Sorry, I couldn't get the transcript for that YouTube video using any available method (unavailable/private/no captions?)."
|
682 |
+
logger.info(f"YouTube transcript fetch completed. Content found: {bool(content)}")
|
683 |
else:
|
684 |
user_feedback_message = "Sorry, I couldn't understand that YouTube URL format."
|
685 |
else: # Website Logic (Requests/BS4 -> URLToText API)
|
|
|
720 |
logger.warning(f"Summary generation failed or returned error: {summary}")
|
721 |
else:
|
722 |
# Send the successful summary
|
723 |
+
logger.info("Summary generated successfully. Sending response.")
|
724 |
await context.bot.send_message(
|
725 |
chat_id=user.id,
|
726 |
text=summary,
|
|
|
736 |
|
737 |
# --- Send Feedback if any step failed ---
|
738 |
if user_feedback_message and not success:
|
739 |
+
logger.warning(f"Sending failure feedback to user: {user_feedback_message}")
|
740 |
await context.bot.send_message(chat_id=user.id, text=user_feedback_message)
|
741 |
|
742 |
except Exception as e:
|
|
|
751 |
finally:
|
752 |
# --- Cleanup ---
|
753 |
# Delete the "Processing..." status message or the original message with buttons
|
754 |
+
logger.debug("Cleaning up status message...")
|
755 |
try:
|
756 |
if message_to_delete_later: # If we sent a separate status message
|
757 |
await context.bot.delete_message(chat_id=user.id, message_id=message_to_delete_later.message_id)
|
758 |
+
logger.debug("Deleted separate status message.")
|
759 |
elif query: # Otherwise, delete the original message with the buttons
|
|
|
760 |
await query.delete_message()
|
761 |
+
logger.debug(f"Deleted original message for query {query.id}.")
|
762 |
except Exception as del_e:
|
763 |
# Log if deletion fails, but don't let it stop anything
|
764 |
logger.warning(f"Could not delete status/button message: {del_e}")
|
|
|
768 |
"""Log Errors caused by Updates."""
|
769 |
# Log the error and traceback
|
770 |
logger.error(f"Exception while handling an update: {context.error}", exc_info=context.error)
|
771 |
+
# (Keep optional developer notification code as before if desired)
|
772 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
773 |
|
774 |
# --- Bot Application Setup Function ---
|
775 |
async def setup_bot():
|
|
|
799 |
|
800 |
# --- Run Setup and Store Application Instance ---
|
801 |
logger.info("Running bot setup...")
|
|
|
|
|
802 |
ptb_app = asyncio.run(setup_bot())
|
803 |
logger.info(f"Bot setup finished. Application instance: {'OK' if ptb_app else 'Failed'}")
|
804 |
|