Merge pull request #4 from Kenearos/copilot/sub-pr-3

Fix resource management, validation logic, and import organization in setup wizard
This commit is contained in:
Kenearos 2026-01-02 21:15:02 +01:00 committed by GitHub
commit 5f9dbe6b15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 44 additions and 42 deletions

View file

@ -79,11 +79,11 @@ class Config:
def get_system_prompt(self): def get_system_prompt(self):
"""Returns the system prompt for the AI""" """Returns the system prompt for the AI"""
return """Du bist Kene (auch bekannt als Kenearos oder Keneraosmd), ein Twitch-Streamer. return """Du bist Kene's AI-Assistent, der für ihn im Chat antwortet.
WICHTIG: Du antwortest ALS Kene selbst, nicht als separater Bot. Sprich in der ersten Person ("Ich...", "Mein Setup...", etc.) WICHTIG: Du antwortest im Namen von Kene, aber als sein Helfer. Sprich in der ersten Person für ihn ("Ich...", "Mein Setup..."), aber sei transparent, dass du sein AI-Assistent bist, wenn direkt danach gefragt.
Über dich: Über Kene:
- Streamer, Botter (Automatisierung/Coding), Gamer - Streamer, Botter (Automatisierung/Coding), Gamer
- 3D-Druck mit Bambu X1C und H2C - 3D-Druck mit Bambu X1C und H2C
- Themen: Gaming (WoW, Elden Ring, Gamedev), 3D-Druck, Coding/Bots, Linux, Home Automation - Themen: Gaming (WoW, Elden Ring, Gamedev), 3D-Druck, Coding/Bots, Linux, Home Automation
@ -100,6 +100,4 @@ Chat-Verhalten:
- Authentisch und natürlich, wie Kene selbst sprechen würde - Authentisch und natürlich, wie Kene selbst sprechen würde
- Bei Tech/3D-Druck-Fragen: Gerne detaillierter mit konkreten Tipps - Bei Tech/3D-Druck-Fragen: Gerne detaillierter mit konkreten Tipps
- Beziehe dich auf frühere Chat-Nachrichten wenn möglich - Beziehe dich auf frühere Chat-Nachrichten wenn möglich
- Ehrlich sagen wenn du etwas nicht weißt - Ehrlich sagen wenn du etwas nicht weißt"""
Wichtig: Du bist Kene's AI-Assistent der für ihn antwortet - handle im Chat wie er es tun würde."""

View file

