sonuprasad23 commited on
Commit
32107fd
·
1 Parent(s): 7ce5499
Files changed (4) hide show
  1. Dockerfile +19 -0
  2. credentials.json +13 -0
  3. requirements.txt +4 -0
  4. webhook_server.py +89 -0
Dockerfile ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use a slim, official Python image
2
+ FROM python:3.10-slim
3
+
4
+ # Set the working directory inside the container
5
+ WORKDIR /app
6
+
7
+ # Copy the requirements file and install dependencies
8
+ # This leverages Docker caching to speed up future builds
9
+ COPY requirements.txt .
10
+ RUN pip install --no-cache-dir -r requirements.txt
11
+
12
+ # Copy the rest of your application files into the container
13
+ COPY . .
14
+
15
+ # Expose the port that Hugging Face uses
16
+ EXPOSE 7860
17
+
18
+ # Command to run the application using a production-grade server
19
+ CMD ["gunicorn", "--bind", "0.0.0.0:7860", "webhook_server:app"]
credentials.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "type": "service_account",
3
+ "project_id": "gen-lang-client-0245189086",
4
+ "private_key_id": "8f7095eb84e7ea8a2cf53634bd859e74ce4bb04d",
5
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC89SogQi981svk\nJZOVdgtvqk/79S5RIuSDNt9683Tsc/Y9T01t6e0QKDWIVN6nBZS8LB8oTSFvPwYJ\n5jPTDZ0rm4QZ/FTgCEV0k9CJRZNLIfBAI2xFAKez5bAT0Z+9mUqiZCI2paSqIzzz\n7U9Z8jdcpooUxE1YQTTXWN2PqIP+doAmHa/AUniXMiu2cdl8YrxmAuEN6VuqdFuR\nzrSUjXrCx6yonHtccNUFSBpmbq8fB3t7tuMjpz51wvIqYQvsM2laYX0xOh1jdbUb\n0E9xSuzBNvRfO9M9zfkFz3zWR7Ke+uJHgkzwGMAL0vTtyOszGG7jBqyVROZUIJj1\n75j2LB7TAgMBAAECggEAVFZMlhdUYLDyUgMfiw8j7ZQjnP6CzL35JkOgnZz6K+ta\nFWVG1u8Y2yRHOHFA62VHTHGY+oDqkl+bz2FK8kFaTDNeU8bXDyNB9NVgt1QxeNBO\nDiKBWY8ASwAShdYDKTm5IR/2UVO/WhzeQLVDvI6qfRTr/nbbWq/H6PIF+e+p8jGn\nXq3LRRZUydAfmIG/8VZXk0NljqPK2lv7yzcXH7DI5imn4XYcbtjXu+Z8RsV6tVGU\nmDg3islMpRFG+XMpwR0Qzowtmsz0VdMoUxWajvqHFppXDOAImb7dClp8nF75Ll8Q\nCdZLYpqkYvg81OkKRsQTHMlNAsIvQwbVlam3YtElaQKBgQDlp42hygN3GAzzXLzZ\nb8I1X5OVKduNEoSwPEVTjpwC3hBfq45T5PKJ/Oa2eZalAvnbudR1UOIXlitMiSCf\niRSetJoFWZW/SyONMzbGc2mkHWFYNF8VAOr7SzmXV8gYEwQ2hPsJ7WxifAr30f9X\n/H5g78WtcYcKDceOCqngMvRpxwKBgQDSom9gKRKgxSSzPziQCKa3O9R4IYzPMjBT\nliKM+hOYaC5nc/mvPj7CJ/+34RngGAHhPYg3lpFPmetvzLrFtL3SafFn1d6KMou9\n3oSsgqUG2ez/ez0JEWdLZkZDiKv2QRSo7xTAMpbhOkms2BoQZrmdW+nbOvVkA5IJ\nu7HZqjEClQKBgAlYfzgFS4zOKsDAlmLW3HVllVDtqiSci0/MtBmJZSnstYffKGSb\nnY8l/pGQcyP18gsSDeZUS08galSsA+raHj+zI81x7tkhCqpVWjZLPhJSq8J2JyRo\nrCdb0VUqWlc4duRtFvY5dj6vw5aAMj73ZIE3YLkFNLShCOzr3CvmhvHDAoGBAJsT\n4sI+myto2kNqSX+qDuybDSxBL0WhIvl3cQqwV9r+4SNjvLHsNxKFln1QKtfVdRTb\nU73xihy8Kx4N8nj7QEJ5o7WaPZUr77yj5rSIH24y5o2Ws3JIsO9PZm30Yv8UHVzf\nYTy8Ql+ipXmvRUlMCF61vDCfcOnoww2NdjzWU+0dAoGBAKMcIMBVeFSLqyE5vGhh\ng9BNs7peeB8qUNhoa7boShsSkVpR4BOBrBk9xpxO8NLbjzaWhOAjS5GG6xP31oMp\nnsnoSl9MH4VubQGdxjsVDQkl9FQx7DhJIJL23uBrzlVLOiNycOUftH+t/UU7K+7H\nQ0V/WfybmSnq9+LX7+3QMcmg\n-----END PRIVATE KEY-----\n",
6
+ "client_email": "vapilogger@gen-lang-client-0245189086.iam.gserviceaccount.com",
7
+ "client_id": "117483781817309554190",
8
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
9
+ "token_uri": "https://oauth2.googleapis.com/token",
10
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
11
+ "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/vapilogger%40gen-lang-client-0245189086.iam.gserviceaccount.com",
12
+ "universe_domain": "googleapis.com"
13
+ }
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ Flask
2
+ gspread
3
+ oauth2client
4
+ gunicorn
webhook_server.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import gspread
4
+ from oauth2client.service_account import ServiceAccountCredentials
5
+ from flask import Flask, request, jsonify
6
+ from datetime import datetime
7
+
8
+ app = Flask(__name__)
9
+
10
+ # --- HTML Template for the Interactive Status Page ---
11
+ HTML_TEMPLATE = """
12
+ <!DOCTYPE html>
13
+ <html lang="en">
14
+ <head>
15
+ <meta charset="UTF-8">
16
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
17
+ <title>Vapi Webhook Server Status</title>
18
+ <style>
19
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f0f2f5; }
20
+ .container { text-align: center; padding: 40px; background-color: white; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); }
21
+ .status { display: inline-block; padding: 12px 24px; background-color: #28a745; color: white; font-size: 20px; font-weight: 600; border-radius: 8px; margin-bottom: 20px; }
22
+ h1 { color: #333; }
23
+ p { color: #555; font-size: 16px; }
24
+ code { background-color: #e9ecef; padding: 4px 8px; border-radius: 4px; font-family: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", monospace; }
25
+ </style>
26
+ </head>
27
+ <body>
28
+ <div class="container">
29
+ <div class="status">Live</div>
30
+ <h1>Your Vapi Webhook Server is Running!</h1>
31
+ <p>This server is successfully deployed and ready to receive requests from Vapi.</p>
32
+ <p>Use the following full URL in your application's configuration:</p>
33
+ <code>{full_webhook_url}</code>
34
+ </div>
35
+ </body>
36
+ </html>
37
+ """
38
+
39
+ def get_google_sheets_client():
40
+ try:
41
+ if not os.path.exists("credentials.json"):
42
+ print("CRITICAL ERROR: 'credentials.json' file not found.")
43
+ return None
44
+ scope = ["https://spreadsheets.google.com/feeds", 'https://www.googleapis.com/auth/spreadsheets',
45
+ "https://www.googleapis.com/auth/drive.file", "https://www.googleapis.com/auth/drive"]
46
+ creds = ServiceAccountCredentials.from_json_keyfile_name("credentials.json", scope)
47
+ return gspread.authorize(creds)
48
+ except Exception as e:
49
+ print(f"CRITICAL ERROR: Could not authenticate with Google Sheets: {e}")
50
+ return None
51
+
52
+ @app.route('/', methods=['GET'])
53
+ def root():
54
+ # This creates the interactive status page
55
+ host = request.host_url
56
+ full_url = f"{host}webhook"
57
+ return HTML_TEMPLATE.format(full_webhook_url=full_url)
58
+
59
+ @app.route('/webhook', methods=['POST'])
60
+ def webhook_handler():
61
+ print(f"Webhook received a POST request at {datetime.now()}.")
62
+ try:
63
+ payload = request.json
64
+ message = payload.get('message', {})
65
+ if message.get('type') == 'hang':
66
+ print("Received 'hang' event. Processing...")
67
+ call_data = message.get('call', {})
68
+ row_to_insert = [
69
+ call_data.get("id", "N/A"),
70
+ datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
71
+ call_data.get("customer", {}).get("number", "N/A"),
72
+ call_data.get("metadata", {}).get("raw_intent", "N/A"),
73
+ message.get('endedReason', 'N/A'),
74
+ call_data.get('summary', 'No summary provided.'),
75
+ "Yes" if "follow-up" in call_data.get('summary', '').lower() else "No",
76
+ json.dumps(call_data.get('transcript', []), indent=2)
77
+ ]
78
+ client = get_google_sheets_client()
79
+ if client:
80
+ sheet_name = os.getenv("GOOGLE_SHEET_NAME", "Call Logs")
81
+ sheet = client.open(sheet_name).sheet1
82
+ sheet.append_row(row_to_insert)
83
+ print(f"Successfully logged call {call_data.get('id')}.")
84
+ else:
85
+ print("Logging to Google Sheets failed: client not available.")
86
+ return jsonify({'status': 'success'}), 200
87
+ except Exception as e:
88
+ print(f"Error in webhook handler: {e}")
89
+ return jsonify({'status': 'error', 'message': str(e)}), 500