Spaces:
Runtime error
Runtime error
| 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) | |
| 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) | |