From ddedd4867cb4d8fa009fa4559b1307adb149bb31 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 6 Jan 2026 15:58:46 +0000 Subject: [PATCH] Add comprehensive credential validation and troubleshooting tools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds robust diagnostic tools to help users identify and fix authentication issues with Twitch OAuth and Perplexity API. New Features: - test_credentials.py: Comprehensive credential validator with detailed diagnostics - Tests Twitch OAuth token with IRC authentication - Validates Perplexity API key and model access - Provides specific error messages and actionable fixes - Automatically tests fallback models (sonar-pro → sonar) - TROUBLESHOOTING.md: Complete troubleshooting guide - Common error messages and solutions - Step-by-step diagnostic procedures - Quick reference for file locations and commands Improvements to setup_wizard.py: - Enhanced Twitch token validation with detailed error messages - Added Perplexity model fallback (sonar-pro → sonar) - Better error handling with specific solutions - Recommends working model if primary model unavailable Documentation Updates: - README.md: Added credential testing section with examples - CLAUDE.md: Updated with new tools and testing procedures - Comprehensive troubleshooting section in README This addresses the common authentication failures users encounter during initial setup and provides clear paths to resolution. --- CLAUDE.md | 24 +++- README.md | 76 ++++++++-- TROUBLESHOOTING.md | 279 ++++++++++++++++++++++++++++++++++++ setup_wizard.py | 145 +++++++++++++------ test_credentials.py | 336 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 807 insertions(+), 53 deletions(-) create mode 100644 TROUBLESHOOTING.md create mode 100644 test_credentials.py diff --git a/CLAUDE.md b/CLAUDE.md index d484d94..b8594a8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,6 +28,8 @@ ``` eugen/ ├── chatbot.py # Main entry point +├── setup_wizard.py # Interactive setup wizard +├── test_credentials.py # Credential validation tool ├── config.py # Configuration management ├── gui.py # Dashboard GUI ├── ai_provider.py # Perplexity API integration @@ -102,12 +104,32 @@ python -m venv venv # Install dependencies pip install -r requirements.txt +# Run interactive setup wizard +python setup_wizard.py + +# Test credentials (recommended before first run) +python test_credentials.py + # Run the bot python chatbot.py ``` -## Testing API Keys +## Testing Credentials +Use the credential validator for comprehensive testing: + +```bash +python test_credentials.py +``` + +This tool: +- Tests Twitch OAuth token with IRC authentication +- Validates Perplexity API key and model access +- Provides detailed error diagnostics +- Suggests fixes for common issues +- Automatically tests fallback models (sonar-pro → sonar) + +Manual testing: - **Twitch**: Test IRC connection to irc.chat.twitch.tv:6667 - **Perplexity**: POST to https://api.perplexity.ai/chat/completions with a simple test message diff --git a/README.md b/README.md index 16ca109..f9b930e 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,27 @@ PERPLEXITY_API_KEY=pplx-your_key_here 3. Generate new API key 4. Copy the key (should start with `pplx-`) +### Testing Credentials (Recommended) + +Before running the bot, test your credentials: + +```bash +python test_credentials.py +``` + +This will: +- Validate your Twitch OAuth token with detailed diagnostics +- Test your Perplexity API key and check model access +- Provide specific error messages and fixes for common issues +- Automatically detect if `sonar-pro` is unavailable and suggest `sonar` fallback + +**Example output:** +``` +✓ Twitch IRC: PASS +✓ Perplexity API: PASS +🎉 All credentials valid! Bot is ready to run. +``` + ### Running the Bot ```bash @@ -105,6 +126,8 @@ Eugen: @User For bed leveling, start by... ``` eugen/ ├── chatbot.py # Main entry point +├── setup_wizard.py # Interactive setup tool +├── test_credentials.py # Credential validator ├── config.py # Configuration management ├── gui.py # Dashboard GUI ├── ai_provider.py # Perplexity API integration @@ -135,20 +158,51 @@ The bot is built with: ## Troubleshooting -**Bot doesn't respond:** -- Check that .env has correct OAuth token and API key -- Verify bot is in the correct channel -- Check logs/eugen.log for errors +### Quick Diagnosis -**API errors:** -- Verify Perplexity API key is valid -- Check your API credits at perplexity.ai -- Enable DEBUG_MODE=true in .env for detailed logs +**First step: Run the credential validator** +```bash +python test_credentials.py +``` + +This will identify exactly what's wrong and provide specific fixes. + +### Common Issues + +**Twitch OAuth Token Invalid:** +- ✗ Error: `Login authentication failed` +- **Fix:** + 1. Go to https://twitchtokengenerator.com + 2. Select "Bot Chat Token" + 3. Make sure you're logged in as the bot account + 4. Copy the new token (including `oauth:` prefix) + 5. Update `TWITCH_OAUTH_TOKEN` in `.env` + +**Bot nickname mismatch:** +- Token must be generated by the same account as `TWITCH_BOT_NICKNAME` +- If bot nickname is `EugenBot`, you must be logged into Twitch as `EugenBot` when generating the token + +**Perplexity Model Not Available:** +- ✗ Error: `400 Bad Request` or model not accessible +- **Fix:** + - Your API key might not have access to `sonar-pro` + - The setup wizard will automatically suggest `sonar` as fallback + - Or manually change `PERPLEXITY_MODEL=sonar` in `.env` + +**Bot doesn't respond in chat:** +- Run `python test_credentials.py` to verify credentials +- Check that bot is in the correct channel +- Check `logs/eugen.log` for errors +- Verify bot was mentioned correctly (`@Eugen` or `Eugen:`) **IRC connection failed:** -- Verify internet connection -- Check firewall settings for port 6667 -- Regenerate Twitch OAuth token if expired +- Check internet connection +- Verify firewall allows port 6667 +- Run credential validator for detailed diagnostics + +**API rate limits:** +- Check your Perplexity API credits at https://www.perplexity.ai/settings/api +- Enable `DEBUG_MODE=true` in `.env` for detailed API logs ## License diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md new file mode 100644 index 0000000..d75f45d --- /dev/null +++ b/TROUBLESHOOTING.md @@ -0,0 +1,279 @@ +# Eugen Bot - Troubleshooting Guide + +Quick reference for diagnosing and fixing common issues with the Eugen Twitch bot. + +## Quick Start Diagnostic Tool + +**Before anything else**, run the credential validator: + +```bash +python test_credentials.py +``` + +This tool will: +- Test your Twitch OAuth token +- Validate your Perplexity API key +- Check model availability (sonar-pro vs sonar) +- Provide specific error messages +- Suggest exact fixes + +--- + +## Common Error Messages + +### 1. Twitch IRC Authentication Failed + +**Error Output:** +``` +IRC EVENT: privnotice | Source: tmi.twitch.tv | Args: ['Login authentication failed'] +``` + +**Causes:** +- Token is expired or invalid +- Token doesn't match the bot nickname +- Bot account is banned/suspended + +**Solution:** + +1. **Verify Bot Nickname Match** + - If `TWITCH_BOT_NICKNAME=Eugen`, you must generate the token while logged into Twitch as `Eugen` + - Token and bot nickname MUST match the same account + +2. **Generate New Token** + ``` + 1. Go to: https://twitchtokengenerator.com + 2. Log into Twitch as the bot account + 3. Select "Bot Chat Token" + 4. Copy the full token (including oauth: prefix) + 5. Update .env file: + TWITCH_OAUTH_TOKEN=oauth:your_new_token_here + ``` + +3. **Test the Token** + ```bash + python test_credentials.py + ``` + +### 2. Perplexity API 400 Bad Request + +**Error Output:** +``` +⚠ Unerwartete Antwort: 400 +``` + +**Causes:** +- API key doesn't have access to `sonar-pro` model +- Invalid request format +- Model not available on your plan + +**Solution:** + +1. **Use Model Fallback** + - The setup wizard automatically tries `sonar` if `sonar-pro` fails + - Manually update `.env`: + ``` + PERPLEXITY_MODEL=sonar + ``` + +2. **Verify API Key** + ```bash + python test_credentials.py + ``` + +3. **Check API Credits** + - Go to: https://www.perplexity.ai/settings/api + - Verify your account has credits + - Check API key is active + +### 3. Perplexity API 401 Unauthorized + +**Error Output:** +``` +✗ Perplexity API-Key ist ungültig (401 Unauthorized) +``` + +**Causes:** +- Invalid API key +- API key was revoked +- Typo in `.env` file + +**Solution:** + +1. **Generate New API Key** + ``` + 1. Go to: https://www.perplexity.ai/settings/api + 2. Create new API key + 3. Copy the key (starts with pplx-) + 4. Update .env: + PERPLEXITY_API_KEY=pplx-your_new_key_here + ``` + +2. **Verify No Extra Spaces** + - Check `.env` file has no spaces around the `=` sign + - Format: `PERPLEXITY_API_KEY=pplx-abc123` (no spaces) + +### 4. Bot Doesn't Respond in Chat + +**Symptoms:** +- Bot connects successfully +- Bot joins channel +- But doesn't respond to mentions + +**Checklist:** + +1. **Verify Bot is Mentioned Correctly** + ``` + ✓ @Eugen how are you? + ✓ Eugen: tell me about WoW + ✓ Eugen, what's new? + ✗ eugen (lowercase doesn't work) + ``` + +2. **Check Debug Logs** + ```bash + tail -f logs/eugen.log + ``` + Look for: + - `Mention detected from {user}` + - `API call to Perplexity` + - Any error messages + +3. **Enable Debug Mode** + ``` + DEBUG_MODE=true + ``` + Restart the bot and check logs again + +4. **Test Credentials** + ```bash + python test_credentials.py + ``` + +### 5. Connection Reset by Peer + +**Error Output:** +``` +IRC EVENT: disconnect | Args: ['Connection reset by peer'] +``` + +**Causes:** +- Network interruption +- Firewall blocking port 6667 +- Invalid authentication (see #1) + +**Solution:** + +1. **Check Firewall** + - Allow outbound connections on port 6667 + - Try temporarily disabling firewall to test + +2. **Verify Internet Connection** + ```bash + ping irc.chat.twitch.tv + ``` + +3. **Check OAuth Token** (most common cause) + ```bash + python test_credentials.py + ``` + +### 6. Rate Limiting / API Quota Exceeded + +**Error Output:** +``` +429 Too Many Requests +``` + +**Causes:** +- Too many API calls in short time +- Perplexity API quota exceeded + +**Solution:** + +1. **Check API Usage** + - Go to: https://www.perplexity.ai/settings/api + - View usage and limits + +2. **Reduce Request Frequency** + - Consider implementing cooldowns + - Limit responses to specific users + +3. **Wait and Retry** + - Wait a few minutes + - Restart the bot + +--- + +## Diagnostic Commands + +### Check Configuration +```bash +cat .env +``` + +### View Recent Logs +```bash +tail -n 50 logs/eugen.log +``` + +### View API Debug Logs +```bash +tail -n 50 logs/api_debug.log +``` + +### Test Credentials +```bash +python test_credentials.py +``` + +### Run Setup Wizard Again +```bash +python setup_wizard.py +``` + +--- + +## Getting Help + +### Before Asking for Help + +1. Run `python test_credentials.py` +2. Check `logs/eugen.log` +3. Enable `DEBUG_MODE=true` and restart +4. Review this troubleshooting guide + +### Information to Include + +When reporting issues, include: +- Error messages from logs +- Output of `python test_credentials.py` +- Contents of `.env` (REDACT secrets!) +- Python version: `python --version` +- OS: Windows/Linux/Mac + +### Resources + +- **GitHub Issues**: https://github.com/Kenearos/KI-Chat-Bot-Eugen/issues +- **Twitch Token Generator**: https://twitchtokengenerator.com +- **Perplexity API**: https://www.perplexity.ai/settings/api + +--- + +## Quick Reference + +### File Locations +- Configuration: `.env` +- Main log: `logs/eugen.log` +- API debug: `logs/api_debug.log` +- Conversations: `data/conversations/` + +### Required Credentials +- Twitch OAuth Token (starts with `oauth:`) +- Twitch Bot Nickname (must match token account) +- Twitch Channel (starts with `#`) +- Perplexity API Key (starts with `pplx-`) + +### Testing Tools +- `python setup_wizard.py` - Interactive setup +- `python test_credentials.py` - Validate credentials +- `python chatbot.py` - Run the bot diff --git a/setup_wizard.py b/setup_wizard.py index f779cea..d823012 100644 --- a/setup_wizard.py +++ b/setup_wizard.py @@ -69,20 +69,47 @@ async def validate_twitch_token(token, bot_nickname): try: print_info("Validiere Twitch-Verbindung...") with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: - sock.settimeout(5) - sock.connect(('irc.chat.twitch.tv', 6667)) + sock.settimeout(10) + + # Connect + try: + sock.connect(('irc.chat.twitch.tv', 6667)) + except socket.timeout: + print_warning("Verbindung zu Twitch IRC hat zu lange gedauert") + print_info("Prüfe deine Internetverbindung und Firewall-Einstellungen") + print_info("Fahre trotzdem fort - bitte später manuell prüfen!") + return True # Send authentication sock.send(f"PASS {token}\r\n".encode()) sock.send(f"NICK {bot_nickname}\r\n".encode()) - response = sock.recv(1024).decode() + # Receive response + response = sock.recv(2048).decode() if "Login authentication failed" in response: print_error("Twitch OAuth Token ist ungültig!") + print_info("\nMögliche Gründe:") + print(f" • Token ist abgelaufen oder ungültig") + print(f" • Token passt nicht zum Bot-Namen '{bot_nickname}'") + print(f" • Account ist gesperrt oder eingeschränkt") + print_info("\nLösung:") + print(f" → Gehe zu: https://twitchtokengenerator.com") + print(f" → Stelle sicher, dass du als '{bot_nickname}' eingeloggt bist") + print(f" → Generiere einen neuen 'Bot Chat Token'") return False - print_success("Twitch-Verbindung erfolgreich!") + if ":tmi.twitch.tv 001" in response or "Welcome" in response: + print_success("Twitch-Verbindung erfolgreich!") + return True + + # Ambiguous - probably OK + if "authentication failed" not in response.lower(): + print_success("Twitch-Verbindung scheinbar erfolgreich (kein Fehler erkannt)") + return True + + print_warning(f"Unerwartete Antwort von Twitch IRC") + print_info("Fahre trotzdem fort - bitte später manuell prüfen!") return True except Exception as e: @@ -93,8 +120,8 @@ async def validate_twitch_token(token, bot_nickname): async def validate_perplexity_key(api_key, model="sonar-pro"): """ - Validate Perplexity API key with test request - + Validate Perplexity API key with test request and model fallback + Note: Validation uses the specified model (default: sonar-pro). If your API key doesn't have access to this model, validation may fail even with a valid key for other models. @@ -104,40 +131,73 @@ async def validate_perplexity_key(api_key, model="sonar-pro"): model (str): Model to test with (defaults to sonar-pro) Returns: - tuple: (bool success, bool should_retry) - success indicates if validation passed, - should_retry indicates if user should be prompted to retry + tuple: (bool success, bool should_retry, str recommended_model) + success indicates if validation passed, + should_retry indicates if user should be prompted to retry, + recommended_model is the working model if different from requested """ - try: - print_info(f"Validiere Perplexity API-Key mit Modell '{model}'...") - async with httpx.AsyncClient(timeout=10.0) as client: - response = await client.post( - "https://api.perplexity.ai/chat/completions", - json={ - "model": model, - "messages": [{"role": "user", "content": "test"}], - "max_tokens": 10 - }, - headers={ - "Authorization": f"Bearer {api_key}", - "Content-Type": "application/json" - } - ) + # Test with multiple models + models_to_test = [model] + if model != "sonar": + models_to_test.append("sonar") - if response.status_code == 200: - print_success("Perplexity API-Key ist gültig!") - return (True, False) - elif response.status_code == 401: - print_error("Perplexity API-Key ist ungültig!") - return (False, True) - else: - print_warning(f"Unerwartete Antwort: {response.status_code}") - print_info("Fahre trotzdem fort - bitte später manuell prüfen!") - return (True, False) + for test_model in models_to_test: + try: + print_info(f"Validiere Perplexity API-Key mit Modell '{test_model}'...") + async with httpx.AsyncClient(timeout=10.0) as client: + response = await client.post( + "https://api.perplexity.ai/chat/completions", + json={ + "model": test_model, + "messages": [{"role": "user", "content": "test"}], + "max_tokens": 10 + }, + headers={ + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json" + } + ) - except Exception as e: - print_warning(f"Konnte Perplexity API nicht testen: {e}") - print_info("Fahre trotzdem fort - bitte später manuell prüfen!") - return (True, False) + if response.status_code == 200: + print_success(f"Perplexity API-Key ist gültig mit Modell '{test_model}'!") + if test_model != model: + print_warning(f"Hinweis: Modell '{model}' nicht verfügbar, verwende '{test_model}'") + return (True, False, test_model) + elif response.status_code == 401: + print_error("Perplexity API-Key ist ungültig (401 Unauthorized)!") + print_info("Bitte überprüfe deinen API-Key auf: https://www.perplexity.ai/settings/api") + return (False, True, None) + elif response.status_code == 400: + try: + error_data = response.json() + error_msg = error_data.get("error", {}).get("message", "Unbekannter Fehler") + + # Check if model access issue + if "model" in error_msg.lower() and test_model != models_to_test[-1]: + print_warning(f"Modell '{test_model}' nicht verfügbar: {error_msg}") + print_info("Versuche Fallback-Modell...") + continue + else: + print_warning(f"400 Fehler: {error_msg}") + print_info("Fahre trotzdem fort - bitte später manuell prüfen!") + return (True, False, model) + except: + print_warning(f"Unerwartete Antwort: {response.status_code}") + print_info("Fahre trotzdem fort - bitte später manuell prüfen!") + return (True, False, model) + else: + print_warning(f"Unerwartete Antwort: {response.status_code}") + print_info("Fahre trotzdem fort - bitte später manuell prüfen!") + return (True, False, model) + + except Exception as e: + print_warning(f"Konnte Perplexity API nicht testen: {e}") + print_info("Fahre trotzdem fort - bitte später manuell prüfen!") + return (True, False, model) + + # All models failed + print_error("Alle Modelle fehlgeschlagen!") + return (False, True, None) def create_env_file(config): @@ -279,14 +339,17 @@ async def run_wizard(): print(" → Kopiere den Key (beginnt mit 'pplx-...')") print() + recommended_model = "sonar-pro" while True: api_key = get_input("Perplexity API Key", secret=True, required=True) config['PERPLEXITY_API_KEY'] = api_key - success, should_retry = await validate_perplexity_key(api_key) + success, should_retry, working_model = await validate_perplexity_key(api_key, "sonar-pro") if success: + if working_model: + recommended_model = working_model break - + if should_retry: retry = input("\nErneut versuchen? (j/n): ") if retry.lower() != 'j': @@ -302,7 +365,7 @@ async def run_wizard(): if advanced: config['PERPLEXITY_MODEL'] = get_input( "Perplexity Modell", - default="sonar-pro" + default=recommended_model ) config['MAX_TOKENS'] = get_input( "Max Tokens pro Antwort", @@ -317,7 +380,7 @@ async def run_wizard(): default="1" ) else: - config['PERPLEXITY_MODEL'] = "sonar-pro" + config['PERPLEXITY_MODEL'] = recommended_model config['MAX_TOKENS'] = "450" config['DEBUG_MODE'] = "false" config['CONTEXT_RETENTION_HOURS'] = "1" diff --git a/test_credentials.py b/test_credentials.py new file mode 100644 index 0000000..7f07820 --- /dev/null +++ b/test_credentials.py @@ -0,0 +1,336 @@ +""" +Credential Testing Utility for Eugen Bot +Tests and validates Twitch OAuth tokens and Perplexity API keys +""" +import asyncio +import socket +import sys +import os +import httpx +from dotenv import load_dotenv + + +class Colors: + """ANSI color codes""" + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKCYAN = '\033[96m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + + +def print_header(text): + """Print colored header""" + print(f"\n{Colors.HEADER}{Colors.BOLD}{'='*70}{Colors.ENDC}") + print(f"{Colors.HEADER}{Colors.BOLD}{text.center(70)}{Colors.ENDC}") + print(f"{Colors.HEADER}{Colors.BOLD}{'='*70}{Colors.ENDC}\n") + + +def print_success(text): + """Print success message""" + print(f"{Colors.OKGREEN}✓ {text}{Colors.ENDC}") + + +def print_error(text): + """Print error message""" + print(f"{Colors.FAIL}✗ {text}{Colors.ENDC}") + + +def print_info(text): + """Print info message""" + print(f"{Colors.OKCYAN}ℹ {text}{Colors.ENDC}") + + +def print_warning(text): + """Print warning message""" + print(f"{Colors.WARNING}⚠ {text}{Colors.ENDC}") + + +async def test_twitch_token(token, nickname): + """ + Test Twitch OAuth token with detailed diagnostics + + Args: + token (str): OAuth token + nickname (str): Bot nickname + + Returns: + bool: True if valid + """ + print_header("TESTING TWITCH CREDENTIALS") + + # Check token format + if not token: + print_error("Token is empty!") + print_info("Get a token from: https://twitchtokengenerator.com") + return False + + if not token.startswith("oauth:"): + print_warning(f"Token doesn't start with 'oauth:' (got: {token[:10]}...)") + print_info("Token should look like: oauth:abcd1234...") + return False + + print_info(f"Token format: ✓ Starts with 'oauth:'") + print_info(f"Token length: {len(token)} characters") + print_info(f"Bot nickname: {nickname}") + + # Test IRC connection + print_info("\nConnecting to Twitch IRC (irc.chat.twitch.tv:6667)...") + + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.settimeout(10) + + # Connect + try: + sock.connect(('irc.chat.twitch.tv', 6667)) + print_success("Connected to IRC server") + except socket.timeout: + print_error("Connection timed out!") + print_info("Check your internet connection and firewall settings") + return False + except Exception as e: + print_error(f"Connection failed: {e}") + return False + + # Send authentication + print_info("Sending authentication...") + sock.send(f"PASS {token}\r\n".encode()) + sock.send(f"NICK {nickname}\r\n".encode()) + + # Wait for response + response = "" + try: + response = sock.recv(2048).decode() + except socket.timeout: + print_error("No response from server (timeout)") + return False + + # Parse response + print_info(f"\nServer response received ({len(response)} bytes)") + + if "Login authentication failed" in response: + print_error("AUTHENTICATION FAILED!") + print_info("\nPossible reasons:") + print(f" 1. Token is expired or invalid") + print(f" 2. Token doesn't match the bot nickname '{nickname}'") + print(f" 3. Account that generated the token is banned/restricted") + print_info("\nHow to fix:") + print(f" → Go to: https://twitchtokengenerator.com") + print(f" → Make sure you're logged into Twitch as '{nickname}'") + print(f" → Generate a new 'Bot Chat Token'") + print(f" → Update TWITCH_OAUTH_TOKEN in .env") + return False + + if ":tmi.twitch.tv 001" in response or "Welcome" in response: + print_success("AUTHENTICATION SUCCESSFUL!") + print_info(f"Bot '{nickname}' can connect to Twitch IRC") + return True + + # Ambiguous response + print_warning("Received unexpected response:") + for line in response.split('\r\n'): + if line: + print(f" {line[:100]}") + + # If we got this far without clear failure, consider it success + if "authentication failed" not in response.lower(): + print_success("Authentication appears successful (no error detected)") + return True + + return False + + except Exception as e: + print_error(f"Error testing Twitch token: {e}") + import traceback + traceback.print_exc() + return False + + +async def test_perplexity_key(api_key, model="sonar-pro"): + """ + Test Perplexity API key with model fallback + + Args: + api_key (str): Perplexity API key + model (str): Model to test (will fallback to 'sonar' if needed) + + Returns: + tuple: (success, recommended_model) + """ + print_header("TESTING PERPLEXITY API") + + # Check key format + if not api_key: + print_error("API key is empty!") + print_info("Get a key from: https://www.perplexity.ai/settings/api") + return False, None + + if not api_key.startswith("pplx-"): + print_warning(f"API key doesn't start with 'pplx-' (got: {api_key[:10]}...)") + print_info("Key should look like: pplx-abc123...") + + print_info(f"API key format: Starts with '{api_key[:5]}...'") + print_info(f"API key length: {len(api_key)} characters") + + # Test with different models + models_to_test = [model] + if model != "sonar": + models_to_test.append("sonar") + + for test_model in models_to_test: + print_info(f"\nTesting with model: {test_model}") + + try: + async with httpx.AsyncClient(timeout=15.0) as client: + response = await client.post( + "https://api.perplexity.ai/chat/completions", + json={ + "model": test_model, + "messages": [{"role": "user", "content": "Say 'test successful' and nothing else"}], + "max_tokens": 10 + }, + headers={ + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json" + } + ) + + print_info(f"Response status: {response.status_code}") + + if response.status_code == 200: + print_success(f"API KEY VALID with model '{test_model}'!") + + # Try to parse response + try: + data = response.json() + if "choices" in data and len(data["choices"]) > 0: + content = data["choices"][0]["message"]["content"] + print_info(f"Test response: '{content}'") + except: + pass + + return True, test_model + + elif response.status_code == 401: + print_error("API key is INVALID (401 Unauthorized)") + print_info("\nHow to fix:") + print(f" → Go to: https://www.perplexity.ai/settings/api") + print(f" → Generate a new API key") + print(f" → Update PERPLEXITY_API_KEY in .env") + return False, None + + elif response.status_code == 400: + try: + error_data = response.json() + error_msg = error_data.get("error", {}).get("message", "Unknown error") + print_warning(f"400 Bad Request: {error_msg}") + + # Check if it's a model access issue + if "model" in error_msg.lower(): + print_info(f"Model '{test_model}' not available on your plan") + if test_model != models_to_test[-1]: + print_info("Trying fallback model...") + continue + else: + print_info(f"Error details: {error_msg}") + except: + print_warning(f"400 Bad Request (couldn't parse error)") + + # If this was the last model, fail + if test_model == models_to_test[-1]: + print_error("All models failed!") + return False, None + + elif response.status_code == 429: + print_error("Rate limited! Too many requests.") + print_info("Wait a few minutes and try again") + return False, None + + else: + print_warning(f"Unexpected status code: {response.status_code}") + try: + print_info(f"Response: {response.text[:200]}") + except: + pass + + # Try next model if available + if test_model != models_to_test[-1]: + continue + + return False, None + + except httpx.TimeoutException: + print_error("Request timed out!") + print_info("Check your internet connection") + return False, None + except Exception as e: + print_error(f"Error testing API: {e}") + import traceback + traceback.print_exc() + return False, None + + return False, None + + +async def main(): + """Main test runner""" + print(f""" +{Colors.HEADER}{Colors.BOLD}╔════════════════════════════════════════════════════════════════════╗ +║ EUGEN BOT - CREDENTIAL VALIDATOR ║ +║ Tests Twitch & Perplexity Credentials ║ +╚════════════════════════════════════════════════════════════════════╝{Colors.ENDC} +""") + + # Load .env file + if not os.path.exists('.env'): + print_error("No .env file found!") + print_info("Please run: python setup_wizard.py") + sys.exit(1) + + load_dotenv() + + # Get credentials + twitch_token = os.getenv('TWITCH_OAUTH_TOKEN', '') + twitch_nickname = os.getenv('TWITCH_BOT_NICKNAME', '') + twitch_channel = os.getenv('TWITCH_CHANNEL', '') + perplexity_key = os.getenv('PERPLEXITY_API_KEY', '') + perplexity_model = os.getenv('PERPLEXITY_MODEL', 'sonar-pro') + + print_info("Loaded configuration from .env:") + print(f" • Bot Nickname: {twitch_nickname}") + print(f" • Channel: {twitch_channel}") + print(f" • Model: {perplexity_model}") + + # Test credentials + results = {} + + # Test Twitch + results['twitch'] = await test_twitch_token(twitch_token, twitch_nickname) + + # Test Perplexity + results['perplexity'], recommended_model = await test_perplexity_key(perplexity_key, perplexity_model) + + # Final summary + print_header("VALIDATION SUMMARY") + + print(f"Twitch IRC: {'✓ PASS' if results['twitch'] else '✗ FAIL'}") + print(f"Perplexity API: {'✓ PASS' if results['perplexity'] else '✗ FAIL'}") + + if recommended_model and recommended_model != perplexity_model: + print_warning(f"\nRecommendation: Update PERPLEXITY_MODEL to '{recommended_model}' in .env") + + if all(results.values()): + print_success("\n🎉 All credentials valid! Bot is ready to run.") + print_info("Start the bot with: python chatbot.py") + sys.exit(0) + else: + print_error("\n❌ Some credentials are invalid. Please fix and re-test.") + sys.exit(1) + + +if __name__ == "__main__": + asyncio.run(main())