diff --git a/voicebot-app/Dockerfile b/voicebot-app/Dockerfile index 6e09262..8e0c539 100644 --- a/voicebot-app/Dockerfile +++ b/voicebot-app/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8-slim +FROM python:3.8.5-slim WORKDIR /app diff --git a/voicebot-app/Procfile b/voicebot-app/Procfile new file mode 100644 index 0000000..e6cb5ad --- /dev/null +++ b/voicebot-app/Procfile @@ -0,0 +1 @@ +web: python app.py \ No newline at end of file diff --git a/voicebot-app/app.py b/voicebot-app/app.py new file mode 100644 index 0000000..e6f4e38 --- /dev/null +++ b/voicebot-app/app.py @@ -0,0 +1,256 @@ +''' Import Libraries ''' + +from flask import Flask, request, jsonify, render_template +import os +import json +from twilio.twiml.messaging_response import MessagingResponse +from twilio.rest import Client +import requests +from ibm_watson import AssistantV2 +from ibm_cloud_sdk_core.authenticators import IAMAuthenticator +from ibm_watson import SpeechToTextV1 +from ibm_watson.websocket import RecognizeCallback, AudioSource +from ibm_watson import TextToSpeechV1 +import logging as logger + +''' Initialize Flask Variables ''' +logger.basicConfig(level="DEBUG") + +app = Flask(__name__) +account_sid = "" +auth_token = "" +filePath = './static/audios/' +apikey = '' +url = '' +assistantid = '' +sessionid = '' + +######################### +# Watson Assistant Sessions +######################### + +def createSession(): + global sessionid + session = assistant.create_session(assistantid).get_result() + sessionid = session.get('session_id') + print('New Session created ID: ', sessionid) + +def destroySession(): + try: + response = assistant.delete_session( + assistant_id=assistantid, session_id=sessionid).get_result() + print(response) + except Exception as e: + pass + +def session(): + global sessionid + if sessionid == '': + createSession() + else: + destroySession() + createSession() + +def watsonAssistant(msg): + + message = assistant.message( + assistantid, + sessionid, + input={'text': msg} + ).get_result() + + try: + payload = { + "message": message['output']['generic'][0]['text'] + } + except: + payload = { + "message": "Eu não entendi. Você pode tentar reformular a frase." + } + + return payload['message'] + +class MyRecognizeCallback(RecognizeCallback): + def __init__(self): + RecognizeCallback.__init__(self) + + def on_data(self, data): + print(json.dumps(data, indent=2)) + + def on_error(self, error): + print('Error received: {}'.format(error)) + + def on_inactivity_timeout(self, error): + print('Inactivity timeout: {}'.format(error)) + +myRecognizeCallback = MyRecognizeCallback() + +def speechtoText(): + with open('speechtotext.json', 'r') as credentialsFile: + credentials = json.loads(credentialsFile.read()) + + stt_apikey = credentials.get('apikey') + stt_url = credentials.get('url') + + authenticator = IAMAuthenticator(stt_apikey) + speech_to_text = SpeechToTextV1( + authenticator=authenticator + ) + + speech_to_text.set_service_url(stt_url) + + with open(filePath+"input.ogg",'rb') as audio_file: + speech_recognition_results = speech_to_text.recognize( + audio=audio_file, + content_type='audio/ogg', + recognize_callback=myRecognizeCallback, + model='pt-BR_BroadbandModel', + ).get_result() + text_translation = speech_recognition_results["results"][0]["alternatives"][0]["transcript"] + return text_translation.strip() + +@app.route('/', methods=['GET', 'POST']) +def index(): + + try: + with open('watsonassistant.json', 'r') as credentialsFile: + credentials = json.loads(credentialsFile.read()) + global apikey, url, assistantid, authenticator, assistant + apikey = credentials.get('apikey') + url = credentials.get('url') + assistantid = credentials.get('assistant-id') + + authenticator = IAMAuthenticator(apikey) + assistant = AssistantV2( + version='2020-04-01', + authenticator=authenticator + ) + + assistant.set_service_url(url) + + except: + logger.debug('Watson Assistant Credentials not found!') + + if request.method == 'GET': + return render_template('index.html') + if request.method == 'POST': + + getTwilioCredentials() + + ResponseMsg = json.dumps(request.form.to_dict(), indent=2) + respo = json.loads(ResponseMsg) + print(respo) + + receivedMsg = respo.get('Body') + + if receivedMsg == 'Ola' or receivedMsg == 'ola' or receivedMsg == 'Hi' or receivedMsg == 'hi': + session() + message = watsonAssistant('') + resp = MessagingResponse() + resp.message(message) + return str(resp) + + if respo.get('MediaContentType0') == 'audio/ogg': + audio_url = respo.get('MediaUrl0') + + res = requests.get(audio_url) + with open(filePath+"input.ogg", "wb") as o: + o.write(res.content) + + transcript = speechtoText() + message = watsonAssistant(transcript) + resp = MessagingResponse() + resp.message(message) + return str(resp) + + if respo.get('Body') == 'Test': + message = "If you are able to get this message then the application works" + resp = MessagingResponse() + resp.message(message) + return str(resp) + + message = watsonAssistant(receivedMsg) + resp = MessagingResponse() + resp.message(message) + return str(resp) + +@app.route('/storeTwilioCredentials', methods=['POST']) +def storeTwilioCredentials(): + receivedPayload = json.loads(request.get_data()) + + data = { + "account_sid": receivedPayload.get('account_sid'), + "auth_token": receivedPayload.get('auth_token') + } + + with open('twiliocredentials.json', 'w') as fs: + json.dump(data, fs, indent=2) + + return jsonify({"status": "Configured"}) + +@app.route('/getTwilioCredentials', methods=['GET']) +def getTwilioCredentials(): + try: + with open('twiliocredentials.json') as creds: + twiliocreds = json.loads(creds.read()) + twiliocreds.get('auth_token') + return jsonify({"status": "Configured"}) + except: + return jsonify({"status": "Not Configured"}) + +@app.route('/storeWaCredentials', methods=['POST']) +def storeWaCredentials(): + receivedPayload = json.loads(request.get_data()) + + data = { + "apikey": receivedPayload.get('wa_apikey'), + "assistant-id": receivedPayload.get('wa_assistant_id'), + "url": receivedPayload.get('wa_url') + } + + with open('watsonassistant.json', 'w') as fs: + json.dump(data, fs, indent=2) + + return jsonify({"status": "Configured"}) + +@app.route('/getWaCredentials', methods=['GET']) +def getWaCredentials(): + try: + with open('watsonassistant.json') as creds: + wacreds = json.loads(creds.read()) + wacreds.get('apikey') + return jsonify({"status": "Configured"}) + except: + return jsonify({"status": "Not Configured"}) + +@app.route('/storeSttCredentials', methods=['POST']) +def storeSttCredentials(): + receivedPayload = json.loads(request.get_data()) + + data = { + "apikey": receivedPayload.get('stt_apikey'), + "url": receivedPayload.get('stt_url') + } + + with open('speechtotext.json', 'w') as fs: + json.dump(data, fs, indent=2) + + return jsonify({"status": "Configured"}) + +@app.route('/getSttCredentials', methods=['GET']) +def getSttCredentials(): + try: + with open('speechtotext.json') as creds: + sttcreds = json.loads(creds.read()) + sttcreds.get('apikey') + return jsonify({"status": "Configured"}) + except: + return jsonify({"status": "Not Configured"}) + + +''' Start the Server ''' + +port = os.getenv('VCAP_APP_PORT', '8080') +if __name__ == "__main__": + app.secret_key = os.urandom(12) + app.run(debug=True, host='0.0.0.0', port=port) \ No newline at end of file diff --git a/voicebot-app/deploy.yaml b/voicebot-app/deploy.yaml index 7e2fec4..3a493ec 100644 --- a/voicebot-app/deploy.yaml +++ b/voicebot-app/deploy.yaml @@ -2,6 +2,7 @@ apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2 kind: Deployment metadata: name: voicebot-app + namespace: default labels: app: flask spec: @@ -25,6 +26,7 @@ apiVersion: v1 kind: Service metadata: name: voicebot-app-service + namespace: default spec: selector: app: flask @@ -34,8 +36,8 @@ spec: targetPort: 8080 nodePort: 32000 type: NodePort - - --- + +--- apiVersion: route.openshift.io/v1 kind: Route @@ -48,4 +50,4 @@ spec: kind: Service name: voicebot-app-service port: - targetPort: 8080 + targetPort: 8080 \ No newline at end of file diff --git a/voicebot-app/manifest.yml b/voicebot-app/manifest.yml new file mode 100644 index 0000000..fbf0f00 --- /dev/null +++ b/voicebot-app/manifest.yml @@ -0,0 +1,4 @@ +applications: + - name: whatsapp-server + random-route: true + memory: 256M \ No newline at end of file diff --git a/voicebot-app/requirements.txt b/voicebot-app/requirements.txt new file mode 100644 index 0000000..6c5a3f4 --- /dev/null +++ b/voicebot-app/requirements.txt @@ -0,0 +1,6 @@ +Flask==2.0.1 +PyJWT==2.1.0 +twilio==6.59.0 +ibm-watson==5.1.0 +requests==2.25.1 + diff --git a/voicebot-app/static/audios/file b/voicebot-app/static/audios/file new file mode 100644 index 0000000..e69de29 diff --git a/voicebot-app/static/css/style.css b/voicebot-app/static/css/style.css new file mode 100644 index 0000000..e02fa9b --- /dev/null +++ b/voicebot-app/static/css/style.css @@ -0,0 +1,6 @@ +.custom-spacing { + margin-top: 45%; + margin-left: 2%; + margin-right: 2%; + color: white; +} \ No newline at end of file diff --git a/voicebot-app/static/js/index.js b/voicebot-app/static/js/index.js new file mode 100644 index 0000000..fee58b4 --- /dev/null +++ b/voicebot-app/static/js/index.js @@ -0,0 +1,98 @@ +const twilioCredSubmit = document.getElementById("twilio-cred-submit"); +const twilioStatus = document.getElementById("twilioStatus"); +const waCredSubmit = document.getElementById("wa-cred-submit"); +const waStatus = document.getElementById("waStatus"); +const sttCredSubmit = document.getElementById("stt-cred-submit"); +const sttStatus = document.getElementById("sttStatus"); + +twilioCredSubmit.onclick = () => storeTwilioCredentials(); +waCredSubmit.onclick = () => storeWaCredentials(); +sttCredSubmit.onclick = () => storeSttCredentials(); + +const storeTwilioCredentials = async () => { + var url = '/storeTwilioCredentials'; + var payload = { + account_sid: document.getElementById('account_sid').value, + auth_token: document.getElementById('auth_token').value + } + var options = { + method: 'post', + body: JSON.stringify(payload) + } + + console.log(options); + + await fetch(url, options).then(async (response) => { + data = await response.json(); + console.log(data); + getTwilioStatus(); + location.reload(); + }); +}; + +const getTwilioStatus = async () => { + await fetch('/getTwilioCredentials').then(async (response) => { + data = await response.json(); + console.log(data); + twilioStatus.innerHTML = data.status; + }); +}; + +const storeWaCredentials = async () => { + var url = '/storeWaCredentials'; + var payload = { + wa_apikey: document.getElementById('wa_apikey').value, + wa_assistant_id: document.getElementById('wa_assistant_id').value, + wa_url: document.getElementById('wa_url').value + } + var options = { + method: 'post', + body: JSON.stringify(payload) + } + + console.log(options); + + await fetch(url, options).then(async (response) => { + data = await response.json(); + console.log(data); + getWaStatus(); + location.reload(); + }); +}; + +const getWaStatus = async () => { + await fetch('/getWaCredentials').then(async (response) => { + data = await response.json(); + console.log(data); + waStatus.innerHTML = data.status; + }); +}; + +const storeSttCredentials = async () => { + var url = '/storeSttCredentials'; + var payload = { + stt_apikey: document.getElementById('stt_apikey').value, + stt_url: document.getElementById('stt_url').value + } + var options = { + method: 'post', + body: JSON.stringify(payload) + } + + console.log(options); + + await fetch(url, options).then(async (response) => { + data = await response.json(); + console.log(data); + getSttStatus(); + location.reload(); + }); +}; + +const getSttStatus = async () => { + await fetch('/getSttCredentials').then(async (response) => { + data = await response.json(); + console.log(data); + sttStatus.innerHTML = data.status; + }); +}; diff --git a/voicebot-app/templates/index.html b/voicebot-app/templates/index.html new file mode 100644 index 0000000..efd6e4a --- /dev/null +++ b/voicebot-app/templates/index.html @@ -0,0 +1,275 @@ + + + + + + + + + + + + Voicebot Application + + + + +