jjmandog commited on
Commit
d86c812
Β·
verified Β·
1 Parent(s): 47ee0c8

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +557 -0
app.py ADDED
@@ -0,0 +1,557 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ πŸš— JAY'S MOBILE WASH - IPHONE FORWARDING AI SYSTEM
4
+ Lightweight version optimized for HuggingFace Spaces deployment
5
+ """
6
+
7
+ import os
8
+ import json
9
+ import requests
10
+ import logging
11
+ from datetime import datetime, timedelta
12
+ from flask import Flask, render_template, request, jsonify
13
+ from flask_cors import CORS
14
+ from flask_socketio import SocketIO, emit
15
+ import threading
16
+ from threading import Lock
17
+ from collections import defaultdict
18
+ import time
19
+ import uuid
20
+
21
+ # Load environment variables
22
+ from dotenv import load_dotenv
23
+ load_dotenv()
24
+
25
+ # Initialize Flask app
26
+ app = Flask(__name__)
27
+ app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'jays-mobile-wash-2025')
28
+
29
+ # Enable CORS and SocketIO
30
+ CORS(app)
31
+ socketio = SocketIO(app, cors_allowed_origins="*")
32
+
33
+ # Configuration
34
+ SIGNALWIRE_PROJECT_ID = os.environ.get('SIGNALWIRE_PROJECT_ID', '')
35
+ SIGNALWIRE_AUTH_TOKEN = os.environ.get('SIGNALWIRE_AUTH_TOKEN', '')
36
+ SIGNALWIRE_SPACE_URL = os.environ.get('SIGNALWIRE_SPACE_URL', '')
37
+ DEEPSEEK_API_KEY = os.environ.get('DEEPSEEK_API_KEY', '')
38
+
39
+ # Business settings
40
+ BUSINESS_NAME = "Jay's Mobile Wash"
41
+ JAY_PHONE = os.environ.get('JAY_PHONE', '+15622289429')
42
+ SIGNALWIRE_PHONE = os.environ.get('SIGNALWIRE_PHONE', '+17149278841')
43
+ FORWARDING_ENABLED = True
44
+ FORWARDING_DELAY = 20
45
+
46
+ # Services and pricing
47
+ SERVICES = {
48
+ 'basic_wash': {'price': 25.00, 'name': 'Basic Wash'},
49
+ 'premium_wash': {'price': 45.00, 'name': 'Premium Wash'},
50
+ 'full_detail': {'price': 85.00, 'name': 'Full Detail'},
51
+ 'ceramic_coating': {'price': 150.00, 'name': 'Ceramic Coating'},
52
+ 'headlight_restoration': {'price': 35.00, 'name': 'Headlight Restoration'}
53
+ }
54
+
55
+ # Initialize SignalWire
56
+ signalwire_client = None
57
+ VoiceResponse = None
58
+ MessagingResponse = None
59
+
60
+ try:
61
+ from signalwire.rest import Client
62
+ from signalwire.voice_response import VoiceResponse
63
+ from signalwire.messaging_response import MessagingResponse
64
+
65
+ if SIGNALWIRE_PROJECT_ID and SIGNALWIRE_AUTH_TOKEN and SIGNALWIRE_SPACE_URL:
66
+ signalwire_client = Client(
67
+ SIGNALWIRE_PROJECT_ID,
68
+ SIGNALWIRE_AUTH_TOKEN,
69
+ signalwire_space_url=SIGNALWIRE_SPACE_URL
70
+ )
71
+ print("βœ… SignalWire initialized")
72
+ else:
73
+ print("⚠️ SignalWire credentials not found")
74
+ except ImportError:
75
+ print("⚠️ SignalWire library not available")
76
+
77
+ # Logging setup
78
+ logging.basicConfig(level=logging.INFO)
79
+ logger = logging.getLogger(__name__)
80
+
81
+ # Thread-safe system state
82
+ class SystemState:
83
+ def __init__(self):
84
+ self._lock = Lock()
85
+ self._data = {
86
+ 'calls_today': 0,
87
+ 'sms_today': 0,
88
+ 'calls_forwarded': 0,
89
+ 'ai_responses': 0,
90
+ 'revenue_today': 0.0,
91
+ 'active_calls': {},
92
+ 'active_sms': {},
93
+ 'start_time': datetime.now(),
94
+ 'status': 'healthy'
95
+ }
96
+
97
+ def get(self, key):
98
+ with self._lock:
99
+ return self._data.get(key, 0)
100
+
101
+ def set(self, key, value):
102
+ with self._lock:
103
+ self._data[key] = value
104
+
105
+ def increment(self, key):
106
+ with self._lock:
107
+ self._data[key] = self._data.get(key, 0) + 1
108
+
109
+ def get_all(self):
110
+ with self._lock:
111
+ return self._data.copy()
112
+
113
+ # Initialize system state
114
+ system_state = SystemState()
115
+
116
+ # Simple AI processor
117
+ class SimpleAI:
118
+ def __init__(self):
119
+ self.response_cache = {}
120
+
121
+ def analyze_sentiment(self, text):
122
+ """Simple sentiment analysis"""
123
+ try:
124
+ from textblob import TextBlob
125
+ blob = TextBlob(text)
126
+ polarity = blob.sentiment.polarity
127
+ return {
128
+ 'score': polarity,
129
+ 'label': 'positive' if polarity > 0.1 else 'negative' if polarity < -0.1 else 'neutral'
130
+ }
131
+ except:
132
+ return {'score': 0.0, 'label': 'neutral'}
133
+
134
+ def detect_intent(self, text):
135
+ """Detect customer intent"""
136
+ text_lower = text.lower()
137
+
138
+ if any(word in text_lower for word in ['price', 'cost', 'how much', 'pricing']):
139
+ return 'pricing'
140
+ elif any(word in text_lower for word in ['book', 'schedule', 'appointment', 'when']):
141
+ return 'booking'
142
+ elif any(word in text_lower for word in ['service', 'wash', 'detail', 'clean']):
143
+ return 'services'
144
+ elif any(word in text_lower for word in ['location', 'where', 'area', 'address']):
145
+ return 'location'
146
+ elif any(word in text_lower for word in ['urgent', 'emergency', 'asap', 'now']):
147
+ return 'urgent'
148
+ elif any(word in text_lower for word in ['jay', 'owner', 'human', 'person']):
149
+ return 'human_request'
150
+ else:
151
+ return 'general'
152
+
153
+ def generate_response(self, user_input, context=None):
154
+ """Generate AI response"""
155
+
156
+ # Check cache first
157
+ cache_key = f"{user_input[:50]}_{context.get('forwarded', False) if context else False}"
158
+ if cache_key in self.response_cache:
159
+ return self.response_cache[cache_key]
160
+
161
+ intent = self.detect_intent(user_input)
162
+ sentiment = self.analyze_sentiment(user_input)
163
+
164
+ # Add forwarding acknowledgment if needed
165
+ forwarding_ack = ""
166
+ if context and context.get('forwarded'):
167
+ forwarding_ack = "Thank you for your patience while the call was connected. "
168
+
169
+ # Generate response based on intent
170
+ if intent == 'pricing':
171
+ response = f"{forwarding_ack}I'd be happy to help with pricing! Our basic wash is $25, premium wash is $45, and full detail is $85. Which service interests you most?"
172
+
173
+ elif intent == 'booking':
174
+ response = f"{forwarding_ack}I can help you schedule an appointment! We're available Monday-Saturday 8AM-6PM, Sunday 10AM-4PM. What day works best for you?"
175
+
176
+ elif intent == 'services':
177
+ response = f"{forwarding_ack}We offer mobile car detailing services: Basic wash ($25), Premium wash with wax ($45), Full detail ($85), and ceramic coating ($150). Which would you like to know more about?"
178
+
179
+ elif intent == 'location':
180
+ response = f"{forwarding_ack}We provide mobile service within a 15-mile radius of Los Angeles. We come to your location! Where are you located?"
181
+
182
+ elif intent == 'urgent':
183
+ response = f"{forwarding_ack}I understand this is urgent. Let me connect you with Jay directly right away for immediate assistance."
184
+
185
+ elif intent == 'human_request':
186
+ response = f"{forwarding_ack}Let me connect you with Jay personally. He'll be right with you!"
187
+
188
+ else:
189
+ response = f"{forwarding_ack}Hi! Thanks for contacting Jay's Mobile Wash! I can help with pricing, scheduling, or questions about our mobile car detailing services. How can I assist you today?"
190
+
191
+ # Try DeepSeek if available for better responses
192
+ if DEEPSEEK_API_KEY and intent not in ['urgent', 'human_request']:
193
+ try:
194
+ deepseek_response = self.get_deepseek_response(user_input, context)
195
+ if deepseek_response:
196
+ response = deepseek_response
197
+ except Exception as e:
198
+ logger.warning(f"DeepSeek failed: {e}")
199
+
200
+ # Cache and return
201
+ self.response_cache[cache_key] = response
202
+ return response
203
+
204
+ def get_deepseek_response(self, prompt, context=None):
205
+ """Get response from DeepSeek API"""
206
+ try:
207
+ headers = {
208
+ "Authorization": f"Bearer {DEEPSEEK_API_KEY}",
209
+ "Content-Type": "application/json"
210
+ }
211
+
212
+ system_prompt = f"""You are Jay's Mobile Wash AI assistant. Be friendly and professional.
213
+
214
+ BUSINESS INFO:
215
+ - Services: Basic wash ($25), Premium wash ($45), Full detail ($85), Ceramic coating ($150), Headlight restoration ($35)
216
+ - Hours: Mon-Sat 8AM-6PM, Sun 10AM-4PM
217
+ - Service area: 15 mile radius from Los Angeles
218
+ - Owner: Jay, Phone: {JAY_PHONE}
219
+
220
+ {"IMPORTANT: Customer's call was forwarded from Jay's phone. Be empathetic about any wait time." if context and context.get('forwarded') else ""}"""
221
+
222
+ data = {
223
+ "model": "deepseek-chat",
224
+ "messages": [
225
+ {"role": "system", "content": system_prompt},
226
+ {"role": "user", "content": prompt}
227
+ ],
228
+ "max_tokens": 150,
229
+ "temperature": 0.7
230
+ }
231
+
232
+ response = requests.post(
233
+ "https://api.deepseek.com/v1/chat/completions",
234
+ headers=headers,
235
+ json=data,
236
+ timeout=10
237
+ )
238
+
239
+ if response.status_code == 200:
240
+ result = response.json()
241
+ return result['choices'][0]['message']['content'].strip()
242
+
243
+ except Exception as e:
244
+ logger.error(f"DeepSeek error: {e}")
245
+
246
+ return None
247
+
248
+ # Initialize AI processor
249
+ ai = SimpleAI()
250
+
251
+ # ===== VOICE WEBHOOKS =====
252
+
253
+ @app.route('/voice/incoming', methods=['POST'])
254
+ def handle_voice():
255
+ """Handle incoming voice calls with iPhone forwarding"""
256
+ try:
257
+ if not signalwire_client or not VoiceResponse:
258
+ return "Service unavailable", 503
259
+
260
+ call_sid = request.form.get('CallSid')
261
+ from_number = request.form.get('From')
262
+ to_number = request.form.get('To')
263
+ forwarded_from = request.form.get('ForwardedFrom')
264
+
265
+ # Detect forwarding
266
+ is_forwarded = bool(forwarded_from) or (to_number == SIGNALWIRE_PHONE)
267
+
268
+ logger.info(f"πŸ“ž Call: {from_number} β†’ {to_number}, Forwarded: {is_forwarded}")
269
+
270
+ # Update stats
271
+ system_state.increment('calls_today')
272
+ if is_forwarded:
273
+ system_state.increment('calls_forwarded')
274
+
275
+ # Store active call
276
+ system_state.get_all()['active_calls'][call_sid] = {
277
+ 'from': from_number,
278
+ 'forwarded': is_forwarded,
279
+ 'start': datetime.now().isoformat()
280
+ }
281
+
282
+ response = VoiceResponse()
283
+
284
+ # Greeting
285
+ if is_forwarded:
286
+ greeting = "Hi! Thanks for calling Jay's Mobile Wash. I'm Jay's AI assistant ready to help while Jay is with another customer. How can I assist you?"
287
+ else:
288
+ greeting = "Hi! Welcome to Jay's Mobile Wash! I'm your AI assistant ready to help with pricing, scheduling, or any questions. How can I help you?"
289
+
290
+ response.say(greeting, voice='alice')
291
+
292
+ # Gather speech
293
+ response.gather(
294
+ input='speech',
295
+ timeout=10,
296
+ action=f'/voice/process?call_sid={call_sid}&forwarded={is_forwarded}',
297
+ speech_timeout=3
298
+ )
299
+
300
+ # Fallback
301
+ response.say("I didn't catch that. Let me connect you with Jay.", voice='alice')
302
+ response.dial(JAY_PHONE, timeout=30)
303
+
304
+ return str(response)
305
+
306
+ except Exception as e:
307
+ logger.error(f"Voice error: {e}")
308
+ response = VoiceResponse()
309
+ response.say("Technical issue. Connecting you with Jay.", voice='alice')
310
+ response.dial(JAY_PHONE, timeout=30)
311
+ return str(response)
312
+
313
+ @app.route('/voice/process', methods=['POST'])
314
+ def process_speech():
315
+ """Process customer speech"""
316
+ try:
317
+ call_sid = request.args.get('call_sid')
318
+ is_forwarded = request.args.get('forwarded') == 'True'
319
+ speech_result = request.form.get('SpeechResult', '')
320
+ confidence = float(request.form.get('Confidence', '0.0'))
321
+
322
+ logger.info(f"🎀 Speech: {speech_result} (conf: {confidence:.2f})")
323
+
324
+ if not speech_result or confidence < 0.4:
325
+ response = VoiceResponse()
326
+ response.say("I didn't understand clearly. Let me connect you with Jay.", voice='alice')
327
+ response.dial(JAY_PHONE, timeout=30)
328
+ return str(response)
329
+
330
+ # Analyze intent
331
+ intent = ai.detect_intent(speech_result)
332
+
333
+ # Check for escalation
334
+ if intent in ['urgent', 'human_request'] or 'jay' in speech_result.lower():
335
+ response = VoiceResponse()
336
+ response.say("Let me connect you with Jay right away.", voice='alice')
337
+ response.dial(JAY_PHONE, timeout=30)
338
+ return str(response)
339
+
340
+ # Generate AI response
341
+ context = {'forwarded': is_forwarded, 'call_sid': call_sid}
342
+ ai_response = ai.generate_response(speech_result, context)
343
+
344
+ system_state.increment('ai_responses')
345
+
346
+ response = VoiceResponse()
347
+ response.say(ai_response, voice='alice')
348
+
349
+ # Continue conversation
350
+ response.gather(
351
+ input='speech',
352
+ timeout=8,
353
+ action=f'/voice/process?call_sid={call_sid}&forwarded={is_forwarded}',
354
+ speech_timeout=3
355
+ )
356
+
357
+ response.say("Thank you for calling Jay's Mobile Wash! Have a great day!", voice='alice')
358
+ return str(response)
359
+
360
+ except Exception as e:
361
+ logger.error(f"Speech processing error: {e}")
362
+ response = VoiceResponse()
363
+ response.say("Technical issue. Connecting with Jay.", voice='alice')
364
+ response.dial(JAY_PHONE, timeout=30)
365
+ return str(response)
366
+
367
+ # ===== SMS WEBHOOKS =====
368
+
369
+ @app.route('/sms/incoming', methods=['POST'])
370
+ def handle_sms():
371
+ """Handle incoming SMS"""
372
+ try:
373
+ message_sid = request.form.get('MessageSid')
374
+ from_number = request.form.get('From')
375
+ message_body = request.form.get('Body', '').strip()
376
+
377
+ logger.info(f"πŸ“± SMS from {from_number}: {message_body[:50]}...")
378
+
379
+ system_state.increment('sms_today')
380
+
381
+ if not message_body:
382
+ return send_sms("Hi! How can I help you with Jay's Mobile Wash today?", from_number)
383
+
384
+ # Analyze message
385
+ intent = ai.detect_intent(message_body)
386
+ sentiment = ai.analyze_sentiment(message_body)
387
+
388
+ # Check for escalation
389
+ if (intent in ['urgent', 'human_request'] or
390
+ sentiment['score'] < -0.6 or
391
+ 'jay' in message_body.lower()):
392
+
393
+ response_text = f"I'll have Jay respond to you personally. For immediate assistance, call {JAY_PHONE}."
394
+ # TODO: Notify Jay
395
+ return send_sms(response_text, from_number)
396
+
397
+ # Generate AI response
398
+ ai_response = ai.generate_response(message_body, {'sms': True})
399
+
400
+ # Optimize for SMS (160 char limit)
401
+ if len(ai_response) > 320:
402
+ ai_response = ai_response[:317] + "..."
403
+
404
+ system_state.increment('ai_responses')
405
+
406
+ return send_sms(ai_response, from_number)
407
+
408
+ except Exception as e:
409
+ logger.error(f"SMS error: {e}")
410
+ return send_sms("Thanks for your message! Jay will get back to you soon.", from_number)
411
+
412
+ def send_sms(message, to_number):
413
+ """Send SMS response"""
414
+ try:
415
+ if signalwire_client:
416
+ signalwire_client.messages.create(
417
+ body=message,
418
+ from_=SIGNALWIRE_PHONE,
419
+ to=to_number
420
+ )
421
+ logger.info(f"πŸ“€ SMS sent to {to_number}")
422
+
423
+ return str(MessagingResponse()) if MessagingResponse else ""
424
+ except Exception as e:
425
+ logger.error(f"SMS send error: {e}")
426
+ return ""
427
+
428
+ # ===== WEB INTERFACE =====
429
+
430
+ @app.route('/')
431
+ def dashboard():
432
+ """Main dashboard"""
433
+ try:
434
+ stats = system_state.get_all()
435
+ uptime = datetime.now() - stats['start_time']
436
+
437
+ dashboard_data = {
438
+ 'business_name': BUSINESS_NAME,
439
+ 'jay_phone': JAY_PHONE,
440
+ 'ai_phone': SIGNALWIRE_PHONE,
441
+ 'forwarding_enabled': FORWARDING_ENABLED,
442
+ 'forwarding_delay': FORWARDING_DELAY,
443
+ 'stats': {
444
+ 'calls_today': stats['calls_today'],
445
+ 'sms_today': stats['sms_today'],
446
+ 'calls_forwarded': stats['calls_forwarded'],
447
+ 'ai_responses': stats['ai_responses'],
448
+ 'revenue_today': stats['revenue_today'],
449
+ 'uptime_hours': int(uptime.total_seconds() / 3600),
450
+ 'active_calls': len(stats['active_calls']),
451
+ 'active_sms': len(stats['active_sms']),
452
+ 'status': stats['status']
453
+ },
454
+ 'services': SERVICES,
455
+ 'signalwire_configured': bool(signalwire_client),
456
+ 'deepseek_configured': bool(DEEPSEEK_API_KEY)
457
+ }
458
+
459
+ return render_template('dashboard.html', **dashboard_data)
460
+
461
+ except Exception as e:
462
+ logger.error(f"Dashboard error: {e}")
463
+ return f"Dashboard error: {e}", 500
464
+
465
+ @app.route('/api/stats')
466
+ def api_stats():
467
+ """API endpoint for real-time stats"""
468
+ stats = system_state.get_all()
469
+ uptime = datetime.now() - stats['start_time']
470
+
471
+ return jsonify({
472
+ 'calls_today': stats['calls_today'],
473
+ 'sms_today': stats['sms_today'],
474
+ 'calls_forwarded': stats['calls_forwarded'],
475
+ 'ai_responses': stats['ai_responses'],
476
+ 'revenue_today': stats['revenue_today'],
477
+ 'uptime_seconds': int(uptime.total_seconds()),
478
+ 'active_calls': len(stats['active_calls']),
479
+ 'active_sms': len(stats['active_sms']),
480
+ 'status': stats['status'],
481
+ 'timestamp': datetime.now().isoformat()
482
+ })
483
+
484
+ @app.route('/health')
485
+ def health_check():
486
+ """Health check endpoint"""
487
+ health = {
488
+ 'status': 'healthy',
489
+ 'signalwire': bool(signalwire_client),
490
+ 'deepseek': bool(DEEPSEEK_API_KEY),
491
+ 'uptime': int((datetime.now() - system_state.get_all()['start_time']).total_seconds()),
492
+ 'timestamp': datetime.now().isoformat()
493
+ }
494
+
495
+ status_code = 200 if health['signalwire'] else 503
496
+ return jsonify(health), status_code
497
+
498
+ # ===== SOCKETIO EVENTS =====
499
+
500
+ @socketio.on('connect')
501
+ def handle_connect():
502
+ emit('status', {'message': 'Connected to Jay\'s Mobile Wash AI'})
503
+ logger.info("Client connected")
504
+
505
+ @socketio.on('request_stats')
506
+ def handle_stats_request():
507
+ stats = system_state.get_all()
508
+ emit('stats_update', stats)
509
+
510
+ # ===== BACKGROUND TASKS =====
511
+
512
+ def broadcast_stats():
513
+ """Broadcast stats every 30 seconds"""
514
+ while True:
515
+ try:
516
+ stats = system_state.get_all()
517
+ socketio.emit('stats_broadcast', stats)
518
+ time.sleep(30)
519
+ except Exception as e:
520
+ logger.error(f"Broadcast error: {e}")
521
+ time.sleep(60)
522
+
523
+ # Start background thread
524
+ stats_thread = threading.Thread(target=broadcast_stats, daemon=True)
525
+ stats_thread.start()
526
+
527
+ # ===== ERROR HANDLERS =====
528
+
529
+ @app.errorhandler(404)
530
+ def not_found(error):
531
+ return jsonify({'error': 'Not found'}), 404
532
+
533
+ @app.errorhandler(500)
534
+ def internal_error(error):
535
+ logger.error(f"Internal error: {error}")
536
+ return jsonify({'error': 'Internal server error'}), 500
537
+
538
+ # ===== STARTUP =====
539
+
540
+ if __name__ == '__main__':
541
+ print("\nπŸš— JAY'S MOBILE WASH AI SYSTEM")
542
+ print("=" * 50)
543
+ print(f"πŸ“ž Jay's Phone: {JAY_PHONE}")
544
+ print(f"πŸ€– AI Phone: {SIGNALWIRE_PHONE}")
545
+ print(f"πŸ”„ Forwarding: {'βœ… Enabled' if FORWARDING_ENABLED else '❌ Disabled'}")
546
+ print(f"🌐 SignalWire: {'βœ… Connected' if signalwire_client else '❌ Not configured'}")
547
+ print(f"🧠 DeepSeek: {'βœ… Connected' if DEEPSEEK_API_KEY else '❌ Not configured'}")
548
+ print("=" * 50)
549
+ print("πŸš€ Starting server...")
550
+
551
+ # Run the app
552
+ socketio.run(
553
+ app,
554
+ host='0.0.0.0',
555
+ port=7860,
556
+ debug=False
557
+ )