Quantx-WhatsApp / main.py
williamagyapong's picture
Update main.py
76bd636 verified
from flask import Flask, request, make_response
import os
from datetime import datetime
import logging
from dotenv import load_dotenv
from heyoo import WhatsApp
import assemblyai as aai
import openai
from utility import generateResponse, parse_multiple_transactions, create_inventory, create_sale, read_datalake, delete_transaction
from google.cloud import firestore
import ast
import base64
import requests
# load env data
load_dotenv()
# messenger object
messenger = WhatsApp(
os.environ["whatsapp_token"],
phone_number_id=os.environ["phone_number_id"]
)
aai.settings.api_key = os.environ["aai_key"]
transcriber = aai.Transcriber()
# Authenticate to Firestore with the JSON account key
db = firestore.Client.from_service_account_json("firestore-key.json")
app = Flask(__name__)
VERIFY_TOKEN = "30cca545-3838-48b2-80a7-9e43b1ae8ce4"
img_ID = "344744a88ad1098"
img_secret = "3c542a40c215327045d7155bddfd8b8bc84aebbf"
url = "https://api.imgur.com/3/image"
headers = {"Authorization": f"Client-ID {img_ID}"}
# Interactive button
def messenger_button(recipient_phone, message, header='Transaction Confirmation', footer='', btn_name='Confirm Details'):
messenger.send_button(
recipient_id=recipient_phone,
button={
"header": f"{header}",
"body": f"{message}",
"footer": f"{footer}",
"action": {
"button": f"{btn_name}",
"sections": [
{
"title": "iBank",
"rows": [
{"id": "confirm", "title": "Record Transaction", "description": ""},
{"id": "cancel", "title": "Cancel Transaction", "description": ""},
],
}
],
},
},
)
def messenger_reply_button(recipient_phone, message, header_message = "*Please confirm the details below before we proceed*:\n\n"):
messenger.send_reply_button(
recipient_id=f"{recipient_phone}",
button={
"type": "button",
"body": {
"text": f"{header_message} {message}"
},
"action": {
"buttons": [
{
"type": "reply",
"reply": {
"id": "confirm",
"title": "Confirm"
}
},
{
"type": "reply",
"reply": {
"id": "cancel",
"title": "Cancel"
}
}
]
}
},
)
def respond(query_str: str):
response = "hello, I don't have a brain yet"
return response
def persist_temporary_transaction(transactions, mobile):
"""
Persists the transaction data temporarily in Firestore.
"""
# intent = transactions[0]['intent'].lower()
temp_ref = db.collection("users").document(mobile).collection("temp_transactions").document('pending-user-action')
data = {
"transactions": transactions,
"status": "pending",
"created_at": datetime.now().isoformat()
}
temp_ref.set(data)
return True
def process_user_msg(message, mobile):
response = str(generateResponse(message))
parsed_trans_data = parse_multiple_transactions(response)
# messenger.send_message(f"{response} \n\n {parsed_trans_data}", mobile)
logging.info(f"\nAnswer: {response}\n")
intent = parsed_trans_data[0]['intent'].lower()
if intent == 'read':
response2 = str(read_datalake(mobile, message))
# Check if response is a string and represents a valid image path
if isinstance(response2, str) and os.path.isfile(os.path.join(response2)):
image_path = os.path.join(response2)
print("My image path:", image_path)
with open(image_path, "rb") as file:
data = file.read()
base64_data = base64.b64encode(data)
# Upload image to Imgur and get URL
response2 = requests.post(url, headers=headers, data={"image": base64_data})
url1= response2.json()["data"]["link"]
print(url1)
messenger.send_image(image=url1, recipient_id=mobile)
else:
messenger.send_message(f"{response} \n\n {response2}", recipient_id=mobile)
else:
# Persist transaction data temporarily for Create, Update, or Delete operations
persist = persist_temporary_transaction(parsed_trans_data, mobile)
if persist:
# messenger.send_message(f"{response} \n\n {parsed_trans_data}", mobile)
# Give user the chance to confirm/cancel transaction before processing other intents
messenger_reply_button(mobile, f"{response}")
else:
messenger.send_message("Please try again!", mobile)
return True
def process_intent(parsed_trans_data, mobile):
intent = parsed_trans_data[0]['intent'].lower()
trans_type = parsed_trans_data[0]['transaction_type'].lower()
if intent == 'create':
if trans_type in ('purchase', 'purchases', 'inventory'):
if create_inventory(mobile, parsed_trans_data):
firestore_msg = "Transaction recorded successfully!"
else:
firestore_msg = "Sorry, could not record transaction!"
elif trans_type in ('sale', 'sales'):
if create_sale(mobile, parsed_trans_data):
firestore_msg = "Transaction recorded successfully!"
else:
firestore_msg = "Sorry, could not record transaction!"
elif intent == 'update':
pass
elif intent == 'delete':
item = parsed_trans_data[0]['details']['item']
item_deleted = delete_transaction(mobile, parsed_trans_data)
if item_deleted:
firestore_msg = f"You successfully deleted {item} from {trans_type}!"
else:
firestore_msg = f"Sorry, could not delete the {item} from {trans_type}!"
# elif intent == 'read':
# response = str(read_datalake(mobile, message))
# parsed_trans_data = ""
# firestore_msg = response
else:
firestore_msg = f'The detected intent, {intent}, is not currently supported!'
# return firestore_msg, parsed_trans_data, response
return firestore_msg
def handle_interactive_response(mobile, button_id):
"""
Handles the user's button response (Confirm or Cancel).
"""
doc_id = 'pending-user-action'
temp_ref = db.collection("users").document(mobile).collection("temp_transactions").document(doc_id)
transaction = temp_ref.get()
if transaction.exists:
transaction_data = transaction.to_dict()
if button_id == "confirm":
# Move to the appropriate collection
transactions = transaction_data["transactions"]
msg = process_intent(transactions, mobile)
# Mark as confirmed or delete temporary record
temp_ref.delete()
messenger.send_message(f"{msg}", recipient_id = mobile)
elif button_id == "cancel":
# Delete the temporary record
temp_ref.delete()
messenger.send_message("Transaction has been canceled.", recipient_id = mobile)
else:
messenger.send_message("Invalid action. Please try again.", recipient_id = mobile)
else:
messenger.send_message("No pending transaction found.", recipient_id = mobile)
@app.route("/", methods=["GET", "POST"])
def hook():
if request.method == "GET":
if request.args.get("hub.verify_token") == VERIFY_TOKEN:
logging.info("Verified webhook")
response = make_response(request.args.get("hub.challenge"), 200)
response.mimetype = "text/plain"
return response
logging.error("Webhook Verification failed")
return "Invalid verification token"
# get message update..
data = request.get_json()
changed_field = messenger.changed_field(data)
if changed_field == "messages":
new_message = messenger.get_mobile(data)
if new_message:
mobile = messenger.get_mobile(data)
message_type = messenger.get_message_type(data)
if message_type == "text":
message = messenger.get_message(data)
# Handle greetings
if message.lower() in ("hi", "hello", "hola", "help", "how are you", "sawubona"):
response = "Hi there! My name is Qx-SmartLedger. How can I help you today?"
messenger.send_message(message=f"{response}", recipient_id=mobile)
else:
process_user_msg(message, mobile)
# messenger.send_message(message=f"User message processed!", recipient_id=mobile)
elif message_type == "audio":
audio = messenger.get_audio(data)
audio_id, mime_type = audio["id"], audio["mime_type"]
audio_url = messenger.query_media_url(audio_id)
audio_filename = messenger.download_media(audio_url, mime_type)
transcript = transcriber.transcribe(audio_filename)
print(audio_filename)
print(transcript.text)
transcribed_message = transcript.text
# logging.info(f"\nAudio: {audio}\n")
process_user_msg(transcribed_message, mobile)
# firestore_msg1, parsed_trans_data1, response1 = process_user_msg(transcribed_message, mobile)
# messenger.send_message(message=f"Raw Response: {response1}, \n \n Parsed Response: {parsed_trans_data1}, \n \n Final Response: {firestore_msg1}", recipient_id=mobile)
elif message_type == "interactive":
message_response = messenger.get_interactive_response(data)
interactive_type = message_response.get("type")
message_id = message_response[interactive_type]["id"]
message_text = message_response[interactive_type]["title"]
logging.info(f"Interactive Message: {interactive_type} {message_id}: {message_text}")
# messenger.send_message(message = f"Interactive type: {interactive_type}\n Message ID: {message_id} \n Message body: {message_body}", recipient_id=mobile)
# Handle either create, update, or delete operation based on user action
handle_interactive_response(mobile, message_id)
else:
messenger.send_message(message=f"Please send me text or audio messages", recipient_id=mobile)
return "ok"
if __name__ == '__main__':
app.run(debug=True, host="0.0.0.0", port=7860)