diff --git a/bin/claude_voice.py b/bin/claude_voice.py new file mode 100755 index 0000000..4cfbd00 --- /dev/null +++ b/bin/claude_voice.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +# [Script] : claude_voice.py +# [Apps] : MRDEVS TOOLS +# [Description]: Convierte instrucciones de voz a texto para Claude Code +# [Author] : Cortana Rosero One +# [Generated] : Created by Claude Code (claude-3-7-sonnet-20250219) +# [Created] : 2025/03/30 16:45:00 +# [Modified] : 2025/03/30 16:45:00 +# [Version] : 1.3.0 +# [Use Notes] : Instalar dependencias: pip install speechrecognition pydub pyaudio + +import os +import sys +import subprocess +import argparse +import time +import speech_recognition as sr +from pydub import AudioSegment +from pydub.playback import play + +# Colores para la salida +class Colors: + PURPLE = '\033[95m' + BLUE = '\033[94m' + CYAN = '\033[96m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + END = '\033[0m' + +def play_sound(sound_type): + """Reproduce un sonido para indicar estados""" + sounds_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "sounds") + if not os.path.exists(sounds_dir): + os.makedirs(sounds_dir, exist_ok=True) + + # Usar sonidos predeterminados si existen, o crearlos + sound_files = { + "start": os.path.join(sounds_dir, "start.mp3"), + "stop": os.path.join(sounds_dir, "stop.mp3"), + "error": os.path.join(sounds_dir, "error.mp3") + } + + # Si no hay archivo de sonido, usar un beep básico + try: + if os.path.exists(sound_files[sound_type]): + sound = AudioSegment.from_file(sound_files[sound_type]) + play(sound) + else: + # Frecuencias para diferentes tipos de sonidos + if sound_type == "start": + print("\a") # Beep básico del sistema + elif sound_type == "stop": + print("\a") + time.sleep(0.1) + print("\a") + elif sound_type == "error": + print("\a") + time.sleep(0.1) + print("\a") + time.sleep(0.1) + print("\a") + except Exception: + # Si hay algún error reproduciendo el sonido, simplemente continuamos + pass + +def recognize_speech(language="es-ES"): + """Captura audio del micrófono y lo convierte a texto""" + recognizer = sr.Recognizer() + + with sr.Microphone() as source: + print(f"{Colors.BLUE}[Claude Voice]{Colors.END} {Colors.YELLOW}Escuchando...{Colors.END} (Presiona Ctrl+C para detener)") + play_sound("start") + + # Ajustar para ruido ambiental + recognizer.adjust_for_ambient_noise(source, duration=0.5) + try: + audio = recognizer.listen(source, timeout=10, phrase_time_limit=15) + print(f"{Colors.BLUE}[Claude Voice]{Colors.END} {Colors.GREEN}Procesando audio...{Colors.END}") + + # Intentar reconocer usando Google Speech Recognition + text = recognizer.recognize_google(audio, language=language) + play_sound("stop") + return text + except sr.UnknownValueError: + play_sound("error") + print(f"{Colors.RED}No se pudo entender el audio{Colors.END}") + return None + except sr.RequestError as e: + play_sound("error") + print(f"{Colors.RED}Error en el servicio de reconocimiento: {e}{Colors.END}") + return None + except Exception as e: + play_sound("error") + print(f"{Colors.RED}Error: {e}{Colors.END}") + return None + +def send_to_claude(text, silent=False): + """Envía el texto reconocido a Claude Code""" + if not text: + return False + + if not silent: + print(f"{Colors.BLUE}[Claude Voice]{Colors.END} Enviando a Claude Code: {Colors.BOLD}{text}{Colors.END}") + + try: + # Usar la ruta de instalación de Claude Code + claude_cmd = "claude" if os.system("which claude > /dev/null 2>&1") == 0 else "/usr/local/bin/claude" + + # Enviar el texto como entrada a Claude + result = subprocess.run([claude_cmd, text], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + check=False) + + if result.returncode != 0: + print(f"{Colors.RED}Error al ejecutar Claude Code: {result.stderr}{Colors.END}") + return False + return True + except Exception as e: + print(f"{Colors.RED}Error al ejecutar Claude Code: {e}{Colors.END}") + return False + +def interactive_mode(language="es-ES", continuous=False): + """Modo interactivo que escucha continuamente comandos de voz""" + print(f"{Colors.BLUE}[Claude Voice]{Colors.END} {Colors.GREEN}Modo interactivo iniciado. Di tus instrucciones para Claude Code.{Colors.END}") + print(f"{Colors.BLUE}[Claude Voice]{Colors.END} {Colors.YELLOW}Di 'salir' o 'terminar' para finalizar{Colors.END}") + + try: + while True: + text = recognize_speech(language) + + if text: + text = text.strip() + print(f"{Colors.BLUE}[Claude Voice]{Colors.END} {Colors.GREEN}Reconocido: {Colors.BOLD}{text}{Colors.END}") + + # Verificar comandos de salida + if text.lower() in ["salir", "terminar", "exit", "quit", "goodbye", "bye"]: + print(f"{Colors.BLUE}[Claude Voice]{Colors.END} {Colors.YELLOW}Saliendo del modo de voz...{Colors.END}") + break + + # Enviar a Claude Code + success = send_to_claude(text) + + # Si no es modo continuo, salir después del primer comando exitoso + if not continuous and success: + break + + # Pausa breve entre reconocimientos + if continuous: + time.sleep(1) + + except KeyboardInterrupt: + print(f"\n{Colors.BLUE}[Claude Voice]{Colors.END} {Colors.YELLOW}Modo de voz interrumpido por el usuario{Colors.END}") + + print(f"{Colors.BLUE}[Claude Voice]{Colors.END} {Colors.GREEN}¡Hasta pronto!{Colors.END}") + +def main(): + parser = argparse.ArgumentParser(description='Claude Code Voice - Convierte voz a texto para Claude Code') + parser.add_argument('-l', '--language', default='es-ES', help='Idioma para reconocimiento (ej. es-ES, en-US)') + parser.add_argument('-c', '--continuous', action='store_true', help='Modo continuo - escucha constantemente hasta que digas "salir"') + parser.add_argument('-t', '--text', help='Texto a enviar directamente (sin reconocimiento de voz)') + parser.add_argument('-s', '--silent', action='store_true', help='Modo silencioso - no muestra mensajes extra') + + args = parser.parse_args() + + if args.text: + # Modo de texto directo + send_to_claude(args.text, args.silent) + else: + # Modo interactivo con reconocimiento de voz + interactive_mode(args.language, args.continuous) + +if __name__ == "__main__": + main() \ No newline at end of file