fmab777 commited on
Commit
9412ba0
Β·
verified Β·
1 Parent(s): 1bca705

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +44 -31
main.py CHANGED
@@ -1,4 +1,4 @@
1
- # main.py (Fixing get_secret for optional secrets)
2
  import os
3
  import re
4
  import logging
@@ -41,10 +41,7 @@ except ImportError:
41
  DEFAULT_PARSER = 'html.parser'
42
 
43
  # --- Logging Setup ---
44
- logging.basicConfig(
45
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
46
- level=logging.INFO
47
- )
48
  logging.getLogger("httpx").setLevel(logging.WARNING)
49
  logging.getLogger("telegram.ext").setLevel(logging.INFO)
50
  logging.getLogger('telegram.bot').setLevel(logging.INFO)
@@ -61,17 +58,9 @@ ptb_app: Optional[Application] = None
61
  # --- Environment Variable Loading & Configuration ---
62
  logger.info("Attempting to load secrets and configuration...")
63
  def get_secret(secret_name):
64
- """Gets a secret from environment variables and logs status safely."""
65
  value = os.environ.get(secret_name)
66
- if value:
67
- status = "Found"
68
- # Safely log the beginning of the value
69
- log_length = min(len(value), 8)
70
- value_start = value[:log_length]
71
- logger.info(f"Secret '{secret_name}': {status} (Value starts with: {value_start}...)")
72
- else:
73
- status = "Not Found"
74
- logger.warning(f"Secret '{secret_name}': {status}") # Changed to warning for not found
75
  return value
76
 
77
  TELEGRAM_TOKEN = get_secret('TELEGRAM_TOKEN')
@@ -79,20 +68,18 @@ OPENROUTER_API_KEY = get_secret('OPENROUTER_API_KEY')
79
  URLTOTEXT_API_KEY = get_secret('URLTOTEXT_API_KEY')
80
  SUPADATA_API_KEY = get_secret('SUPADATA_API_KEY')
81
  APIFY_API_TOKEN = get_secret('APIFY_API_TOKEN')
82
- WEBHOOK_SECRET = get_secret('WEBHOOK_SECRET') # This will now correctly return None if not set, without erroring
83
 
84
  OPENROUTER_MODEL = os.environ.get("OPENROUTER_MODEL", "deepseek/deepseek-chat-v3-0324:free")
85
  APIFY_ACTOR_ID = os.environ.get("APIFY_ACTOR_ID", "karamelo/youtube-transcripts")
86
 
87
- # Critical checks remain the same
88
  if not TELEGRAM_TOKEN: logger.critical("❌ FATAL: TELEGRAM_TOKEN not found."); raise RuntimeError("Exiting: Telegram token missing.")
89
  if not OPENROUTER_API_KEY: logger.error("❌ ERROR: OPENROUTER_API_KEY not found. Summarization will fail.")
90
 
91
- # Optional checks remain the same (logging handled by get_secret)
92
- if not URLTOTEXT_API_KEY: pass # Logged as warning by get_secret
93
- if not SUPADATA_API_KEY: pass # Logged as warning by get_secret
94
- if not APIFY_API_TOKEN: pass # Logged as warning by get_secret
95
- if not WEBHOOK_SECRET: logger.info("Optional secret 'WEBHOOK_SECRET' not found. Webhook security disabled.") # Explicit info log
96
 
97
  logger.info("Secret loading and configuration check finished.")
98
  logger.info(f"Using OpenRouter Model: {OPENROUTER_MODEL}")
@@ -101,9 +88,7 @@ logger.info(f"Using Apify Actor (via REST): {APIFY_ACTOR_ID}")
101
  _apify_token_exists = bool(APIFY_API_TOKEN)
102
 
103
  # --- Retry Decorator ---
104
- @retry( stop=stop_after_attempt(4), wait=wait_exponential(multiplier=1, min=2, max=15),
105
- retry=retry_if_exception_type((NetworkError, RetryAfter, TimedOut, BadRequest)),
106
- before_sleep=before_sleep_log(logger, logging.WARNING), reraise=True )
107
  async def retry_bot_operation(func, *args, **kwargs):
108
  try: return await func(*args, **kwargs)
109
  except BadRequest as e:
