synapse-assistant-ai 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.4
2
+ Name: synapse-assistant-ai
3
+ Version: 0.1.0
4
+ Summary: Free offline voice-controlled AI assistant. Install β†’ Run β†’ Talk.
5
+ License: MIT
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: vosk>=0.3.45
9
+ Requires-Dist: sounddevice>=0.4.6
10
+ Requires-Dist: pyttsx3>=2.90
11
+ Requires-Dist: ollama>=0.4.0
12
+ Requires-Dist: python-telegram-bot>=21.0
13
+ Requires-Dist: opencv-python-headless>=4.8.0
14
+ Requires-Dist: ultralytics>=8.2.0
15
+ Requires-Dist: pytesseract>=0.3.10
16
+ Requires-Dist: apscheduler>=3.10.0
17
+ Requires-Dist: python-dotenv>=1.0.0
18
+ Requires-Dist: pywhatkit>=5.4
19
+ Requires-Dist: duckduckgo-search>=5.0
20
+ Requires-Dist: numpy
21
+
22
+ # πŸ€– Super-AI β€” Free Offline Voice-Controlled Assistant
23
+
24
+ **Install. Run. Talk.** That's it.
25
+
26
+ ## Install (one command)
27
+
28
+ ```bash
29
+ cd alright
30
+ pip install .
31
+ ```
32
+
33
+ ## Run (one command)
34
+
35
+ ```bash
36
+ superai
37
+ ```
38
+
39
+ The first time you run it, an interactive setup will start right in your terminal:
40
+ - It will ask what you want to name your AI (e.g., Jarvis, Friday).
41
+ - It will ask if you want to set up Telegram notifications.
42
+ - **You simply paste your Telegram Bot Token and Chat ID directly in the terminal** when prompted.
43
+
44
+ Once setup is done, your AI will automatically message you on Telegram whenever it finishes a background task. Then it will auto-download the necessary AI files (~1.1 GB, one-time only).
45
+
46
+ After that, it starts instantly. Just talk:
47
+
48
+ > **"Jarvis, open youtube"**
49
+
50
+ ## What it can do
51
+
52
+ | Say this | It does this |
53
+ |----------|-------------|
54
+ | "Jarvis, **open google**" | Opens google.com in browser |
55
+ | "Jarvis, **open youtube**" | Opens youtube.com |
56
+ | "Jarvis, **launch Safari**" | Opens Safari app |
57
+ | "Jarvis, **play** /path/to/song.mp3" | Plays media file |
58
+ | "Jarvis, **send whatsapp** to +919876543210 β€” hello" | Automates sending WhatsApp message |
59
+ | "Jarvis, **search web** for latest AI news" | Opens Chrome & tells you the top web result |
60
+ | "Jarvis, **send message** β€” meeting at 5" | Sends Telegram message |
61
+ | "Jarvis, **send voice note** β€” I'll be late" | Sends voice note on Telegram |
62
+ | "Jarvis, **set reminder** 5 minutes β€” drink water" | Reminds you in 5 min |
63
+ | "Jarvis, **what is Python?**" | Answers your question by voice |
64
+ | "Jarvis, **analyse video** /path/to/clip.mp4" | Tells what's in the video |
65
+
66
+ ## Requirements
67
+
68
+ - Python 3.10+
69
+ - Microphone
70
+ - 6 GB RAM (minimum)
71
+ - ~1.5 GB disk space (for AI files)
72
+ - Internet (only for first-time download and web searches)
73
+
74
+ ## How it works
75
+
76
+ ```
77
+ You speak β†’ AI Brain β†’ Action / Answer β†’ Speaks back
78
+ ```
79
+
80
+ Everything runs locally on your machine. No cloud. No API bills. No data leaves your computer.
81
+
82
+ ## License
83
+
84
+ MIT
@@ -0,0 +1,63 @@
1
+ # πŸ€– Super-AI β€” Free Offline Voice-Controlled Assistant
2
+
3
+ **Install. Run. Talk.** That's it.
4
+
5
+ ## Install (one command)
6
+
7
+ ```bash
8
+ cd alright
9
+ pip install .
10
+ ```
11
+
12
+ ## Run (one command)
13
+
14
+ ```bash
15
+ superai
16
+ ```
17
+
18
+ The first time you run it, an interactive setup will start right in your terminal:
19
+ - It will ask what you want to name your AI (e.g., Jarvis, Friday).
20
+ - It will ask if you want to set up Telegram notifications.
21
+ - **You simply paste your Telegram Bot Token and Chat ID directly in the terminal** when prompted.
22
+
23
+ Once setup is done, your AI will automatically message you on Telegram whenever it finishes a background task. Then it will auto-download the necessary AI files (~1.1 GB, one-time only).
24
+
25
+ After that, it starts instantly. Just talk:
26
+
27
+ > **"Jarvis, open youtube"**
28
+
29
+ ## What it can do
30
+
31
+ | Say this | It does this |
32
+ |----------|-------------|
33
+ | "Jarvis, **open google**" | Opens google.com in browser |
34
+ | "Jarvis, **open youtube**" | Opens youtube.com |
35
+ | "Jarvis, **launch Safari**" | Opens Safari app |
36
+ | "Jarvis, **play** /path/to/song.mp3" | Plays media file |
37
+ | "Jarvis, **send whatsapp** to +919876543210 β€” hello" | Automates sending WhatsApp message |
38
+ | "Jarvis, **search web** for latest AI news" | Opens Chrome & tells you the top web result |
39
+ | "Jarvis, **send message** β€” meeting at 5" | Sends Telegram message |
40
+ | "Jarvis, **send voice note** β€” I'll be late" | Sends voice note on Telegram |
41
+ | "Jarvis, **set reminder** 5 minutes β€” drink water" | Reminds you in 5 min |
42
+ | "Jarvis, **what is Python?**" | Answers your question by voice |
43
+ | "Jarvis, **analyse video** /path/to/clip.mp4" | Tells what's in the video |
44
+
45
+ ## Requirements
46
+
47
+ - Python 3.10+
48
+ - Microphone
49
+ - 6 GB RAM (minimum)
50
+ - ~1.5 GB disk space (for AI files)
51
+ - Internet (only for first-time download and web searches)
52
+
53
+ ## How it works
54
+
55
+ ```
56
+ You speak β†’ AI Brain β†’ Action / Answer β†’ Speaks back
57
+ ```
58
+
59
+ Everything runs locally on your machine. No cloud. No API bills. No data leaves your computer.
60
+
61
+ ## License
62
+
63
+ MIT
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "synapse-assistant-ai"
7
+ version = "0.1.0"
8
+ description = "Free offline voice-controlled AI assistant. Install β†’ Run β†’ Talk."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = {text = "MIT"}
12
+
13
+ dependencies = [
14
+ # Speech-to-Text (offline)
15
+ "vosk>=0.3.45",
16
+ "sounddevice>=0.4.6",
17
+
18
+ # Text-to-Speech (offline, system voices)
19
+ "pyttsx3>=2.90",
20
+
21
+ # LLM client (talks to auto-managed Ollama)
22
+ "ollama>=0.4.0",
23
+
24
+ # Telegram (optional at runtime, installed so it's ready)
25
+ "python-telegram-bot>=21.0",
26
+
27
+ # Video analysis (optional at runtime)
28
+ "opencv-python-headless>=4.8.0",
29
+ "ultralytics>=8.2.0",
30
+ "pytesseract>=0.3.10",
31
+
32
+ # Scheduler
33
+ "apscheduler>=3.10.0",
34
+
35
+ # Config
36
+ "python-dotenv>=1.0.0",
37
+
38
+ # WhatsApp & Web Search
39
+ "pywhatkit>=5.4",
40
+ "duckduckgo-search>=5.0",
41
+
42
+ # Utilities
43
+ "numpy",
44
+ ]
45
+
46
+ [project.scripts]
47
+ synapse-ai = "super_ai:start_loop"
48
+ superai = "super_ai:start_loop"
49
+
50
+ [tool.setuptools.packages.find]
51
+ include = ["super_ai*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,103 @@
1
+ """
2
+ super_ai
3
+ ════════
4
+ Free, offline, voice‑controlled AI assistant.
5
+
6
+ Install: pip install .
7
+ Run: superai
8
+
9
+ That's it. Models auto‑download. Telegram notifications auto‑send.
10
+ """
11
+
12
+ __version__ = "0.1.0"
13
+
14
+ from .speech import listen, speak, check_wake_word, text_to_wav
15
+ from .llm import ask
16
+ from .actions import run_action
17
+
18
+
19
+ def start_loop():
20
+ """
21
+ The WHOLE assistant. One function. Called by `superai` CLI.
22
+ """
23
+ from .config import interactive_setup, cfg
24
+ from .messaging import notify
25
+
26
+ # ── Interactive Setup (if first run) ──
27
+ interactive_setup()
28
+
29
+ # ── Banner ──
30
+ print()
31
+ print("╔══════════════════════════════════════════╗")
32
+ print("β•‘ πŸ€– S U P E R - A I v0.1.0 β•‘")
33
+ print("β•‘ Free Β· Offline Β· Voice-Controlled β•‘")
34
+ print("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•")
35
+ print()
36
+
37
+ # ── Auto‑download models (first run only) ──
38
+ print("⏳ Checking models...")
39
+ from .model_manager import ensure_all_models
40
+ ensure_all_models()
41
+
42
+ # ── Warm up engines ──
43
+ print("⏳ Starting speech engine...")
44
+ from .speech import _ensure_vosk, _ensure_tts
45
+ _ensure_vosk()
46
+ _ensure_tts()
47
+
48
+ print("⏳ Starting AI brain...")
49
+ from .llm import _ensure_llm
50
+ _ensure_llm()
51
+
52
+ # ── Ready ──
53
+ print()
54
+ print("═" * 44)
55
+ print(f" βœ… READY! Say \"{cfg.wake_word}\" + command")
56
+ print(f" Example: \"{cfg.wake_word} open youtube\"")
57
+ print(f" Press Ctrl+C to quit")
58
+ print("═" * 44)
59
+ print()
60
+
61
+ speak(f"Super AI is ready. Say {cfg.wake_word} followed by your command.")
62
+
63
+ # Notify on Telegram that AI is online
64
+ notify("🟒 Super-AI is now online and listening!")
65
+
66
+ # ── Main loop β€” runs forever until Ctrl+C ──
67
+ while True:
68
+ try:
69
+ # 1. Listen
70
+ text = listen(timeout=15)
71
+ if not text:
72
+ continue
73
+
74
+ # 2. Wake word?
75
+ command = check_wake_word(text)
76
+ if command is None:
77
+ continue # not for us
78
+
79
+ print(f"\n🎯 Command: \"{command}\"")
80
+
81
+ # 3. Ask LLM
82
+ response = ask(command)
83
+
84
+ # 4. Execute or speak
85
+ if isinstance(response, dict):
86
+ # Tool‑call β†’ run action β†’ speak result β†’ Telegram notify (auto)
87
+ result = run_action(response)
88
+ speak(result)
89
+ else:
90
+ # Plain answer β†’ speak it
91
+ speak(response)
92
+
93
+ print()
94
+
95
+ except KeyboardInterrupt:
96
+ print("\n\nπŸ‘‹ Shutting down...")
97
+ speak("Goodbye!")
98
+ notify("πŸ”΄ Super-AI is now offline.")
99
+ break
100
+ except Exception as exc:
101
+ print(f"[error] {exc}")
102
+ speak("Sorry, something went wrong.")
103
+ notify(f"⚠️ Error: {exc}")
@@ -0,0 +1,217 @@
1
+ """
2
+ super_ai.actions
3
+ ────────────────
4
+ OS‑level action dispatcher.
5
+
6
+ LLM returns JSON β†’ this module executes it β†’ sends Telegram notification.
7
+ """
8
+
9
+ import os
10
+ import sys
11
+ import webbrowser
12
+ import subprocess
13
+ import platform
14
+ import urllib.parse
15
+ from pathlib import Path
16
+ from datetime import datetime, timedelta
17
+
18
+ from apscheduler.schedulers.background import BackgroundScheduler
19
+
20
+ from .config import cfg
21
+
22
+ # ═══════════════════════════════════════════════════
23
+ # Scheduler (reminders)
24
+ # ═══════════════════════════════════════════════════
25
+
26
+ _scheduler = BackgroundScheduler()
27
+ _scheduler.start()
28
+
29
+
30
+ # ═══════════════════════════════════════════════════
31
+ # Action functions
32
+ # ═══════════════════════════════════════════════════
33
+
34
+ def open_url(url: str) -> str:
35
+ """Open a URL in the default browser."""
36
+ if not url.startswith(("http://", "https://")):
37
+ url = "https://" + url
38
+ webbrowser.open(url)
39
+ return f"Opened {url}"
40
+
41
+
42
+ def launch_app(name: str) -> str:
43
+ """Launch a desktop app by name."""
44
+ system = platform.system()
45
+ try:
46
+ if system == "Darwin":
47
+ subprocess.Popen(["open", "-a", name])
48
+ elif system == "Linux":
49
+ subprocess.Popen(["xdg-open", name])
50
+ elif system == "Windows":
51
+ os.startfile(name)
52
+ else:
53
+ return f"Unsupported OS: {system}"
54
+ return f"Launched {name}"
55
+ except Exception as exc:
56
+ return f"Could not launch {name}: {exc}"
57
+
58
+
59
+ def play_media(path: str) -> str:
60
+ """Play audio/video with default system player."""
61
+ filepath = Path(path).expanduser().resolve()
62
+ if not filepath.is_file():
63
+ return f"File not found: {filepath}"
64
+
65
+ system = platform.system()
66
+ try:
67
+ if system == "Darwin":
68
+ subprocess.Popen(["open", str(filepath)])
69
+ elif system == "Linux":
70
+ subprocess.Popen(["xdg-open", str(filepath)])
71
+ elif system == "Windows":
72
+ os.startfile(str(filepath))
73
+ return f"Playing {filepath.name}"
74
+ except Exception as exc:
75
+ return f"Could not play: {exc}"
76
+
77
+
78
+ def send_message(to: str = "", body: str = "") -> str:
79
+ """Send Telegram text message."""
80
+ from .messaging import send_text
81
+ return send_text(to, body)
82
+
83
+
84
+ def send_voice_note(to: str = "", text: str = "") -> str:
85
+ """Generate voice note from text and send via Telegram."""
86
+ from .speech import text_to_wav
87
+ from .messaging import send_voice
88
+
89
+ wav_path = text_to_wav(text)
90
+ return send_voice(to, wav_path)
91
+
92
+
93
+ def analyse_video_action(path: str) -> str:
94
+ """Analyse a video and return summary."""
95
+ from .video import analyse_video
96
+ return analyse_video(path)
97
+
98
+
99
+ def set_reminder(text: str, seconds: int = 60) -> str:
100
+ """Set a reminder β€” speaks + sends Telegram notification when fires."""
101
+ from .speech import speak
102
+ from .messaging import notify
103
+
104
+ def _fire():
105
+ msg = f"⏰ Reminder: {text}"
106
+ print(f"\n[reminder] {msg}")
107
+ speak(f"Reminder: {text}")
108
+ notify(msg)
109
+
110
+ run_at = datetime.now() + timedelta(seconds=int(seconds))
111
+ _scheduler.add_job(_fire, "date", run_date=run_at)
112
+ return f"Reminder set for {seconds} seconds: {text}"
113
+
114
+
115
+ def send_whatsapp(phone: str, message: str) -> str:
116
+ """Send a WhatsApp message automatically using pywhatkit."""
117
+ try:
118
+ import pywhatkit
119
+
120
+ # Resolve contact name to phone number if it exists in config
121
+ contact_name = phone.lower().strip()
122
+ if contact_name in cfg.whatsapp_contacts:
123
+ phone = cfg.whatsapp_contacts[contact_name]
124
+
125
+ print(f"[action] Preparing to send WhatsApp message to {phone}...")
126
+
127
+ # sendwhatmsg_instantly opens whatsapp web, types the message, and presses enter.
128
+ pywhatkit.sendwhatmsg_instantly(phone, message, wait_time=15, tab_close=True, close_time=3)
129
+ return f"WhatsApp message sent to {phone}"
130
+ except ImportError:
131
+ return "pywhatkit library is not installed."
132
+ except Exception as exc:
133
+ return f"Could not send WhatsApp message: {exc}"
134
+
135
+
136
+ def search_web(query: str) -> str:
137
+ """Search the web using duckduckgo-search and open the browser so the user can see."""
138
+ try:
139
+ # Open browser to show the user
140
+ webbrowser.open(f"https://www.google.com/search?q={urllib.parse.quote(query)}")
141
+
142
+ from duckduckgo_search import DDGS
143
+ with DDGS() as ddgs:
144
+ results = list(ddgs.text(query, max_results=2))
145
+
146
+ if not results:
147
+ return f"I couldn't find any web results for {query}."
148
+
149
+ # Get the first snippet
150
+ snippet = results[0]['body']
151
+
152
+ return f"According to the web: {snippet}"
153
+ except ImportError:
154
+ return "duckduckgo-search library is not installed."
155
+ except Exception as exc:
156
+ return f"Could not search the web: {exc}"
157
+
158
+
159
+ # ═══════════════════════════════════════════════════
160
+ # Dispatcher
161
+ # ═══════════════════════════════════════════════════
162
+
163
+ _ACTION_MAP = {
164
+ "open_url": open_url,
165
+ "launch_app": launch_app,
166
+ "play_media": play_media,
167
+ "send_message": send_message,
168
+ "send_voice_note": send_voice_note,
169
+ "analyse_video": analyse_video_action,
170
+ "set_reminder": set_reminder,
171
+ "send_whatsapp": send_whatsapp,
172
+ "search_web": search_web,
173
+ }
174
+
175
+
176
+ def run_action(action_data: dict) -> str:
177
+ """
178
+ Execute an action from LLM's JSON output.
179
+ After completion, auto-sends Telegram notification.
180
+
181
+ Parameters
182
+ ----------
183
+ action_data : dict
184
+ {"action": "open_url", "args": {"url": "https://google.com"}}
185
+
186
+ Returns
187
+ -------
188
+ str β€” result message (spoken back to user)
189
+ """
190
+ from .messaging import notify
191
+
192
+ action_name = action_data.get("action", "")
193
+ args = action_data.get("args", {})
194
+
195
+ func = _ACTION_MAP.get(action_name)
196
+ if func is None:
197
+ supported = ", ".join(sorted(_ACTION_MAP.keys()))
198
+ return f"Unknown action: {action_name}. I can do: {supported}"
199
+
200
+ try:
201
+ print(f"[action] β–Ά {action_name}({args})")
202
+ result = func(**args)
203
+ print(f"[action] βœ“ {result}")
204
+
205
+ # ── Auto‑notify on Telegram ──
206
+ notify(f"βœ… Task done: {action_name}\n{result}")
207
+
208
+ return result
209
+
210
+ except Exception as exc:
211
+ err = f"Action {action_name} failed: {exc}"
212
+ print(f"[action] βœ— {err}")
213
+
214
+ # ── Notify failure too ──
215
+ notify(f"❌ Task failed: {action_name}\n{exc}")
216
+
217
+ return err
@@ -0,0 +1,118 @@
1
+ """
2
+ super_ai.config
3
+ ───────────────
4
+ Auto-config. User ko kuch set nahi karna.
5
+ """
6
+
7
+ import os
8
+ import json
9
+ from pathlib import Path
10
+ from dataclasses import dataclass, field
11
+
12
+ # Models stored in ~/.superai/models/
13
+ DATA_DIR = Path.home() / ".superai"
14
+ MODELS_DIR = DATA_DIR / "models"
15
+ CONFIG_FILE = DATA_DIR / "config.json"
16
+
17
+
18
+ @dataclass
19
+ class Config:
20
+ """All settings with defaults β€” works out of the box."""
21
+
22
+ # Paths
23
+ data_dir: Path = field(default_factory=lambda: DATA_DIR)
24
+ models_dir: Path = field(default_factory=lambda: MODELS_DIR)
25
+
26
+ # Speech Model
27
+ vosk_model_url: str = "https://alphacephei.com/vosk/models/vosk-model-small-en-in-0.4.zip"
28
+ vosk_model_name: str = "vosk-model-small-en-in-0.4"
29
+
30
+ # AI Brain
31
+ ollama_model: str = "llama3.2:1b"
32
+
33
+ # Wake word
34
+ wake_word: str = "hey ai"
35
+
36
+ # TTS
37
+ tts_rate: int = 150
38
+ tts_voice_index: int = 0
39
+
40
+ # Telegram (optional)
41
+ telegram_bot_token: str = ""
42
+ telegram_chat_id: str = ""
43
+
44
+ # WhatsApp Contacts (optional: {"name": "+91..."})
45
+ whatsapp_contacts: dict = field(default_factory=dict)
46
+
47
+ @classmethod
48
+ def load(cls) -> "Config":
49
+ DATA_DIR.mkdir(parents=True, exist_ok=True)
50
+ MODELS_DIR.mkdir(parents=True, exist_ok=True)
51
+
52
+ c = cls()
53
+ if CONFIG_FILE.exists():
54
+ try:
55
+ with open(CONFIG_FILE, "r") as f:
56
+ data = json.load(f)
57
+ c.wake_word = data.get("wake_word", c.wake_word)
58
+ c.tts_rate = data.get("tts_rate", c.tts_rate)
59
+ c.tts_voice_index = data.get("tts_voice_index", c.tts_voice_index)
60
+ c.telegram_bot_token = data.get("telegram_bot_token", c.telegram_bot_token)
61
+ c.telegram_chat_id = data.get("telegram_chat_id", c.telegram_chat_id)
62
+ c.whatsapp_contacts = data.get("whatsapp_contacts", c.whatsapp_contacts)
63
+ except Exception:
64
+ pass
65
+ return c
66
+
67
+ def save(self):
68
+ data = {
69
+ "wake_word": self.wake_word,
70
+ "tts_rate": self.tts_rate,
71
+ "tts_voice_index": self.tts_voice_index,
72
+ "telegram_bot_token": self.telegram_bot_token,
73
+ "telegram_chat_id": self.telegram_chat_id,
74
+ "whatsapp_contacts": self.whatsapp_contacts,
75
+ }
76
+ with open(CONFIG_FILE, "w") as f:
77
+ json.dump(data, f, indent=4)
78
+
79
+ @property
80
+ def vosk_model_path(self) -> Path:
81
+ return self.models_dir / self.vosk_model_name
82
+
83
+
84
+ cfg = Config.load()
85
+
86
+ def interactive_setup():
87
+ """Prompt user for setup if not done yet."""
88
+ if CONFIG_FILE.exists():
89
+ return
90
+
91
+ print("\n" + "="*50)
92
+ print("πŸš€ Welcome to Super-AI Setup!")
93
+ print("="*50)
94
+
95
+ # AI Name
96
+ print("\n1. Name your AI")
97
+ print("What should the AI respond to? (e.g., Jarvis, Friday, or Hey AI)")
98
+ name = input(f"Enter name [Default: {cfg.wake_word}]: ").strip()
99
+ if name:
100
+ cfg.wake_word = name.lower()
101
+
102
+ # Telegram
103
+ print("\n2. Telegram Notifications (Optional)")
104
+ print("Do you want to receive notifications on your phone when tasks are done?")
105
+ tg_choice = input("Enter y/n [Default: n]: ").strip().lower()
106
+ if tg_choice == 'y':
107
+ print("\n--- Telegram Setup Guide ---")
108
+ print("a. Open Telegram app and search for '@BotFather'")
109
+ print("b. Send '/newbot' and follow steps to get your HTTP API Token.")
110
+ cfg.telegram_bot_token = input("Paste your Bot Token here: ").strip()
111
+
112
+ print("\nc. Now search for '@userinfobot' on Telegram and send '/start'.")
113
+ print("d. It will reply with your 'Id'.")
114
+ cfg.telegram_chat_id = input("Paste your ID here: ").strip()
115
+ print("----------------------------")
116
+
117
+ cfg.save()
118
+ print("\nβœ… Setup complete! Settings saved.\n")