diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 10a65ba..03f60f3 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -164,3 +164,43 @@ jobs: echo "Tests: ${{ needs.test.result }}" echo "Security: ${{ needs.security-scan.result }}" echo "Quality: ${{ needs.code-quality.result }}" +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: Python package + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.9", "3.10", "3.11"] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest diff --git a/LICENSE_GUIDE.md b/LICENSE_GUIDE.md new file mode 100644 index 0000000..f8ce3d3 --- /dev/null +++ b/LICENSE_GUIDE.md @@ -0,0 +1,204 @@ +# LICENSE - Eugen Twitch Bot + +## Empfohlene Lizenz: MIT License + +Für ein öffentliches Gaming & 3D-Druck Twitch-Bot-Projekt empfehlen wir die **MIT License**. + +### Warum MIT? + +| Aspekt | MIT | GPL | Apache 2.0 | +|--------|-----|-----|-----------| +| **Einfachheit** | ✅ Sehr kurz & verständlich | ⚠️ Komplex | ⚠️ 200+ Zeilen | +| **Commercial Use** | ✅ Erlaubt | ✅ Erlaubt | ✅ Erlaubt | +| **Modifikation** | ✅ Erlaubt | ✅ Erlaubt | ✅ Erlaubt | +| **Private Use** | ✅ Erlaubt | ✅ Erlaubt | ✅ Erlaubt | +| **Distribution** | ✅ Erlaubt | ⚠️ Nur unter GPL | ✅ Erlaubt | +| **Copyleft** | ❌ Nein | ✅ Ja (striktes Copyleft) | ❌ Nein (file-level) | +| **Patent Protection** | ❌ Nein | ❌ Nein | ✅ Ja | +| **Liability Disclaimer** | ✅ Ja | ✅ Ja | ✅ Ja | + +--- + +## MIT License (Volltext) + +Erstelle eine Datei `LICENSE` im Root-Verzeichnis mit folgendem Inhalt: + +``` +MIT License + +Copyright (c) 2026 Eugen Twitch Bot Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +--- + +## Was die MIT License bedeutet + +### ✅ Erlaubt + +- **Commercial Use**: Jemand kann den Bot für Profit verwenden +- **Modification**: Code kann verändert werden +- **Distribution**: Code kann weitergegeben werden +- **Private Use**: Privat nutzen ohne Einschränkung +- **Sublicense**: Unter einer anderen Lizenz weitergeben (mit Bedingungen) + +### ❌ Nicht erlaubt + +- **Liability**: Deine Haftung ist ausgeschlossen +- **Warranty**: Keine Garantie für Funktionalität + +### ⚠️ Bedingungen + +- **Lizenz & Copyright Notice** muss mitgegeben werden +- **Disclaimer** muss in allen Kopien stehen + +--- + +## Lizenz in GitHub aktivieren + +### Option 1: GitHub Web-UI (Einfachste Methode) + +1. Gehe zu deinem GitHub Repo +2. **Settings** → **License** +3. Wähle **MIT License** aus Dropdown +4. GitHub erstellt automatisch die `LICENSE` Datei +5. Commit & Push + +### Option 2: Lokal hinzufügen + +```powershell +# LICENSE Datei erstellen (siehe oben) +# Dann committen: +git add LICENSE +git commit -m "Add MIT License" +git push +``` + +--- + +## README Badge hinzufügen + +In deiner `README.md` oben hinzufügen: + +```markdown +# Eugen Twitch Bot + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) + +Intelligenter Gaming & 3D-Druck Twitch-Chat-Agent mit Perplexity AI Integration. +``` + +Ergebnis: Goldener Badge mit "License: MIT" 🏆 + +--- + +## Lizenz-Vergleich für andere Use-Cases + +### GPL 3.0 (Für Open Source Puristen) + +``` +Nutzbar, wenn: +- Du willst dass Derivate auch Open Source bleiben (Copyleft) +- Dein Projekt bereits GPL ist +- Community über Commercial Use nicht hinwegkommt + +Nicht, wenn: +- Kommerzielle Nutzung problemlos sein soll +- Du simplify willst +``` + +### Apache 2.0 (Für Enterprise) + +``` +Nutzbar, wenn: +- Patent-Schutz wichtig ist +- Enterprise-Unternehmen nutzen sollen +- Längere, formale Lizenz okay ist + +Nicht, wenn: +- Du es einfach hast +- Kleine Open-Source-Projekte +``` + +--- + +## Setup Checkliste + +- [ ] `LICENSE` Datei erstellen (MIT Text oben kopieren) +- [ ] In `README.md` Badge hinzufügen +- [ ] In `.gitignore` nichts Wichtiges ignorieren +- [ ] `.env.example` committen (keine echten Keys!) +- [ ] GitHub Repo Settings → License → MIT +- [ ] First commit mit License-Info + +--- + +## Dateistruktur nach Lizenz-Setup + +``` +eugen/ +├── LICENSE ← MIT Text +├── README.md ← Mit Badge +├── .env.example ← Template ohne Keys +├── .gitignore +├── requirements.txt +├── chatbot.py +├── config.py +├── gui.py +├── ai_provider.py +├── memory.py +└── data/ + └── conversations/ +``` + +--- + +## Copyright-Zeile anpassen + +Je nachdem wer dein "Owner" ist: + +**Dein Name:** +``` +Copyright (c) 2026 Dein Name +``` + +**Team/Org:** +``` +Copyright (c) 2026 Eugen Bot Team +``` + +**Community:** +``` +Copyright (c) 2026 Eugen Twitch Bot Contributors +``` + +--- + +## Legal Quick Reference + +| Question | MIT Answer | +|----------|------------| +| Darf ich das kommerziell nutzen? | ✅ Ja | +| Muss ich den Code offen halten? | ❌ Nein | +| Muss ich Original nennen? | ✅ Ja | +| Kann ich Änderungen machen? | ✅ Ja, ohne mir zu sagen | +| Haftung? | ❌ Nein (Disclaimer) | + +--- diff --git a/README.md b/README.md index a9299cc..16ca109 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,17 @@ pip install -r requirements.txt ### Configuration #### Option 1: Setup Wizard (Recommended) -Simply run the bot and the setup wizard will guide you: +Run the interactive setup wizard to configure the bot: ```bash -python chatbot.py +python setup_wizard.py ``` +The wizard will: +- Guide you through getting Twitch OAuth token +- Help you set up Perplexity API key +- Validate your credentials +- Create necessary directories and config files + #### Option 2: Manual Configuration Copy `.env.example` to `.env` and fill in your credentials: ```bash diff --git a/config.py b/config.py index 54adda8..1dfc717 100644 --- a/config.py +++ b/config.py @@ -79,18 +79,25 @@ class Config: def get_system_prompt(self): """Returns the system prompt for the AI""" - return """Du bist Eugen, ein hilfreicher und freundlicher Twitch-Chat-Bot. + return """Du bist Kene's AI-Assistent, der für ihn im Chat antwortet. -Du bist Experte für folgende Themen: -- Gaming: World of Warcraft, Elden Ring, Gamedev -- 3D-Druck: Prusa i3, Bambu Labs, Creality -- Tech: Python, Linux, Home Automation +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. -Verhalte dich wie ein echter Chat-Teilnehmer: -- Sei freundlich und hilfsbereit -- Antworte kurz und prägnant (max 2-3 Sätze für Twitch-Chat) -- Verwende gelegentlich Gaming- oder Tech-Slang -- Beziehe dich auf vorherige Gespräche wenn möglich -- Sei humorvoll aber respektvoll +Über Kene: +- Streamer, Botter (Automatisierung/Coding), Gamer +- 3D-Druck mit Bambu X1C und H2C +- Themen: Gaming (WoW, Elden Ring, Gamedev), 3D-Druck, Coding/Bots, Linux, Home Automation +- Humor: Vielseitig - Sarkasmus, Dad-Jokes, Gaming-Memes, Spaß & Fun +- Chat-Stil: Locker und direkt, aber technisch detailliert wenn nötig -Wenn du etwas nicht weißt, sage es ehrlich. Wenn die Frage nicht zu deinen Themen passt, biete trotzdem Hilfe an oder verweise auf passende Ressourcen.""" +Politische Haltung: +- Linkspolitisch eingestellt +- Argumentierst klar gegen rechte Positionen, Rechtsmitte und Nazis +- Bei politischen Themen: Klare Kante, aber sachlich bleiben + +Chat-Verhalten: +- Antworte kurz (2-3 Sätze max für Twitch-Chat) +- Authentisch und natürlich, wie Kene selbst sprechen würde +- Bei Tech/3D-Druck-Fragen: Gerne detaillierter mit konkreten Tipps +- Beziehe dich auf frühere Chat-Nachrichten wenn möglich +- Ehrlich sagen wenn du etwas nicht weißt""" diff --git a/requirements.txt b/requirements.txt index 7e5c8ec..0f81a20 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ irc==20.1.0 python-dotenv==1.0.0 -PySimpleGUI==4.60.0 +PySimpleGUI==5.0.8.3 requests==2.31.0 httpx==0.25.0 diff --git a/setup_wizard.py b/setup_wizard.py new file mode 100644 index 0000000..f779cea --- /dev/null +++ b/setup_wizard.py @@ -0,0 +1,381 @@ +""" +Setup Wizard for Eugen Twitch Bot +Interactive configuration tool for first-time setup +""" +import os +import sys +import asyncio +import socket +import getpass +from pathlib import Path +from dotenv import set_key, load_dotenv +import httpx + + +class Colors: + """ANSI color codes for terminal output""" + 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}{'='*60}{Colors.ENDC}") + print(f"{Colors.HEADER}{Colors.BOLD}{text.center(60)}{Colors.ENDC}") + print(f"{Colors.HEADER}{Colors.BOLD}{'='*60}{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 validate_twitch_token(token, bot_nickname): + """ + Validate Twitch OAuth token by attempting IRC connection + + Args: + token (str): OAuth token + bot_nickname (str): Bot nickname for authentication + + Returns: + bool: True if valid + """ + if not token.startswith("oauth:"): + print_error("Token muss mit 'oauth:' beginnen!") + return False + + 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)) + + # Send authentication + sock.send(f"PASS {token}\r\n".encode()) + sock.send(f"NICK {bot_nickname}\r\n".encode()) + + response = sock.recv(1024).decode() + + if "Login authentication failed" in response: + print_error("Twitch OAuth Token ist ungültig!") + return False + + print_success("Twitch-Verbindung erfolgreich!") + return True + + except Exception as e: + print_warning(f"Konnte Twitch-Verbindung nicht testen: {e}") + print_info("Fahre trotzdem fort - bitte später manuell prüfen!") + return True + + +async def validate_perplexity_key(api_key, model="sonar-pro"): + """ + 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: + api_key (str): Perplexity API key + 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 + """ + 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" + } + ) + + 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) + + 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) + + +def create_env_file(config): + """ + Create or update .env file with configuration + + Args: + config (dict): Configuration dictionary + """ + env_file = ".env" + + # Create .env if it doesn't exist + if not os.path.exists(env_file): + Path(env_file).touch() + + # Write all config values + for key, value in config.items(): + set_key(env_file, key, value) + + print_success(f".env Datei erstellt/aktualisiert!") + + +def create_directories(): + """Create necessary directories for bot operation""" + directories = [ + "data", + "data/conversations", + "logs" + ] + + for directory in directories: + Path(directory).mkdir(parents=True, exist_ok=True) + + print_success("Verzeichnisse erstellt!") + + +def get_input(prompt, default=None, required=True, secret=False): + """ + Get user input with validation + + Args: + prompt (str): Input prompt + default (str): Default value + required (bool): Whether input is required + secret (bool): Whether to hide input (for passwords) + + Returns: + str: User input + """ + if default: + prompt_text = f"{prompt} [{default}]: " + else: + prompt_text = f"{prompt}: " + + while True: + if secret: + value = getpass.getpass(prompt_text) + else: + value = input(prompt_text) + + # Use default if provided and input is empty + if not value and default: + return default + + # Check if required + if required and not value: + print_error("Dieses Feld ist erforderlich!") + continue + + return value + + +async def run_wizard(): + """Main wizard flow""" + + # Welcome + print_header("🤖 EUGEN BOT SETUP WIZARD 🤖") + print("Willkommen beim Setup-Assistenten für deinen Twitch-Bot!") + print("Dieser Wizard führt dich durch die Erstkonfiguration.\n") + + print_info("Du benötigst:") + print(" 1. Einen Twitch OAuth Token") + print(" 2. Deinen Twitch Channel-Namen") + print(" 3. Einen Perplexity API Key") + print() + + input("Drücke ENTER um zu starten...") + + # Configuration dictionary + config = {} + + # Step 1: Twitch Configuration + print_header("SCHRITT 1: TWITCH KONFIGURATION") + + print_info("Twitch OAuth Token generieren:") + print(" → Gehe zu: https://twitchtokengenerator.com") + print(" → Wähle 'Bot Chat Token'") + print(" → Authorisiere und kopiere das 'oauth:...' Token") + print() + + while True: + token = get_input("Twitch OAuth Token", secret=True, required=True) + if not token.startswith("oauth:"): + print_warning("Token sollte mit 'oauth:' beginnen. Füge ich hinzu...") + token = f"oauth:{token}" + + config['TWITCH_OAUTH_TOKEN'] = token + break + + config['TWITCH_CHANNEL'] = get_input( + "Twitch Channel Name (ohne #)", + default="keneraosmd", + required=True + ) + + # Add # if not present + if not config['TWITCH_CHANNEL'].startswith('#'): + config['TWITCH_CHANNEL'] = f"#{config['TWITCH_CHANNEL']}" + + config['TWITCH_BOT_NICKNAME'] = get_input( + "Bot Nickname", + default="Eugen", + required=True + ) + + # Validate Twitch + if not await validate_twitch_token(config['TWITCH_OAUTH_TOKEN'], config['TWITCH_BOT_NICKNAME']): + retry = input("\nTrotzdem fortfahren? (j/n): ") + if retry.lower() != 'j': + print_error("Setup abgebrochen!") + return False + + # Step 2: Perplexity Configuration + print_header("SCHRITT 2: PERPLEXITY API") + + print_info("Perplexity API Key erhalten:") + print(" → Gehe zu: https://www.perplexity.ai/settings/api") + print(" → Erstelle einen neuen API Key") + print(" → Kopiere den Key (beginnt mit 'pplx-...')") + print() + + 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) + if success: + break + + if should_retry: + retry = input("\nErneut versuchen? (j/n): ") + if retry.lower() != 'j': + print_error("Setup abgebrochen!") + return False + + # Step 3: Advanced Settings + print_header("SCHRITT 3: ERWEITERTE EINSTELLUNGEN") + + print("Möchtest du erweiterte Einstellungen konfigurieren?") + advanced = input("(j/n, default: n): ").lower() == 'j' + + if advanced: + config['PERPLEXITY_MODEL'] = get_input( + "Perplexity Modell", + default="sonar-pro" + ) + config['MAX_TOKENS'] = get_input( + "Max Tokens pro Antwort", + default="450" + ) + config['DEBUG_MODE'] = get_input( + "Debug Mode aktivieren?", + default="false" + ) + config['CONTEXT_RETENTION_HOURS'] = get_input( + "Context Retention (Stunden)", + default="1" + ) + else: + config['PERPLEXITY_MODEL'] = "sonar-pro" + config['MAX_TOKENS'] = "450" + config['DEBUG_MODE'] = "false" + config['CONTEXT_RETENTION_HOURS'] = "1" + + # Step 4: Create files and directories + print_header("SCHRITT 4: SETUP ABSCHLIESSEN") + + print_info("Erstelle Konfigurationsdateien...") + create_env_file(config) + create_directories() + + # Final summary + print_header("✅ SETUP ERFOLGREICH!") + + print(f"{Colors.OKGREEN}Deine Konfiguration:{Colors.ENDC}") + print(f" Channel: {config['TWITCH_CHANNEL']}") + print(f" Bot Name: {config['TWITCH_BOT_NICKNAME']}") + print(f" Modell: {config['PERPLEXITY_MODEL']}") + print(f" Max Tokens: {config['MAX_TOKENS']}") + print() + + print_info("Nächste Schritte:") + print(" 1. Starte den Bot mit: python chatbot.py") + print(" 2. Der Bot verbindet sich mit Twitch") + print(" 3. Erwähne den Bot im Chat mit '@Eugen' oder 'Eugen:'") + print() + + print_success("Viel Spaß mit deinem Bot! 🚀") + return True + + +def main(): + """Main entry point""" + try: + # Check if already configured + if os.path.exists('.env'): + load_dotenv() + if os.getenv('TWITCH_OAUTH_TOKEN') and os.getenv('PERPLEXITY_API_KEY'): + print_warning(".env Datei existiert bereits!") + reconfigure = input("Möchtest du die Konfiguration überschreiben? (j/n): ") + if reconfigure.lower() != 'j': + print_info("Setup abgebrochen. Nutze die bestehende Konfiguration.") + return + + # Run async wizard + success = asyncio.run(run_wizard()) + + if not success: + sys.exit(1) + + except KeyboardInterrupt: + print() + print_warning("Setup abgebrochen!") + sys.exit(1) + except Exception as e: + print_error(f"Fehler während des Setups: {e}") + sys.exit(1) + + +if __name__ == "__main__": + main()