# Setup — jarvis-voice

This is a single Python script (`neo-voice-assistant.py`) that listens for a
wake word on your Mac's microphone, sends what you say to a Telegram bot, waits
for that bot's agent to reply, and speaks the reply out loud.

Read this top to bottom before you run anything — the script deliberately
refuses to start if any of four required values are missing, so there is
nothing to half-configure.

## What you need

| Requirement | Why | Have it already? |
|---|---|---|
| macOS | The script shells out to `afplay` and `say`, both macOS-only binaries. It will not run on Windows or Linux as-is. | Check: you're on a Mac. |
| Python 3 (any recent version) | Runs the script. No version-specific syntax is used. | Check: `python3 --version` |
| Homebrew | Used to install `portaudio`, a system library `PyAudio` needs to compile. | Check: `brew --version` |
| A working microphone | `speech_recognition` records from it to detect the wake word and your speech. | Built into every Mac; external mic works too. |
| Internet connection | Speech-to-text calls Google's free Web Speech API; replies go through Telegram's API; voice synthesis calls ElevenLabs' API. | — |
| ElevenLabs account + API key | The script calls ElevenLabs for cloned/branded text-to-speech. **The script exits immediately at startup if this key is missing** — it is not truly optional in the current code, even though `say` can produce speech if the ElevenLabs *call* itself fails at runtime. | Sign up free at elevenlabs.io, grab an API key. |
| An ElevenLabs voice ID | Which voice ElevenLabs speaks in. Use your own cloned voice or any library voice. | From your ElevenLabs dashboard. |
| A Telegram bot (via @BotFather) + its chat ID | This script does not contain any AI itself — it POSTs what you say to a Telegram chat and polls for a reply. You need a bot token, and a separate agent (yours — a Claude Code agent, an n8n flow, anything) that actually reads that chat and replies. | Create via https://t.me/BotFather; get the chat ID from the chat you'll use. |
| **NOT needed:** Docker | Nothing in this repo runs in a container. | — |
| **NOT needed:** GPU | All processing here is either a network call (ElevenLabs, Telegram, Google STT) or macOS's own `say`. | — |
| **NOT needed:** any local AI model | The "brain" is external — whatever agent answers your Telegram bot. This repo has no model code. | — |

Note: the README's "Make it your own" section describes an optional local-TTS
upgrade path (Chatterbox via mlx-audio, Apple Silicon only) as something you
could wire in yourself. **The code as shipped does not call Chatterbox or
mlx-audio at all** — today it's a two-tier voice path (ElevenLabs, falling
back to `say` only if the ElevenLabs *request* fails), and ElevenLabs is a
hard requirement to start the script at all. If you want the "runs on any Mac
with zero paid keys" version, you'd need to make `ELEVENLABS_API_KEY` truly
optional in the code and default straight to `say` — see Troubleshooting.

## Install, step by step

1. **Install Homebrew** (skip if `brew --version` already prints something):
   ```bash
   /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
   ```
   What it does: installs the Homebrew package manager, used below for `portaudio`.
   Check it worked: `brew --version` → prints `Homebrew x.y.z`.

2. **Install Python 3** (skip if `python3 --version` already prints 3.x):
   ```bash
   brew install python3
   ```
   Check it worked: `python3 --version` → prints `Python 3.x.x`.

3. **Install portaudio** (system library `PyAudio` needs to build/run):
   ```bash
   brew install portaudio
   ```
   What it does: installs the audio I/O library `PyAudio` links against for
   microphone access.
   Check it worked: `brew list portaudio` → lists installed files, no error.

4. **Clone this repo and enter it:**
   ```bash
   git clone <this-repo-url> jarvis-voice
   cd jarvis-voice
   ```
   Check it worked: `ls` → shows `neo-voice-assistant.py`, `requirements.txt`, `env.example`.

5. **Create and activate a virtual environment:**
   ```bash
   python3 -m venv .venv
   source .venv/bin/activate
   ```
   What it does: isolates this project's Python packages from the rest of your system.
   Check it worked: your shell prompt now starts with `(.venv)`.