@@ -295,12 +280,29 @@ async def generate_summary(text: str, summary_type: str) -> str:
295
  if len(text) > MAX_INPUT_LENGTH: logger.warning(f"Input length ({len(text)}) exceeds limit ({MAX_INPUT_LENGTH}). Truncating."); text = text[:MAX_INPUT_LENGTH] + "... (Content truncated)"
296
  full_prompt = f"{prompt}\n\n{text}"
297
  headers = { "Authorization": f"Bearer {OPENROUTER_API_KEY}", "Content-Type": "application/json" }; payload = { "model": OPENROUTER_MODEL, "messages": [{"role": "user", "content": full_prompt}] }; openrouter_api_endpoint = "https://openrouter.ai/api/v1/chat/completions"
 
 
 
 
 
 
298
  try:
299
- async with httpx.AsyncClient(timeout=60.0) as client:
300
- logger.debug(f"Sending request to OpenRouter ({OPENROUTER_MODEL})..."); response = await client.post(openrouter_api_endpoint, headers=headers, json=payload); logger.debug(f"Received status {response.status_code} from OpenRouter.")
 
 
 
 
 
 
 
 
 
 
301
  if response.status_code == 200:
302
  try:
303
  data = response.json()
 
304
  if data.get("choices") and isinstance(data["choices"], list) and len(data["choices"]) > 0:
305
  message = data["choices"][0].get("message")
306
  if message and isinstance(message, dict):
@@ -311,14 +313,25 @@ async def generate_summary(text: str, summary_type: str) -> str:
311
  else: logger.error(f"Unexpected choices structure in OpenRouter resp: {data.get('choices')}. Full: {data}"); return "Sorry, could not parse AI response (choices)."
312
  except json.JSONDecodeError: logger.error(f"Failed JSON decode OpenRouter. Status:{response.status_code}. Resp:{response.text[:500]}"); return "Sorry, failed to understand AI response."
313
  except Exception as e: logger.error(f"Error processing OpenRouter success response: {e}", exc_info=True); return "Sorry, error processing AI response."
 
314
  elif response.status_code == 401: logger.error("OpenRouter API key invalid (401)."); return "Error: AI model configuration key is invalid."
315
  elif response.status_code == 402: logger.error("OpenRouter Payment Required (402)."); return "Sorry, AI service limits/payment issue."
316
  elif response.status_code == 429: logger.warning("OpenRouter Rate Limit Exceeded (429)."); return "Sorry, AI model is busy. Try again."
317
  elif response.status_code == 500: logger.error(f"OpenRouter Internal Server Error (500). Resp:{response.text[:500]}"); return "Sorry, AI service internal error."
318
  else: logger.error(f"Unexpected status {response.status_code} from OpenRouter. Resp:{response.text[:500]}"); return f"Sorry, AI service returned unexpected status ({response.status_code})."
319
- except httpx.TimeoutException: logger.error("Timeout connecting to OpenRouter API."); return "Sorry, request to AI model timed out."
320
- except httpx.RequestError as e: logger.error(f"Request error connecting to OpenRouter API: {e}"); return "Sorry, error connecting to AI service."
321
- except Exception as e: logger.error(f"Unexpected error in generate_summary (OpenRouter): {e}", exc_info=True); return "Sorry, unexpected error generating summary."
 
 
 
 
 
 
 
 
 
 
322
 
323
  # --- Background Task Processing ---
324
  async def process_summary_task(
@@ -482,7 +495,7 @@ async def lifespan(app: Starlette):
482
  except Exception as e: logger.warning(f"Could not delete webhook: {e}"); await asyncio.sleep(1)
483
  space_host = os.environ.get("SPACE_HOST"); webhook_path = "/webhook"; full_webhook_url = None
484
  if space_host:
485
- protocol = "https://"; host = space_host.split('://')[-1]; full_webhook_url = f"{protocol}{host.rstrip('/')}{webhook_path}"
486
  if full_webhook_url:
487
  logger.info(f"Setting webhook: {full_webhook_url}"); set_webhook_args = { "url": full_webhook_url, "allowed_updates": Update.ALL_TYPES, "drop_pending_updates": True }
488
  if WEBHOOK_SECRET: set_webhook_args["secret_token"] = WEBHOOK_SECRET; logger.info("Using webhook secret.")
 
1
+ # main.py (Increasing timeout and adding logging in generate_summary)
2
  import os
3
  import re
4
  import logging
 
41
  DEFAULT_PARSER = 'html.parser'
42
 
43
  # --- Logging Setup ---
44
+ logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO )
 
 
 
