first commit
This commit is contained in:
445
agi-scripts/voicebot.py
Executable file
445
agi-scripts/voicebot.py
Executable file
@@ -0,0 +1,445 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import uuid
|
||||
import requests
|
||||
from asterisk.agi import AGI
|
||||
|
||||
|
||||
TMP_DIR = "/tmp/voicebot"
|
||||
|
||||
STT_URL = "http://127.0.0.1:5001/transcribe"
|
||||
TTS_URL = "http://127.0.0.1:5002/synthesize"
|
||||
|
||||
OLLAMA_URL = "http://127.0.0.1:11434/api/generate"
|
||||
OLLAMA_MODEL = "qwen3:14b"
|
||||
|
||||
|
||||
|
||||
def log(agi, msg):
|
||||
agi.verbose(
|
||||
"VOICEBOT: " + str(msg),
|
||||
1
|
||||
)
|
||||
|
||||
|
||||
|
||||
def tts_generate(agi, text, name):
|
||||
|
||||
try:
|
||||
|
||||
log(
|
||||
agi,
|
||||
"TTS request: " + text
|
||||
)
|
||||
|
||||
|
||||
r = requests.post(
|
||||
TTS_URL,
|
||||
json={
|
||||
"text": text
|
||||
},
|
||||
timeout=60
|
||||
)
|
||||
|
||||
r.raise_for_status()
|
||||
|
||||
|
||||
path = TMP_DIR + "/" + name + ".wav"
|
||||
|
||||
|
||||
with open(path, "wb") as f:
|
||||
f.write(r.content)
|
||||
|
||||
|
||||
log(
|
||||
agi,
|
||||
"TTS created " + path
|
||||
)
|
||||
|
||||
|
||||
return path
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
log(
|
||||
agi,
|
||||
"TTS ERROR " + str(e)
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
|
||||
def play_file(agi, path):
|
||||
|
||||
if not path:
|
||||
return
|
||||
|
||||
|
||||
try:
|
||||
|
||||
file = path.replace(
|
||||
".wav",
|
||||
""
|
||||
)
|
||||
|
||||
|
||||
agi.appexec(
|
||||
"Playback",
|
||||
file
|
||||
)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
log(
|
||||
agi,
|
||||
"PLAY ERROR " + str(e)
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
def record_audio(agi, callid):
|
||||
|
||||
|
||||
filename = TMP_DIR + "/" + callid + "_record"
|
||||
|
||||
|
||||
log(
|
||||
agi,
|
||||
"Recording user voice"
|
||||
)
|
||||
|
||||
|
||||
try:
|
||||
|
||||
|
||||
result = agi.send_command(
|
||||
|
||||
f'RECORD FILE "{filename}" wav "#" 10000 0 s=5'
|
||||
|
||||
)
|
||||
|
||||
|
||||
log(
|
||||
agi,
|
||||
"Record result: " + str(result)
|
||||
)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
|
||||
log(
|
||||
agi,
|
||||
"Record ERROR " + str(e)
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
|
||||
wav = filename + ".wav"
|
||||
|
||||
|
||||
if os.path.exists(wav):
|
||||
|
||||
|
||||
size=os.path.getsize(wav)
|
||||
|
||||
|
||||
log(
|
||||
agi,
|
||||
f"Recording saved {wav} size={size}"
|
||||
)
|
||||
|
||||
|
||||
return wav
|
||||
|
||||
|
||||
|
||||
log(
|
||||
agi,
|
||||
"Recording file missing"
|
||||
)
|
||||
|
||||
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def stt_process(agi,wav):
|
||||
|
||||
|
||||
try:
|
||||
|
||||
|
||||
log(
|
||||
agi,
|
||||
"Sending audio to STT"
|
||||
)
|
||||
|
||||
|
||||
with open(wav,"rb") as f:
|
||||
|
||||
|
||||
r=requests.post(
|
||||
|
||||
STT_URL,
|
||||
|
||||
files={
|
||||
"audio":f
|
||||
},
|
||||
|
||||
timeout=120
|
||||
|
||||
)
|
||||
|
||||
|
||||
data=r.json()
|
||||
|
||||
|
||||
text=data.get(
|
||||
"text",
|
||||
""
|
||||
)
|
||||
|
||||
|
||||
log(
|
||||
agi,
|
||||
"STT text: " + text
|
||||
)
|
||||
|
||||
|
||||
return text
|
||||
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
|
||||
log(
|
||||
agi,
|
||||
"STT ERROR " + str(e)
|
||||
)
|
||||
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def ask_ai(agi,text):
|
||||
|
||||
|
||||
try:
|
||||
|
||||
|
||||
log(
|
||||
agi,
|
||||
"AI request: " + text
|
||||
)
|
||||
|
||||
|
||||
prompt = """
|
||||
تو دستیار تلفنی هستی.
|
||||
کوتاه و طبیعی جواب بده.
|
||||
حداکثر دو جمله.
|
||||
|
||||
کاربر:
|
||||
""" + text
|
||||
|
||||
|
||||
|
||||
r=requests.post(
|
||||
|
||||
OLLAMA_URL,
|
||||
|
||||
json={
|
||||
|
||||
"model":OLLAMA_MODEL,
|
||||
|
||||
"prompt":prompt,
|
||||
|
||||
"stream":False,
|
||||
|
||||
"options":{
|
||||
"num_predict":80,
|
||||
"temperature":0.3
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
timeout=180
|
||||
|
||||
)
|
||||
|
||||
|
||||
data=r.json()
|
||||
|
||||
|
||||
answer=data.get(
|
||||
"response",
|
||||
""
|
||||
)
|
||||
|
||||
|
||||
log(
|
||||
agi,
|
||||
"AI response: " + answer
|
||||
)
|
||||
|
||||
|
||||
return answer
|
||||
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
|
||||
log(
|
||||
agi,
|
||||
"AI ERROR " + str(e)
|
||||
)
|
||||
|
||||
|
||||
return "ارتباط با هوش مصنوعی برقرار نشد."
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
|
||||
os.makedirs(
|
||||
TMP_DIR,
|
||||
exist_ok=True
|
||||
)
|
||||
|
||||
|
||||
agi=AGI()
|
||||
|
||||
|
||||
callid=str(uuid.uuid4())[:8]
|
||||
|
||||
|
||||
log(
|
||||
agi,
|
||||
"Call started " + callid
|
||||
)
|
||||
|
||||
|
||||
try:
|
||||
|
||||
|
||||
agi.answer()
|
||||
|
||||
|
||||
|
||||
welcome=tts_generate(
|
||||
|
||||
agi,
|
||||
|
||||
"سلام، من دستیار صوتی هوشمند هستم. بفرمایید.",
|
||||
|
||||
callid+"_welcome"
|
||||
|
||||
)
|
||||
|
||||
|
||||
play_file(
|
||||
agi,
|
||||
welcome
|
||||
)
|
||||
|
||||
|
||||
|
||||
wav=record_audio(
|
||||
agi,
|
||||
callid
|
||||
)
|
||||
|
||||
|
||||
if not wav:
|
||||
|
||||
log(
|
||||
agi,
|
||||
"No recording"
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
text=stt_process(
|
||||
agi,
|
||||
wav
|
||||
)
|
||||
|
||||
|
||||
|
||||
if text:
|
||||
|
||||
answer=ask_ai(
|
||||
agi,
|
||||
text
|
||||
)
|
||||
|
||||
else:
|
||||
|
||||
answer="صدای شما را دریافت نکردم."
|
||||
|
||||
|
||||
|
||||
reply=tts_generate(
|
||||
|
||||
agi,
|
||||
|
||||
answer,
|
||||
|
||||
callid+"_reply"
|
||||
|
||||
)
|
||||
|
||||
|
||||
play_file(
|
||||
agi,
|
||||
reply
|
||||
)
|
||||
|
||||
|
||||
log(
|
||||
agi,
|
||||
"Call completed"
|
||||
)
|
||||
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
|
||||
log(
|
||||
agi,
|
||||
"MAIN ERROR " + str(e)
|
||||
)
|
||||
|
||||
|
||||
finally:
|
||||
|
||||
agi.hangup()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
main()
|
||||
Reference in New Issue
Block a user