6. **Install the Python packages:**
   ```bash
   pip install -r requirements.txt
   ```
   What it does: installs `SpeechRecognition`, `httpx`, and `PyAudio` (the
   three, and only three, imports this script needs beyond the Python
   standard library).
   Check it worked: `python3 -c "import speech_recognition, httpx, pyaudio; print('ok')"` → prints `ok`.

   If `PyAudio` fails to build here, re-run step 3 first — this is almost
   always a missing `portaudio`.

## Configure

Every value below is read from either a real environment variable or a
`.env` file placed next to the script (the script checks the real
environment first, then falls back to reading `.env` itself — no
`python-dotenv` package involved, it parses the file by hand).

1. Copy the example file:
   ```bash
   cp env.example .env
   ```
2. Edit `.env` and fill in real values in place of these placeholders:
   ```
   ELEVENLABS_API_KEY=YOUR_KEY_HERE  # keyzhub-allow
   ELEVENLABS_VOICE_ID=YOUR_VOICE_ID_HERE  # keyzhub-allow
   NEO_TELEGRAM_BOT_TOKEN=YOUR_TELEGRAM_BOT_TOKEN_HERE  # keyzhub-allow
   NEO_TELEGRAM_CHAT_ID=YOUR_TELEGRAM_CHAT_ID_HERE  # keyzhub-allow
   ```
   - `ELEVENLABS_API_KEY` — from your ElevenLabs account dashboard.
   - `ELEVENLABS_VOICE_ID` — the ID of the voice you want to speak with
     (clone your own, or copy an ID from ElevenLabs' voice library).
   - `NEO_TELEGRAM_BOT_TOKEN` — the token @BotFather gives you when you
     create a bot.
   - `NEO_TELEGRAM_CHAT_ID` — the numeric ID of the chat between you and
     that bot (or a private group containing the bot) — this is where the
     script posts what you say and where it expects the agent's reply.

`.env` is already listed in `.gitignore` — it will not be committed.

All four values are required. The script calls `sys.exit()` with an error
message immediately on startup if any one of them is missing.

## Run it

```bash
source .venv/bin/activate   # if not already active
python3 neo-voice-assistant.py
```

Success looks like:
```
  [Using mic: <your microphone name>]

  [Calibrating...]

==================================================
  ⚡ NEO VOICE ASSISTANT ⚡
  Powered by OpenClaw Agent (full Neo brain)
  Say 'Hey Neo' to activate
  Continuous conversation until 'Peace God'
==================================================

  👂 [Waiting for 'Hey Neo'...]
```

Say "Hey Neo" out loud. It should print `⚡ [ACTIVATED]` and speak "Peace
god. What's on your mind?" out loud through your speakers. Anything you say
after that gets sent to your Telegram bot, and whatever replies in that chat
gets spoken back. Say "peace god," "goodbye," "shut down," or "go to sleep"
to end the conversation and return to wake-word listening. Ctrl-C to quit
entirely.

## Troubleshooting

1. **`ERROR: missing ELEVENLABS_API_KEY / NEO_TELEGRAM_BOT_TOKEN / NEO_TELEGRAM_CHAT_ID` at startup.**
   One of the four required values in `.env` (or your real environment) is
   empty or the file wasn't copied. Fix: `cp env.example .env`, fill in all
   four values, confirm the file is actually named `.env` (not `env` or
   `.env.txt`) and sits next to `neo-voice-assistant.py`.

2. **`PyAudio` fails to install, or `python3 neo-voice-assistant.py` errors on `import pyaudio` / can't open the microphone.**
   `PyAudio` needs the system `portaudio` library, which is not a Python
   package. Fix: `brew install portaudio`, then `pip install -r requirements.txt` again inside the activated venv.

3. **It never hears "Hey Neo," or hears you but nothing ever gets a reply.**
   Two separate causes, both external to this script:
   - No transcription happening at all → check your internet connection;
     `recognize_google()` requires a live connection to Google's free Web
     Speech API (no key needed, but no connection means no transcription).
   - Transcribes fine but always says "I'm still processing that... check
     Telegram" → the bot token/chat ID are valid but nothing is actually
     listening to that Telegram chat and replying. This script only relays
     messages; you must have a separate agent (your own bot logic) watching
     `NEO_TELEGRAM_CHAT_ID` and posting a reply within ~40 seconds, or you'll
     always get the fallback message.
