import logging import os import sqlite3 import threading import urllib.parse from http.server import BaseHTTPRequestHandler, HTTPServer import plivo import streamlit as st from streamlit_autorefresh import st_autorefresh from dotenv import load_dotenv # --- Setup logging --- logging.basicConfig(level=logging.INFO) # --- Load environment variables --- load_dotenv() # --- Setup plivo --- try: PLIVO_AUTH_ID = os.getenv("PLIVO_AUTH_ID") PLIVO_AUTH_TOKEN = os.getenv("PLIVO_AUTH_TOKEN") KORA_PHONE_NUMBER = os.getenv("KORA_PHONE_NUMBER") client = plivo.RestClient(auth_id=PLIVO_AUTH_ID, auth_token=PLIVO_AUTH_TOKEN) except Exception as e: st.error(f"Error setting up Plivo: {e}") # --- Thread lock for SQLite access --- db_lock = threading.Lock() # --- Setup SQLite --- try: conn = sqlite3.connect("chat.db", check_same_thread=False) c = conn.cursor() with db_lock: c.execute( """ CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, sender TEXT, text TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP ) """ ) conn.commit() except sqlite3.Error as e: st.error(f"Error setting up SQLite: {e}") # --- Webhook Server (for receiving SMS) --- class SMSWebhookHandler(BaseHTTPRequestHandler): def do_POST(self): try: length = int(self.headers["Content-Length"]) post_data = self.rfile.read(length) data = urllib.parse.parse_qs(post_data.decode("utf-8")) logging.info(f"Webhook received: {data}") message_text = data.get("Text", [""])[0] if message_text: with db_lock: c.execute( "INSERT INTO messages (sender, text) VALUES (?, ?)", (KORA_PHONE_NUMBER, message_text), ) conn.commit() self.send_response(200) self.end_headers() self.wfile.write( b"Got it!" ) except Exception as e: logging.error(f"Webhook error: {e}") self.send_response(500) self.end_headers() self.wfile.write(b"Error processing request") # --- Run webhook server in background --- def run_webhook_server(): try: server = HTTPServer(("", 8502), SMSWebhookHandler) logging.info("Webhook server started on port 8502") server.serve_forever() except Exception as e: logging.error(f"Error starting webhook server: {e}") # --- Start webhook server thread --- if not hasattr(st.session_state, "webhook_server_started"): threading.Thread(target=run_webhook_server, daemon=True).start() st.session_state.webhook_server_started = True # --- Streamlit UI --- st.title("📲 Chat with KORA") # Initialize session state variables if "show_confirmation" not in st.session_state: st.session_state.show_confirmation = False if "last_message_count" not in st.session_state: st.session_state.last_message_count = 0 # Phone input PLIVO_PHONE_NUMBER = st.text_input( "Enter your phone number to chat with KORA", value="+13602304837", max_chars=15 ) # --- Clear Chat UI --- if not st.session_state.show_confirmation: if st.button("Clear Chat"): st.session_state.show_confirmation = True st.rerun() else: col1, col2 = st.columns(2) if col1.button("Yes, clear chat"): try: with db_lock: c.execute("DELETE FROM messages") conn.commit() st.success("Chat history cleared.") st.session_state.show_confirmation = False st.session_state.last_message_count = 0 st.rerun() except sqlite3.Error as e: st.error(f"Error clearing chat history: {e}") elif col2.button("Cancel"): st.session_state.show_confirmation = False st.rerun() # --- Display Chat Messages --- try: with db_lock: messages = c.execute( "SELECT sender, text FROM messages WHERE sender IN (?, ?) ORDER BY timestamp ASC", (PLIVO_PHONE_NUMBER, KORA_PHONE_NUMBER), ).fetchall() except sqlite3.Error as e: messages = [] st.error(f"Error fetching messages: {e}") # Display messages for sender, text in messages: role = "user" if sender == PLIVO_PHONE_NUMBER else "assistant" st.chat_message(role).write(text) st.session_state.last_message_count = len(messages) # --- Chat Input --- if prompt := st.chat_input("Type your message..."): try: # Save message to DB with db_lock: c.execute( "INSERT INTO messages (sender, text) VALUES (?, ?)", (PLIVO_PHONE_NUMBER, prompt), ) conn.commit() # Send message via Plivo client.messages.create( text=prompt, src=PLIVO_PHONE_NUMBER, dst=KORA_PHONE_NUMBER ) st.rerun() # Refresh to show new message immediately except sqlite3.Error as e: st.error(f"Error saving message: {e}") except Exception as e: st.error(f"Error sending message via Plivo: {e}") st_autorefresh(interval=1000, key="auto_refresh")