#!/usr/bin/env python3 """ Interactive onboarding wizard for topic-monitor skill. Runs on first use when no config.json exists. """ import json import sys from pathlib import Path SKILL_DIR = Path(__file__).parent.parent CONFIG_FILE = SKILL_DIR / "config.json" def print_welcome(): print() print("=" * 55) print(" šŸ” Topic Monitor - Setup Wizard") print("=" * 55) print() print("Welcome! Let's set up your personal topic monitoring.") print("You can mix web search, RSS/Atom feeds, and GitHub release feeds.") print() def prompt(question: str, default: str = None) -> str: question = f"{question} [{default}]: " if default else f"{question}: " response = input(question).strip() return response if response else (default or "") def prompt_yes_no(question: str, default: bool = True) -> bool: default_hint = "Y/n" if default else "y/N" response = input(f"{question} ({default_hint}): ").strip().lower() if not response: return default return response in ('y', 'yes', 'ja', 'si', 'oui') def prompt_choice(question: str, choices: list, default: str = None) -> str: print(f"\n{question}") for i, choice in enumerate(choices, 1): marker = " *" if choice == default else "" print(f" {i}. {choice}{marker}") while True: response = input(f"\nEnter number or value [{default or choices[0]}]: ").strip() if not response: return default or choices[0] try: idx = int(response) if 1 <= idx <= len(choices): return choices[idx - 1] except ValueError: pass for choice in choices: if choice.lower() == response.lower(): return choice print(f" Please enter a number 1-{len(choices)} or a valid option.") def prompt_multiline(question: str, hint: str = None) -> list: print(f"\n{question}") if hint: print(f" {hint}") print(" (Enter each item on a new line. Empty line when done)") print() items = [] while True: line = input(" > ").strip() if not line: break items.append(line) return items def prompt_csv(question: str) -> list: response = input(f" {question}: ").strip() if not response: return [] return [item.strip() for item in response.split(",") if item.strip()] def create_topic_id(name: str) -> str: topic_id = name.lower().replace(" ", "-") topic_id = "".join(c for c in topic_id if c.isalnum() or c == "-") topic_id = "-".join(filter(None, topic_id.split("-"))) return topic_id[:30] def gather_topics() -> list: topics = [] print("-" * 55) print("šŸ“‹ STEP 1: Topics to Monitor") print("-" * 55) topic_names = prompt_multiline( "What topics do you want to monitor?", "Examples: AI Models, Security Alerts, Product Updates" ) if not topic_names: print("\nāš ļø No topics entered. You can add them later with manage_topics.py") return [] for i, name in enumerate(topic_names, 1): print(f"\n--- Topic {i}/{len(topic_names)}: {name} ---") query = prompt(f" Search query for '{name}'", f"{name} news updates") keywords = prompt_csv(f"Keywords for '{name}' (comma-separated)") feeds = prompt_csv(f"RSS/Atom feeds for '{name}' (comma-separated, optional)") github_repos = prompt_csv(f"GitHub repos for release monitoring (owner/repo, optional)") required_keywords = prompt_csv(f"Required keywords (all must appear, optional)") exclude_keywords = prompt_csv(f"Exclude keywords (filter out if present, optional)") alert_on_sentiment_shift = prompt_yes_no("Alert on sentiment shift?", default=False) topic = { "id": create_topic_id(name), "name": name, "query": query, "keywords": keywords, "feeds": feeds, "github_repos": github_repos, "required_keywords": required_keywords, "exclude_keywords": exclude_keywords, "frequency": None, "importance_threshold": None, "channels": ["telegram"], "context": "", "alert_on": ["keyword_exact_match"] + (["github_release"] if github_repos else []), "alert_on_sentiment_shift": alert_on_sentiment_shift, "ignore_sources": [], "boost_sources": [], } topics.append(topic) return topics def gather_settings() -> dict: print() print("-" * 55) print("āš™ļø STEP 2: Monitoring Settings") print("-" * 55) frequency = prompt_choice("How often should I check for updates?", ["hourly", "daily", "weekly"], default="daily") importance = prompt_choice("Importance threshold for alerts?", ["low", "medium", "high"], default="medium") digest_enabled = prompt_yes_no("Enable weekly digest?", default=True) digest_day = "sunday" if digest_enabled: digest_day = prompt_choice("Which day should I send the digest?", ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"], default="sunday") return { "frequency": frequency, "importance_threshold": importance, "digest_enabled": digest_enabled, "digest_day": digest_day, "digest_time": "18:00", "max_alerts_per_day": 5, "max_alerts_per_topic_per_day": 2, "deduplication_window_hours": 72, "learning_enabled": True, "quiet_hours": {"enabled": False, "start": "22:00", "end": "08:00"}, } def build_config(topics: list, settings: dict) -> dict: frequency = settings.pop("frequency") importance = settings.pop("importance_threshold") for topic in topics: topic["frequency"] = frequency topic["importance_threshold"] = importance return { "topics": topics, "settings": settings, "channels": { "telegram": { "enabled": True, "chat_id": None, "silent": False, "effects": {"high_importance": "šŸ”„", "medium_importance": "šŸ“Œ"}, }, "discord": {"enabled": False, "webhook_url": None, "username": "Topic Monitor", "avatar_url": None}, "email": { "enabled": False, "to": None, "from": "monitor@yourdomain.com", "smtp_server": "smtp.gmail.com", "smtp_port": 587, "smtp_user": None, "smtp_password": None, }, }, } def save_config(config: dict): with open(CONFIG_FILE, 'w') as f: json.dump(config, f, indent=2) def print_summary(config: dict): print() print("=" * 55) print(" āœ… Setup Complete!") print("=" * 55) print() for topic in config.get("topics", []): print(f"• {topic['name']}") print(f" Query: {topic['query'] or '—'}") print(f" Keywords: {', '.join(topic.get('keywords', [])) or '—'}") print(f" Feeds: {', '.join(topic.get('feeds', [])) or '—'}") print(f" GitHub repos: {', '.join(topic.get('github_repos', [])) or '—'}") print(f" Required keywords: {', '.join(topic.get('required_keywords', [])) or '—'}") print(f" Exclude keywords: {', '.join(topic.get('exclude_keywords', [])) or '—'}") print(f" Sentiment shift alerts: {topic.get('alert_on_sentiment_shift')}") print() print("Test with: python3 scripts/monitor.py --dry-run --verbose") print() def main(): if CONFIG_FILE.exists(): print("\nāš ļø config.json already exists!\n") if not prompt_yes_no("Do you want to start fresh and overwrite it?", default=False): print("\nKeeping existing config. Use manage_topics.py to edit topics.") sys.exit(0) print_welcome() try: topics = gather_topics() settings = gather_settings() config = build_config(topics, settings) save_config(config) print_summary(config) except KeyboardInterrupt: print("\n\nāš ļø Setup cancelled. No changes made.") sys.exit(1) except EOFError: print("\n\nāš ļø Input ended. No changes made.") sys.exit(1) if __name__ == "__main__": main()