@ -5,8 +5,11 @@ Interactive configuration tool for first-time setup
import os import os
import sys import sys
import asyncio import asyncio
import socket
import getpass
from pathlib import Path from pathlib import Path
from dotenv import set_key, load_dotenv from dotenv import set_key, load_dotenv
import httpx
class Colors: class Colors:
@ -48,42 +51,39 @@ def print_warning(text):
print(f"{Colors.WARNING}{text}{Colors.ENDC}") print(f"{Colors.WARNING}{text}{Colors.ENDC}")
async def validate_twitch_token(token, channel): async def validate_twitch_token(token, bot_nickname):
""" """
Validate Twitch OAuth token by attempting IRC connection Validate Twitch OAuth token by attempting IRC connection
Args: Args:
token (str): OAuth token token (str): OAuth token
channel (str): Channel name bot_nickname (str): Bot nickname for authentication
Returns: Returns:
bool: True if valid bool: True if valid
""" """
import socket
if not token.startswith("oauth:"): if not token.startswith("oauth:"):
print_error("Token muss mit 'oauth:' beginnen!") print_error("Token muss mit 'oauth:' beginnen!")
return False return False
try: try:
print_info("Validiere Twitch-Verbindung...") print_info("Validiere Twitch-Verbindung...")
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.settimeout(5) sock.settimeout(5)
sock.connect(('irc.chat.twitch.tv', 6667)) sock.connect(('irc.chat.twitch.tv', 6667))
# Send authentication # Send authentication
sock.send(f"PASS {token}\r\n".encode()) sock.send(f"PASS {token}\r\n".encode())
sock.send(f"NICK testbot\r\n".encode()) sock.send(f"NICK {bot_nickname}\r\n".encode())
response = sock.recv(1024).decode() response = sock.recv(1024).decode()
sock.close()
if "Login authentication failed" in response: if "Login authentication failed" in response:
print_error("Twitch OAuth Token ist ungültig!") print_error("Twitch OAuth Token ist ungültig!")
return False return False
print_success("Twitch-Verbindung erfolgreich!") print_success("Twitch-Verbindung erfolgreich!")
return True return True
except Exception as e: except Exception as e:
print_warning(f"Konnte Twitch-Verbindung nicht testen: {e}") print_warning(f"Konnte Twitch-Verbindung nicht testen: {e}")
@ -91,25 +91,29 @@ async def validate_twitch_token(token, channel):
return True return True
async def validate_perplexity_key(api_key): async def validate_perplexity_key(api_key, model="sonar-pro"):
""" """
Validate Perplexity API key with test request Validate Perplexity API key with test request
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.
Args: Args:
api_key (str): Perplexity API key api_key (str): Perplexity API key
model (str): Model to test with (defaults to sonar-pro)
Returns: Returns:
bool: True if valid tuple: (bool success, bool should_retry) - success indicates if validation passed,
should_retry indicates if user should be prompted to retry
""" """
import httpx
try: try:
print_info("Validiere Perplexity API-Key...") print_info(f"Validiere Perplexity API-Key mit Modell '{model}'...")
async with httpx.AsyncClient(timeout=10.0) as client: async with httpx.AsyncClient(timeout=10.0) as client:
response = await client.post( response = await client.post(
"https://api.perplexity.ai/chat/completions", "https://api.perplexity.ai/chat/completions",
json={ json={
"model": "sonar-pro", "model": model,
"messages": [{"role": "user", "content": "test"}], "messages": [{"role": "user", "content": "test"}],
"max_tokens": 10 "max_tokens": 10
}, },
@ -121,19 +125,19 @@ async def validate_perplexity_key(api_key):
if response.status_code == 200: if response.status_code == 200:
print_success("Perplexity API-Key ist gültig!") print_success("Perplexity API-Key ist gültig!")
return True return (True, False)
elif response.status_code == 401: elif response.status_code == 401:
print_error("Perplexity API-Key ist ungültig!") print_error("Perplexity API-Key ist ungültig!")
return False return (False, True)
else: else:
print_warning(f"Unerwartete Antwort: {response.status_code}") print_warning(f"Unerwartete Antwort: {response.status_code}")
print_info("Fahre trotzdem fort - bitte später manuell prüfen!") print_info("Fahre trotzdem fort - bitte später manuell prüfen!")
return True return (True, False)
except Exception as e: except Exception as e:
print_warning(f"Konnte Perplexity API nicht testen: {e}") print_warning(f"Konnte Perplexity API nicht testen: {e}")
print_info("Fahre trotzdem fort - bitte später manuell prüfen!") print_info("Fahre trotzdem fort - bitte später manuell prüfen!")
return True return (True, False)
def create_env_file(config): def create_env_file(config):
@ -183,8 +187,6 @@ def get_input(prompt, default=None, required=True, secret=False):
Returns: Returns:
str: User input str: User input
""" """
import getpass
if default: if default:
prompt_text = f"{prompt} [{default}]: " prompt_text = f"{prompt} [{default}]: "
else: else:
@ -262,7 +264,7 @@ async def run_wizard():
) )
# Validate Twitch # Validate Twitch
if not await validate_twitch_token(config['TWITCH_OAUTH_TOKEN'], config['TWITCH_CHANNEL']): if not await validate_twitch_token(config['TWITCH_OAUTH_TOKEN'], config['TWITCH_BOT_NICKNAME']):
retry = input("\nTrotzdem fortfahren? (j/n): ") retry = input("\nTrotzdem fortfahren? (j/n): ")
if retry.lower() != 'j': if retry.lower() != 'j':
print_error("Setup abgebrochen!") print_error("Setup abgebrochen!")
@ -281,13 +283,15 @@ async def run_wizard():
api_key = get_input("Perplexity API Key", secret=True, required=True) api_key = get_input("Perplexity API Key", secret=True, required=True)
config['PERPLEXITY_API_KEY'] = api_key config['PERPLEXITY_API_KEY'] = api_key
if await validate_perplexity_key(api_key): success, should_retry = await validate_perplexity_key(api_key)
if success:
break break
retry = input("\nErneut versuchen? (j/n): ") if should_retry:
if retry.lower() != 'j': retry = input("\nErneut versuchen? (j/n): ")
print_error("Setup abgebrochen!") if retry.lower() != 'j':
return False print_error("Setup abgebrochen!")
return False
# Step 3: Advanced Settings # Step 3: Advanced Settings
print_header("SCHRITT 3: ERWEITERTE EINSTELLUNGEN") print_header("SCHRITT 3: ERWEITERTE EINSTELLUNGEN")