45
  logging.getLogger("httpx").setLevel(logging.WARNING)
46
  logging.getLogger("telegram.ext").setLevel(logging.INFO)
47
  logging.getLogger('telegram.bot').setLevel(logging.INFO)
 
58
  # --- Environment Variable Loading & Configuration ---
59
  logger.info("Attempting to load secrets and configuration...")
60
  def get_secret(secret_name):
 
61
  value = os.environ.get(secret_name)
62
+ if value: status = "Found"; log_length = min(len(value), 8); value_start = value[:log_length]; logger.info(f"Secret '{secret_name}': {status} (Value starts with: {value_start}...)")
63
+ else: status = "Not Found"; logger.warning(f"Secret '{secret_name}': {status}")
 
 
 
 
 
 
 
64
  return value
65
 
66
  TELEGRAM_TOKEN = get_secret('TELEGRAM_TOKEN')
 
68
  URLTOTEXT_API_KEY = get_secret('URLTOTEXT_API_KEY')
69
  SUPADATA_API_KEY = get_secret('SUPADATA_API_KEY')
70
  APIFY_API_TOKEN = get_secret('APIFY_API_TOKEN')
71
+ WEBHOOK_SECRET = get_secret('WEBHOOK_SECRET')
72
 
73
  OPENROUTER_MODEL = os.environ.get("OPENROUTER_MODEL", "deepseek/deepseek-chat-v3-0324:free")
74
  APIFY_ACTOR_ID = os.environ.get("APIFY_ACTOR_ID", "karamelo/youtube-transcripts")
75
 
 
76
  if not TELEGRAM_TOKEN: logger.critical("❌ FATAL: TELEGRAM_TOKEN not found."); raise RuntimeError("Exiting: Telegram token missing.")
77
  if not OPENROUTER_API_KEY: logger.error("❌ ERROR: OPENROUTER_API_KEY not found. Summarization will fail.")
78
 
79
+ if not URLTOTEXT_API_KEY: pass
80
+ if not SUPADATA_API_KEY: pass
81
+ if not APIFY_API_TOKEN: pass
82
+ if not WEBHOOK_SECRET: logger.info("Optional secret 'WEBHOOK_SECRET' not found. Webhook security disabled.")
 
83
 
84
  logger.info("Secret loading and configuration check finished.")
85
  logger.info(f"Using OpenRouter Model: {OPENROUTER_MODEL}")
 
88
  _apify_token_exists = bool(APIFY_API_TOKEN)
89
 
90
  # --- Retry Decorator ---
91
+ @retry( stop=stop_after_attempt(4), wait=wait_exponential(multiplier=1, min=2, max=15), retry=retry_if_exception_type((NetworkError, RetryAfter, TimedOut, BadRequest)), before_sleep=before_sleep_log(logger, logging.WARNING), reraise=True )
 
 
92
  async def retry_bot_operation(func, *args, **kwargs):
93
  try: return await func(*args, **kwargs)
94
  except BadRequest as e:
 
280
  if len(text) > MAX_INPUT_LENGTH: logger.warning(f"Input length ({len(text)}) exceeds limit ({MAX_INPUT_LENGTH}). Truncating."); text = text[:MAX_INPUT_LENGTH] + "... (Content truncated)"
281
  full_prompt = f"{prompt}\n\n{text}"
282
  headers = { "Authorization": f"Bearer {OPENROUTER_API_KEY}", "Content-Type": "application/json" }; payload = { "model": OPENROUTER_MODEL, "messages": [{"role": "user", "content": full_prompt}] }; openrouter_api_endpoint = "https://openrouter.ai/api/v1/chat/completions"
283
+
284
+ # *** FIX: Increase timeout and add logging ***
285
+ # Set a longer timeout (e.g., 180 seconds = 3 minutes)
286
+ api_timeout = 180.0
287
+ response = None # Initialize response variable
288
+
289
  try:
