import os import json import requests import sqlite3 import re import datetime # --- Helper: Get Gemini Key --- def get_gemini_key(): candidates = [ "gemini_api_key.txt", # Current dir "/app/gemini_api_key.txt", # Docker default os.path.join(os.path.dirname(__file__), "gemini_api_key.txt"), # Script dir os.path.join(os.path.dirname(os.path.dirname(__file__)), 'gemini_api_key.txt') # Parent dir ] for path in candidates: if os.path.exists(path): try: with open(path, 'r') as f: return f.read().strip() except: pass return os.getenv("GEMINI_API_KEY") def get_matrix_context(industry_name, persona_name): """Fetches Pains, Gains and Arguments from CE Database.""" context = { "industry_pains": "", "industry_gains": "", "persona_description": "", "persona_arguments": "" } db_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'companies_v3_fixed_2.db') if not os.path.exists(db_path): return context try: conn = sqlite3.connect(db_path) c = conn.cursor() # Get Industry Data c.execute('SELECT pains, gains FROM industries WHERE name = ?', (industry_name,)) ind_res = c.fetchone() if ind_res: context["industry_pains"], context["industry_gains"] = ind_res # Get Persona Data c.execute('SELECT description, convincing_arguments FROM personas WHERE name = ?', (persona_name,)) per_res = c.fetchone() if per_res: context["persona_description"], context["persona_arguments"] = per_res conn.close() except Exception as e: print(f"DB Error in matrix lookup: {e}") return context def get_suggested_date(): """Calculates a suggested meeting date (3-4 days in future, avoiding weekends).""" now = datetime.datetime.now() # Jump 3 days ahead suggested = now + datetime.timedelta(days=3) # If weekend, move to Monday if suggested.weekday() == 5: # Saturday suggested += datetime.timedelta(days=2) elif suggested.weekday() == 6: # Sunday suggested += datetime.timedelta(days=1) days_de = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"] return f"{days_de[suggested.weekday()]}, den {suggested.strftime('%d.%m.')} um 10:00 Uhr" def clean_company_name(name): """Removes legal suffixes like GmbH, AG, etc. for a more personal touch.""" if not name: return "" # Remove common German legal forms cleaned = re.sub(r'\s+(GmbH|AG|GmbH\s+&\s+Co\.\s+KG|KG|e\.V\.|e\.K\.|Limited|Ltd|Inc)\.?(?:\s|$)', '', name, flags=re.IGNORECASE) return cleaned.strip() def get_qualitative_area_description(area_str): """Converts a string with area information into a qualitative description.""" nums = re.findall(r'\d+', area_str.replace('.', '').replace(',', '')) area_val = int(nums[0]) if nums else 0 if area_val >= 10000: return "sehr große Flächen" if area_val >= 5000: return "große Flächen" if area_val >= 1000: return "mittlere Flächen" if area_val > 0: return "kleine bis mittlere Flächen" return "Ihre Flächen" # Fallback def get_multi_solution_recommendation(area_str, purpose_str): """ Selects a range of robots based on surface area AND requested purposes. """ recommendations = [] purpose_lower = purpose_str.lower() # 1. Cleaning Logic (Area based) nums = re.findall(r'\d+', area_str.replace('.', '').replace(',', '')) area_val = int(nums[0]) if nums else 0 if "reinigung" in purpose_lower: if area_val >= 5000 or "über 10.000" in area_str: recommendations.append("den Scrubber 75 als industrielles Kraftpaket für Ihre Großflächen") elif area_val >= 1000: recommendations.append("den Scrubber 50 oder Phantas für eine wendige und gründliche Bodenreinigung") else: recommendations.append("den Phantas oder Pudu CC1 für eine effiziente Reinigung Ihrer Räumlichkeiten") # 2. Service/Transport Logic if any(word in purpose_lower for word in ["servieren", "abräumen", "speisen", "getränke"]): recommendations.append("den BellaBot zur Entlastung Ihres Teams beim Transport von Speisen und Getränken") # 3. Marketing/Interaction Logic if any(word in purpose_lower for word in ["marketing", "gästebetreuung", "kundenansprache"]): recommendations.append("den KettyBot als interaktiven Begleiter für Marketing und Patienteninformation") if not recommendations: recommendations.append("unsere wendigen Allrounder wie den Phantas") return { "solution_text": " und ".join(recommendations), "has_multi": len(recommendations) > 1 } def generate_email_draft(lead_data, company_data, booking_link="[IHR BUCHUNGSLINK]"): """ Generates a high-end, personalized sales email using Gemini API and Matrix knowledge. """ api_key = get_gemini_key() if not api_key: return "Error: Gemini API Key not found." # Extract Data from Lead Engine company_raw = lead_data.get('company_name', 'Interessent') company_name = clean_company_name(company_raw) contact_name = lead_data.get('contact_name', 'Damen und Herren') # Metadata from Lead meta = {} if lead_data.get('lead_metadata'): try: meta = json.loads(lead_data['lead_metadata']) except: pass area = meta.get('area', 'Unbekannte Fläche') purpose = meta.get('purpose', 'Reinigung') role = meta.get('role', 'Wirtschaftlicher Entscheider') salutation = meta.get('salutation', 'Damen und Herren') cleaning_functions = meta.get('cleaning_functions', '') # Data from Company Explorer ce_summary = company_data.get('research_dossier') or company_data.get('summary', '') ce_vertical = company_data.get('industry_ai') or company_data.get('vertical', 'Healthcare') ce_opener = company_data.get('ai_opener', '') # Multi-Solution Logic solution = get_multi_solution_recommendation(area, purpose) qualitative_area = get_qualitative_area_description(area) suggested_date = get_suggested_date() # Fetch "Golden Records" from Matrix matrix = get_matrix_context(ce_vertical, role) # Prompt Engineering for "Unwiderstehliche E-Mail" prompt = f""" Du bist ein Senior Sales Executive bei Robo-Planet. Antworte auf eine Anfrage von Tradingtwins. Schreibe eine E-Mail auf "Human Expert Level". WICHTIGE IDENTITÄT: - Anrede-Form: {salutation} (z.B. Herr, Frau) - Name: {contact_name} - Firma: {company_name} STRATEGIE: - STARTE DIREKT mit dem strategischen Aufhänger aus dem Company Explorer ({ce_opener}). Baue daraus den ersten Absatz. - KEIN "mit großem Interesse verfolge ich..." oder ähnliche Phrasen. Das wirkt unnatürlich. - Deine Mail reagiert auf die Anfrage zu: {purpose} für {qualitative_area}. - Fasse die vorgeschlagene Lösung ({solution['solution_text']}) KOMPAKT zusammen. Wir bieten ein ganzheitliches Entlastungskonzept an, keine Detail-Auflistung von Datenblättern. KONTEXT: - Branche: {ce_vertical} - Pains aus Matrix: {matrix['industry_pains']} - Dossier/Wissen: {ce_summary} - Strategischer Aufhänger (CE-Opener): {ce_opener} AUFGABE: 1. ANREDE: Persönlich. 2. EINSTIEG: Nutze den inhaltlichen Kern von: "{ce_opener}". 3. DER ÜBERGANG: Verknüpfe dies mit der Anfrage zu {purpose}. Erkläre, dass manuelle Prozesse bei {qualitative_area} angesichts der Dokumentationspflichten und des Fachkräftemangels zum Risiko werden. 4. DIE LÖSUNG: Schlage die Kombination aus {solution['solution_text']} als integriertes Konzept vor, um das Team in Reinigung, Service und Patientenansprache spürbar zu entlasten. 5. ROI: Sprich kurz die Amortisation (18-24 Monate) an – als Argument für den wirtschaftlichen Entscheider. 6. CTA: Schlag konkret den {suggested_date} vor. Alternativ: {booking_link} STIL: Senior, lösungsorientiert, direkt. Keine unnötigen Füllwörter. FORMAT: Betreff: [Prägnant, z.B. Automatisierungskonzept für {company_name}] [E-Mail Text] """ # Call Gemini API url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={api_key}" headers = {'Content-Type': 'application/json'} payload = {"contents": [{"parts": [{"text": prompt}]}]} try: response = requests.post(url, headers=headers, json=payload) response.raise_for_status() result = response.json() return result['candidates'][0]['content']['parts'][0]['text'] except Exception as e: return f"Error generating draft: {str(e)}" if __name__ == "__main__": # Test Mock mock_lead = { "company_name": "Klinikum Test", "contact_name": "Dr. Müller", "lead_metadata": json.dumps({"area": "5000 qm", "purpose": "Desinfektion und Boden", "city": "Berlin"}) } mock_company = { "vertical": "Healthcare / Krankenhaus", "summary": "Ein großes Klinikum der Maximalversorgung mit Fokus auf Kardiologie." } print(generate_email_draft(mock_lead, mock_company))