venesis / modules /sniper.py
JohnGFX's picture
Update Sniper and Radar logic
5f402f3
import streamlit as st # type: ignore
from datetime import datetime
import google.generativeai as genai # type: ignore
import json
import urllib.parse
from auth import get_current_user
from modules.venegard_brain import get_gemini_model, build_prompt
VENEGARD_TEAM = {
"Jan": {"role": "Partner", "email": "jan@venegard.com", "phone": "+420 605 875 808"},
"Filip": {"role": "Partner", "email": "filip@venegard.com", "phone": "+420 775 610 350"},
"Matěj": {"role": "Partner", "email": "matej@venegard.com", "phone": "+420 725 188 832"},
"Mikuláš": {"role": "Partner", "email": "mikulas@venegard.com", "phone": "+420 000 000 000"},
}
VENEGARD_DEFAULT_PROFILE = {
"role": "Partner",
"email": "contact@venegard.com",
"phone": "+420 000 000 000",
}
def render_sniper_tab(db, scrape_website, extract_pdf_text, uploaded_file, company_size, language, tone, focus, focus_arg, cta_type, creativity):
ui_lang = st.session_state.get("ui_lang", "CZ")
t = {
"CZ": {
"title": "Direct Outreach Engine",
"subtitle": "Generování sekvencí na míru pro konkrétní cíl na základě živé analýzy webu a nastavených parametrů.",
"target_url": "Cílová URL adresa",
"target_email": "Kontaktní e-mail klienta",
"btn_an": "Analyzovat web",
"btn_gen": "Vygenerovat e-maily",
"btn_send": "✉️ Otevřít v e-mailu a odeslat",
"stat_an": "Analyzuji DNA webu a hledám kontakty...",
"succ_an": "✅ Analýza dokončena. Parametry a kontakt byly nastaveny.",
"err_an": "Chyba analýzy:",
"stat_gen": "Inicializuji Venegard Sniper Engine…",
"subj_lbl": "VARIANTY PŘEDMĚTŮ:",
"mail_lbl": "HLAVNÍ EMAIL:",
"foll_lbl": "FOLLOW-UP:",
"succ_gen": "Sekvence vygenerována.",
"toast": "Uloženo do CRM pod jménem",
"err_gen": "Chyba při generování:",
"status": "Vygenerováno"
},
"EN": {
"title": "Direct Outreach Engine",
"subtitle": "Generate sequence-grade outreach assets for a single target based on live web intelligence and configured parameters.",
"target_url": "Target URL",
"target_email": "Target Contact Email",
"btn_an": "Analyze Website",
"btn_gen": "Generate Emails",
"btn_send": "✉️ Open in Email Client",
"stat_an": "Performing Autopilot Analysis & scraping contacts...",
"succ_an": "✅ Target profile synchronized. Parameters and contact updated.",
"err_an": "Analysis error:",
"stat_gen": "Initializing Venegard Sniper Engine...",
"subj_lbl": "SUBJECT VARIANTS:",
"mail_lbl": "MAIN EMAIL:",
"foll_lbl": "FOLLOW-UP:",
"succ_gen": "Sequence generated successfully.",
"toast": "Saved to CRM under",
"err_gen": "Generation error:",
"status": "Generated"
}
}[ui_lang]
st.markdown("<br>", unsafe_allow_html=True)
st.markdown(f"""
<h2 style="margin-bottom: 0.5rem;">{t['title']}</h2>
<p style="color:#9CA3AF; font-size:0.9rem; max-width:640px;">{t['subtitle']}</p>
""", unsafe_allow_html=True)
st.markdown("<br>", unsafe_allow_html=True)
col_url, col_mail = st.columns(2)
with col_url:
target_url = st.text_input(t['target_url'], placeholder="https://domain.com", key="sniper_url")
with col_mail:
# ZDE JE OPRAVA: Odstraněno "key", navázáno pouze na value
target_email = st.text_input(t['target_email'], value=st.session_state.get("target_email_auto", ""), placeholder="info@domain.com")
is_analyzed = st.session_state.get("last_analyzed_url") == target_url
if is_analyzed and target_url != "":
st.success(t['succ_an'])
col_an, col_ge = st.columns(2)
submit_analysis = col_an.button(t['btn_an'], use_container_width=True)
submit_sniper = col_ge.button(t['btn_gen'], key="btn_sniper", disabled=not is_analyzed, use_container_width=True)
if submit_analysis and target_url:
with st.status(t['stat_an'], expanded=True):
client_data = scrape_website(target_url)
model = get_gemini_model()
analysis_prompt = f"""
Analyzuj tento web: {client_data[:3000]}
Navrhni nejlepší obchodní strategii pro agenturu Venegard na základě našeho know-how a POKUS SE NAJÍT KONTAKTNÍ E-MAIL.
Vrať POUZE čistý JSON, kde hodnoty jsou CELÁ ČÍSLA a nalezený e-mail:
{{
"segment_idx": (0: Startup, 1: SME, 2: Mid-Market, 3: Corporate, 4: E-commerce, 5: Agency),
"focus_idx": (0: High-Conversion Web, 1: AI Infrastructure, 2: Cinematic Visuals, 3: Long-Term Growth),
"argument_idx": (0: Digital Authority, 1: Sales & Conversions, 2: 24/7 AI Automation, 3: Tech Dominance),
"tone_idx": (0: Modern B2B, 1: Direct & Punchy, 2: Visionary, 3: Premium),
"cta_idx": (0: Discovery Call, 1: Open Question, 2: Tech Demo, 3: Direct Pitch),
"contact_email": "nalezeny_email@firma.cz (nebo prázdný řetězec, pokud na webu není)"
}}
"""
try:
analysis_res = model.generate_content(analysis_prompt)
prefs = json.loads(analysis_res.text.strip().replace("```json", "").replace("```", ""))
st.session_state.segment_auto_idx = int(prefs.get("segment_idx", 0))
st.session_state.focus_auto_idx = int(prefs.get("focus_idx", 0))
st.session_state.argument_auto_idx = int(prefs.get("argument_idx", 0))
st.session_state.tone_auto_idx = int(prefs.get("tone_idx", 0))
st.session_state.cta_auto_idx = int(prefs.get("cta_idx", 0))
# ZDE JE OPRAVA: Přiřazení do nového stavu, který se načte do "value" parametru políčka
st.session_state.target_email_auto = prefs.get("contact_email", "")
st.session_state.last_analyzed_url = target_url
st.rerun()
except Exception as e:
st.error(f"{t['err_an']} {e}\n\nOdpověď AI: {analysis_res.text}")
if submit_sniper and target_url:
with st.status(t['stat_gen'], expanded=True) as status:
client_data = scrape_website(target_url)
pdf_context = extract_pdf_text(uploaded_file) if uploaded_file else ""
model = get_gemini_model()
raw_name = str(st.session_state.get("name", "")).strip() or "Venegard"
first_name = raw_name.split()[0].capitalize()
profile = VENEGARD_TEAM.get(raw_name, VENEGARD_TEAM.get(first_name, VENEGARD_DEFAULT_PROFILE))
user_phone = profile.get("phone", "+420 605 875 808")
user_email = profile.get("email", "jan@venegard.com")
user_full_name = "Jan Sedlář" if first_name == "Jan" else (raw_name if raw_name != "Venegard" else "Jan Sedlář")
task = f"""
Vytvoř outbound sekvenci v jazyce: {language}.
ABSOLUTNÍ ZÁKAZ FORMÁTOVÁNÍ:
ZAKAZUJI POUŽÍVAT HVĚZDIČKY (*), MŘÍŽKY (#) A TUČNÉ PÍSMO.
Nepiš nadpisy jako "Předmět:" nebo "Část 2". Napiš pouze čistý text oddělený řetězcem "===DELIC===".
Tón: {tone}, Segment: {company_size}.
ČÁST 1 (Předměty):
Napiš 4 krátké, přirozené předměty (max 6 slov). Každý na nový řádek. Bez odrážek a bez čísel.
===DELIC===
ČÁST 2 (Hlavní Email - STRICTNĚ PODLE TÉTO STRUKTURY):
1. Oslovení: Dobrý den,
2. Úvod a pochvala: "Už nějakou dobu sleduji..." nebo "Narazil jsem na...". Zmiň jednu konkrétní detailní věc z jejich webu, která tě zaujala (bez frází "je to super").
3. Přemostění: "Právě proto si myslím, že by si projekt zasloužil..."
4. Představení: "Jmenuji se {user_full_name} a jsem součástí digitální agentury Venegard. Pomáháme značkám růst v online světě."
5. Nabídka: Plynule a lidsky zmiň: {focus} a náš argument: {focus_arg}.
6. CTA: {cta_type}. (např. "Pokud budete mít chvíli, navrhuji krátký call, kde vám ukážu...")
Podpis (Zkopíruj PŘESNĚ toto):
S úctou
{user_full_name} | Venegard
venegard.com
{user_phone}
{user_email}
===DELIC===
ČÁST 3 (Follow-up):
Napiš ultra-krátký, přirozený a lidský follow-up po 3 dnech (max 2-3 věty).
Nesmí to znít suše nebo "korporátně". Přátelsky se připomeň, jestli minulý email nezapadl, a ukaž sebevědomí, že jim {focus} pomůže s {focus_arg}.
NA KONEC PŘIDEJ STEJNÝ PODPIS:
S úctou
{user_full_name} | Venegard
venegard.com
{user_phone}
{user_email}
"""
prompt = build_prompt(
task_specific_prompt=task,
target_client_data=f"URL: {target_url}\nObsah Webu: {client_data}",
extra_context=pdf_context
)
try:
response = model.generate_content(prompt, generation_config=genai.GenerationConfig(temperature=creativity))
clean_text = response.text.replace("*", "").replace("#", "")
parts = clean_text.split("===DELIC===")
subjects_text = parts[0].strip() if len(parts) > 0 else ""
email_body = parts[1].strip() if len(parts) > 1 else ""
followup_body = parts[2].strip() if len(parts) > 2 else ""
subjects_text = subjects_text.replace("ČÁST 1 (Předměty):", "").replace("Předměty:", "").strip()
email_body = email_body.replace("ČÁST 2 (Hlavní Email):", "").replace("Hlavní Email:", "").strip()
followup_body = followup_body.replace("ČÁST 3 (Follow-up):", "").replace("Follow-up:", "").strip()
status.update(label=t['succ_gen'], state="complete", expanded=False)
st.markdown("""<style>code { white-space: pre-wrap !important; word-break: break-word !important; }</style>""", unsafe_allow_html=True)
st.markdown(f'<div class="result-box"><div style="color:#1E60F2; font-weight:600; margin-bottom:10px;">{t["subj_lbl"]}</div></div>', unsafe_allow_html=True)
st.code(subjects_text, language=None)
st.markdown(f'<div class="result-box"><hr style="margin:25px 0; border-color:#1F2937;"><div style="color:#1E60F2; font-weight:600; margin-bottom:10px;">{t["mail_lbl"]}</div></div>', unsafe_allow_html=True)
st.code(email_body, language=None)
if target_email:
first_subj = subjects_text.split('\n')[0].strip() if subjects_text else "Návrh spolupráce"
subject_enc = urllib.parse.quote(first_subj)
body_enc = urllib.parse.quote(email_body)
mailto_link = f"mailto:{target_email}?subject={subject_enc}&body={body_enc}"
st.markdown(f'''
<a href="{mailto_link}" target="_blank" style="text-decoration: none;">
<div style="background-color: #1E60F2; color: white; padding: 12px 20px; border-radius: 8px; text-align: center; font-weight: 600; margin-top: 15px; margin-bottom: 5px; transition: all 0.2s ease;">
{t['btn_send']}
</div>
</a>
''', unsafe_allow_html=True)
else:
st.info("💡 E-mail nebyl na webu nalezen. Doplňte ho nahoře do pole, aby se objevilo tlačítko pro rychlé odeslání.")
st.markdown(f'<div class="result-box"><hr style="margin:25px 0; border-color:#1F2937;"><div style="color:#1E60F2; font-weight:600; margin-bottom:10px;">{t["foll_lbl"]}</div></div>', unsafe_allow_html=True)
st.code(followup_body, language=None)
record = {
"Autor": user_full_name,
"Datum": datetime.now().strftime("%d.%m.%Y"),
"Nástroj": "Sniper",
"Firma": target_url.replace("https://", "").replace("http://", "").split('/')[0],
"Email": target_email,
"URL": target_url,
"Skóre": "N/A",
"Analýza": "Individuální outreach"
}
st.session_state.crm_data.append(record)
db.save(st.session_state.crm_data)
st.toast(f"{t['toast']} {user_full_name}")
except Exception as e:
status.update(label=t['err_gen'], state="error")
st.error(f"{t['err_gen']} {e}")