290
+ async with httpx.AsyncClient(timeout=api_timeout) as client:
291
+ logger.info(f"Sending request to OpenRouter ({OPENROUTER_MODEL}) with timeout {api_timeout}s...")
292
+ response = await client.post(openrouter_api_endpoint, headers=headers, json=payload)
293
+ # Check response immediately after await returns
294
+ if response:
295
+ logger.info(f"Received response from OpenRouter. Status code: {response.status_code}")
296
+ else:
297
+ # This case should technically not happen if await returns, but good for debugging
298
+ logger.error("No response received from OpenRouter after await completed (unexpected).")
299
+ return "Sorry, communication with the AI service failed unexpectedly."
300
+
301
+ # Process the response (status code check and JSON parsing)
302
  if response.status_code == 200:
303
  try:
304
  data = response.json()
305
+ # ... (rest of the success processing logic remains the same)
306
  if data.get("choices") and isinstance(data["choices"], list) and len(data["choices"]) > 0:
307
  message = data["choices"][0].get("message")
308
  if message and isinstance(message, dict):
 
313
  else: logger.error(f"Unexpected choices structure in OpenRouter resp: {data.get('choices')}. Full: {data}"); return "Sorry, could not parse AI response (choices)."
314
  except json.JSONDecodeError: logger.error(f"Failed JSON decode OpenRouter. Status:{response.status_code}. Resp:{response.text[:500]}"); return "Sorry, failed to understand AI response."
315
  except Exception as e: logger.error(f"Error processing OpenRouter success response: {e}", exc_info=True); return "Sorry, error processing AI response."
316
+ # ... (rest of the error status code handling remains the same)
317
  elif response.status_code == 401: logger.error("OpenRouter API key invalid (401)."); return "Error: AI model configuration key is invalid."
318
  elif response.status_code == 402: logger.error("OpenRouter Payment Required (402)."); return "Sorry, AI service limits/payment issue."
319
  elif response.status_code == 429: logger.warning("OpenRouter Rate Limit Exceeded (429)."); return "Sorry, AI model is busy. Try again."
320
  elif response.status_code == 500: logger.error(f"OpenRouter Internal Server Error (500). Resp:{response.text[:500]}"); return "Sorry, AI service internal error."
321
  else: logger.error(f"Unexpected status {response.status_code} from OpenRouter. Resp:{response.text[:500]}"); return f"Sorry, AI service returned unexpected status ({response.status_code})."
322
+
323
+ except httpx.TimeoutException:
324
+ logger.error(f"Timeout error ({api_timeout}s) connecting to OpenRouter API.")
325
+ return f"Sorry, the request to the AI model timed out after {api_timeout} seconds. The content might be too long or the service busy. Please try again later or with shorter content."
326
+ except httpx.RequestError as e:
327
+ logger.error(f"Request error connecting to OpenRouter API: {e}")
328
+ return "Sorry, there was an error connecting to the AI model service."
329
+ except Exception as e:
330
+ # Catch any other unexpected errors during the request/response cycle
331
+ logger.error(f"Unexpected error in generate_summary (OpenRouter request phase): {e}", exc_info=True)
332
+ # Log response status if available
333
+ if response: logger.error(f"--> Last response status before error: {response.status_code}")
334
+ return "Sorry, an unexpected error occurred while trying to generate the summary."
335
 
336
  # --- Background Task Processing ---
337
  async def process_summary_task(
 
495
  except Exception as e: logger.warning(f"Could not delete webhook: {e}"); await asyncio.sleep(1)
496
  space_host = os.environ.get("SPACE_HOST"); webhook_path = "/webhook"; full_webhook_url = None
497
  if space_host:
498
+ protocol = "https"; host = space_host.split('://')[-1]; full_webhook_url = f"{protocol}://{host.rstrip('/')}{webhook_path}" # Corrected URL construction
499
  if full_webhook_url:
500
  logger.info(f"Setting webhook: {full_webhook_url}"); set_webhook_args = { "url": full_webhook_url, "allowed_updates": Update.ALL_TYPES, "drop_pending_updates": True }
501
  if WEBHOOK_SECRET: set_webhook_args["secret_token"] = WEBHOOK_SECRET; logger.info("Using webhook secret.")