1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232 | """
NEO VOICE ASSISTANT โ Jarvis-style voice interface to the REAL Neo agent
Wake word: "Hey Neo" โ Neo responds "Peace God" โ continuous conversation
Routes through OpenClaw agent (same brain as Telegram) with full MCP tools,
MemPalace, memory-lancedb, Shopify โ everything.
Usage:
python3 neo-voice-assistant.py
"""
import os
import sys
import json
import time
import tempfile
import subprocess
import re
import speech_recognition as sr
import httpx
# โโ Config โโ
def _env(name, default=None):
"""Env first, then the gitignored .env next to this script. Never hardcode; fail loud."""
val = os.environ.get(name)
if val:
return val
env_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env")
if os.path.exists(env_path):
with open(env_path) as f:
for line in f:
line = line.strip()
if line.startswith(name + "="):
return line.split("=", 1)[1].strip()
return default
ELEVENLABS_KEY = _env("ELEVENLABS_API_KEY")
VOICE_ID = _env("ELEVENLABS_VOICE_ID", "YjyOkKA67HmAqGFoFWMd") # 19Keys cloned voice
TELEGRAM_TOKEN = _env("NEO_TELEGRAM_BOT_TOKEN")
TELEGRAM_CHAT = _env("NEO_TELEGRAM_CHAT_ID")
if not (ELEVENLABS_KEY and TELEGRAM_TOKEN and TELEGRAM_CHAT):
sys.exit(
"ERROR: missing ELEVENLABS_API_KEY / NEO_TELEGRAM_BOT_TOKEN / NEO_TELEGRAM_CHAT_ID "
"(set in env or the .env at repo root โ see .env.example)."
)
WAKE_WORDS = ["hey neo", "hey neil", "a neo", "hey new", "henio", "hey nio"]
EXIT_WORDS = ["peace god", "peace gods", "goodbye", "shut down", "go to sleep"]
def speak(text):
"""Speak using 19Keys' cloned voice via ElevenLabs."""
if not text or len(text.strip()) < 2:
return
clean = text.replace("**", "").replace("*", "").replace("#", "").replace("`", "")
clean = re.sub(r'\[.*?\]\(.*?\)', '', clean) # Remove markdown links
clean = clean.replace("\n- ", ". ").replace("\n", ". ").strip()
clean = clean[:800] # Cap for TTS
print(f"\n ๐ NEO: {text}\n")
try:
resp = httpx.post(
f"https://api.elevenlabs.io/v1/text-to-speech/{VOICE_ID}/stream",
headers={"xi-api-key": ELEVENLABS_KEY, "Content-Type": "application/json"},
json={
"text": clean,
"model_id": "eleven_turbo_v2_5",
"voice_settings": {"stability": 0.5, "similarity_boost": 0.85, "style": 0.3},
},
timeout=30,
)
if resp.status_code == 200:
with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as f:
f.write(resp.content)
temp_path = f.name
subprocess.run(["afplay", temp_path], check=True)
os.unlink(temp_path)
else:
subprocess.run(["say", "-v", "Alex", clean[:200]])
except Exception as e:
print(f" [TTS Error: {e}]")
subprocess.run(["say", "-v", "Alex", clean[:200]])
def ask_neo_via_openclaw(message):
"""Send message through the REAL Neo OpenClaw agent and get response."""
# Send to Neo via Telegram (the actual Neo agent picks it up)
try:
send_resp = httpx.post(
f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage",
json={"chat_id": TELEGRAM_CHAT, "text": f"[VOICE] {message}"},
timeout=10,
)
sent_msg_id = send_resp.json().get("result", {}).get("message_id", 0)
except Exception as e:
return f"Couldn't reach Neo. {e}"
# Poll for Neo's response (he replies in the same chat)
print(" โณ [Neo is thinking...]")
start = time.time()
last_update_id = 0
for attempt in range(20): # Up to 40 seconds
time.sleep(2)
try:
resp = httpx.get(
f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/getUpdates",
params={"offset": -3, "limit": 3, "timeout": 1},
timeout=10,
)
updates = resp.json().get("result", [])
for update in reversed(updates):
msg = update.get("message", {})
# Look for bot replies after our sent message
msg_id = msg.get("message_id", 0)
from_bot = msg.get("from", {}).get("is_bot", False)
text = msg.get("text", "")
if from_bot and msg_id > sent_msg_id and text:
elapsed = time.time() - start
print(f" โ [Neo responded in {elapsed:.1f}s]")
return text
except Exception:
pass
if time.time() - start > 40:
break
return "I'm still processing that. Check Telegram for my full response."
def listen(recognizer, mic, timeout=None, phrase_limit=10):
"""Listen for speech and return transcription."""
try:
with mic as source:
audio = recognizer.listen(source, timeout=timeout, phrase_time_limit=phrase_limit)
text = recognizer.recognize_google(audio).lower()
return text
except sr.WaitTimeoutError:
return None
except sr.UnknownValueError:
return None
except Exception as e:
print(f" [Listen error: {e}]")
return None
def main():
recognizer = sr.Recognizer()
recognizer.energy_threshold = 300
recognizer.dynamic_energy_threshold = True
# Find MacBook Pro microphone
mic_index = None
for i, name in enumerate(sr.Microphone.list_microphone_names()):
if "MacBook" in name or "Built-in" in name:
mic_index = i
break
mic = sr.Microphone(device_index=mic_index) if mic_index is not None else sr.Microphone()
print(f"\n [Using mic: {sr.Microphone.list_microphone_names()[mic_index or 0]}]")
print("\n [Calibrating...]")
with mic as source:
recognizer.adjust_for_ambient_noise(source, duration=2)
print("\n" + "=" * 50)
print(" โก NEO VOICE ASSISTANT โก")
print(" Powered by OpenClaw Agent (full Neo brain)")
print(" Say 'Hey Neo' to activate")
print(" Continuous conversation until 'Peace God'")
print("=" * 50 + "\n")
while True:
# โโ PHASE 1: Wait for wake word โโ
print(" ๐ [Waiting for 'Hey Neo'...]")
text = listen(recognizer, mic, timeout=None, phrase_limit=4)
if text is None:
continue
if not any(wake in text for wake in WAKE_WORDS):
continue
# โโ PHASE 2: Activated โโ
print("\n โก [ACTIVATED]")
speak("Peace god. What's on your mind?")
# โโ PHASE 3: Continuous conversation through real Neo โโ
silence_count = 0
while True:
print(" ๐ค [Listening...]")
text = listen(recognizer, mic, timeout=8, phrase_limit=20)
if text is None:
silence_count += 1
if silence_count >= 3:
speak("I'm here when you need me.")
break
continue
silence_count = 0
print(f"\n ๐ฃ YOU: {text}")
# Check for exit
if any(exit_word in text for exit_word in EXIT_WORDS):
speak("Peace god. The kingdom stays running.")
break
# Check for re-engage
if any(wake in text for wake in WAKE_WORDS):
speak("I'm right here. What you need?")
continue
# โโ Route through REAL Neo agent via Telegram โโ
response = ask_neo_via_openclaw(text)
speak(response)
print("\n ๐ค [Session ended โ back to wake word mode]\n")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\n Neo Voice Assistant offline. Peace god.\n")
|