/* ============================================================ MSH PI, INC. — AI Chatbot (Standalone HTML embed) Source: mshpi-chatbot.jsx (same logic, inlined here) ============================================================ */ const { useState, useEffect, useRef } = React; // ── Config ────────────────────────────────────────────────── const CHATBOT_PROXY = "/chatbot-proxy/"; const LOGO_URL = "/assets/MSHPI_CHAT_ICON.svg"; const CF_ENDPOINTS = [ CHATBOT_PROXY, "/chatbot-proxy/index.php", "https://mshpi-ai-proxy.jhan1102.workers.dev", "https://mshpi-ai-proxy.jhan1102.workers.dev/" ]; // SYSTEM_PROMPT is stored securely in the Cloudflare Worker — not exposed here. const PHONE_EN = "📞 LA Office: (213) 383-7007\n" + "📞 English 24hrs: (213) 784-2522\n" + "📞 한국어 24시간: (323) 633-3232"; const ERR_MSG = "⚠️ Unable to connect. Please call us directly:\n" + PHONE_EN; const DEFAULT_COLORS = { headerBg: "#0f1e35", headerText: "#c9a84c", bubbleBg: "#0f1e35", bubbleIcon: "#c9a84c", userBubble: "#4E82EE", botBubble: "#f0f4f8", chatBg: "#ffffff", sendBtn: "#4E82EE", accentColor: "#c9a84c", }; const QUICK = [ { label: "Our Services", msg: "What services do you offer?" }, { label: "Contact Us", msg: "How do I contact MSH PI?" }, { label: "Consultation", msg: "I would like a consultation." }, { label: "서비스 안내", msg: "어떤 서비스를 제공하시나요?" }, { label: "연락처", msg: "어떻게 연락하나요?" }, ]; async function callWorker(messages) { let lastErr; for (const endpoint of CF_ENDPOINTS) { try { const r = await fetch(endpoint, { method: "POST", credentials: "omit", headers: { "Content-Type": "application/json", "Accept": "application/json, text/plain;q=0.9, */*;q=0.8" }, body: JSON.stringify({ messages, site: "mshpi", source: "website" }), }); if (!r.ok) throw new Error(`cf_fail_${r.status}_${endpoint}`); const contentType = (r.headers.get("content-type") || "").toLowerCase(); if (contentType.includes("application/json")) { const d = await r.json(); if (d?.text) return d.text; if (typeof d?.reply === "string" && d.reply.trim()) return d.reply; if (typeof d?.message === "string" && d.message.trim()) return d.message; } else { const t = (await r.text()).trim(); if (t) return t; } throw new Error("no_text"); } catch (err) { lastErr = err; } } throw lastErr || new Error("cf_fail"); } function Bubble({ role, text, colors }) { const isBot = role === "bot"; const lines = text.split("\n"); return (
{isBot ? M : }
{lines.map((line,i) => { const isDisclaimer = line.includes("Many Korean Private Investigation agencies") || line.includes("MSH PI, INC. has been operating for more than 21 years") || line.includes("FOR INVESTIGATIVE SERVICES"); const isBold = !isDisclaimer && line.startsWith("**") && line.endsWith("**"); const clean = line.replace(/[*][*]([^*]+)[*][*]/g,"$1").replace(/[*]([^*]+)[*]/g,"$1"); if (!clean.trim()) return
; const warn = clean.startsWith("⚠️"); return (
{clean.split(/(https?:\/\/\S+|\(\d{3}\)\s?\d{3}[-\s\.]\d{4}|\b\d{3}[-\.]\d{3}[-\.]\d{4}\b)/g).map((part,j) => { if (part.match(/^https?:\/\//)) return {part} ↗; if (part.match(/^(?:\(\d{3}\)|\d{3}[-\.])/)) return {part}; return {part}; })}
); })}
); } function AdminPanel({ colors, onSave, onClose }) { const [local,setLocal]=useState({...colors}); const set=(k,v)=>{const n={...local,[k]:v};setLocal(n);onSave(n);}; const FIELDS=[["Header BG","headerBg"],["Header Text","headerText"],["Bubble BG","bubbleBg"],["Bubble Icon","bubbleIcon"],["User Msg BG","userBubble"],["Bot Msg BG","botBubble"],["Window BG","chatBg"],["Send Button","sendBtn"],["Accent","accentColor"]]; return (
e.stopPropagation()} style={{background:"#fff",borderRadius:14,padding:22,width:300,maxHeight:"85vh",overflowY:"auto",boxShadow:"0 16px 48px rgba(0,0,0,0.3)"}}>
🔧 Colour Settings
{FIELDS.map(([label,key])=>(
{label}
set(key,e.target.value)} style={{width:32,height:24,border:"1px solid #ddd",borderRadius:4,cursor:"pointer",padding:2}}/> {local[key]}
))}
); } function MSHPIChatbot() { const [msgs,setMsgs]=useState([]); const [hist,setHist]=useState([]); const [input,setInput]=useState(""); const [busy,setBusy]=useState(false); const [quick,setQuick]=useState(true); const [open,setOpen]=useState(()=>{ try { return localStorage.getItem("mshpi_chat_open") !== "0"; } catch { return true; } }); const [admin,setAdmin]=useState(false); const [colors,setColors]=useState(DEFAULT_COLORS); const bottomRef=useRef(null); const inputRef=useRef(null); const taRef=useRef(null); useEffect(()=>{ setTimeout(()=>{ setMsgs([{id:1,role:"bot",text:"**안녕하세요! MSH PI, INC.에 연락해 주셔서 감사합니다.**\n**오늘 어떻게 도와드릴까요?**\n🇰🇷 웹사이트를 한국어로 보려면 메뉴에서 한국 국기를 선택하세요.\n\n**Hello! Thank you for contacting MSH PI, INC.**\n**How can I help you today?**"}]); },300); },[]); useEffect(()=>{ bottomRef.current?.scrollIntoView({behavior:"smooth"}); },[msgs,busy]); const saveColors=c=>{ setColors(c); try{localStorage.setItem("mshpi_colors",JSON.stringify(c));}catch{} }; useEffect(()=>{ try{const s=localStorage.getItem("mshpi_colors");if(s)setColors(JSON.parse(s));}catch{} },[]); useEffect(()=>{ try{localStorage.setItem("mshpi_chat_open", open ? "1" : "0");}catch{} },[open]); const send=async text=>{ const t=(text||"").trim(); if(!t||busy)return; setInput(""); if(taRef.current)taRef.current.style.height=""; setQuick(false); setBusy(true); setMsgs(prev=>[...prev,{id:Date.now(),role:"user",text:t}]); const newHist=[...hist,{role:"user",content:t}]; const isKorean=/[\uAC00-\uD7A3\u1100-\u11FF\u3130-\u318F]/.test(t); const langHint=isKorean?"[Reply ONLY in Korean] ":"[Reply ONLY in English] "; const apiHist=newHist.map((m,i)=>i===newHist.length-1&&m.role==="user"?{...m,content:langHint+m.content}:m); let reply; try { reply=await callWorker(apiHist); } catch(e) { reply=null; } let botText=reply||ERR_MSG; botText=botText.replace(/https?:\/\/(?:www\.)?mshpiinc\.com[^\s)]*/gi,""); botText=botText.replace(/\*{0,2}MSH PI,? INC?\.? has been operating for more than 21[\s\S]*?outside firms\.(?:[^\n]*)?\*{0,2}\n?/gi,"") .replace(/\*{0,2}Many Korean Private Investigation[\s\S]*?outside firms\.(?:[^\n]*)?\*{0,2}\n?/gi,"") .replace(/\n{3,}/g,"\n\n").trim(); if(/contact\s+page/i.test(botText)&&!botText.includes("mshpi.com/contact")){ const kr=/[\uAC00-\uD7A3]/.test(botText); botText+=kr?"\nhttps://www.mshpi.com/ko/contact-korean-private-investigator":"\nhttps://www.mshpi.com/contact-korean-private-investigator"; } if(!botText.trim()) botText=ERR_MSG; setHist([...newHist,{role:"assistant",content:botText}]); setMsgs(prev=>[...prev,{id:Date.now()+1,role:"bot",text:botText}]); setBusy(false); setTimeout(()=>inputRef.current?.focus(),50); }; const onKey=e=>{ if(e.key==="Enter"&&!e.shiftKey){e.preventDefault();send(input);} }; if(!open) return (
); return (
{admin&&setAdmin(false)}/>}
{/* Header */}
{e.target.style.display="none";e.target.parentNode.innerHTML='M';}} />
MSH PI, INC.
{busy?"Thinking...":"Online"} · 한국어 가능
{/* Licence bar */}
CA PI Lic #24994{" · "}Est. 2004 · Licensed & Insured
{/* Messages */}
{msgs.map(m=>)} {busy&&(
M
{[0,1,2].map(i=>
)}
)}
{/* Quick replies */} {quick&&(
{QUICK.map(q=>( ))}
)} {/* Input */}