Add collapsible Team Help chat widget on all public pages

Self-contained assistant (no external services) with a knowledge base
covering the strategy, levels, costs, wallet setup, funding, joining,
training videos, and live current-sponsor/queue data; falls back to
the configured Telegram group link when it can't answer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-08-12 08:35:28 -05:00
parent 023f8a3464
commit 708068a9a7
5 changed files with 116 additions and 3 deletions
+89
View File
@@ -0,0 +1,89 @@
(function(){
let config=null,sponsorInfo=null;
async function loadData(){
try{const r=await fetch('/api/public/config');if(r.ok)config=await r.json()}catch(e){}
try{const r=await fetch('/api/public/current-sponsor');if(r.ok)sponsorInfo=await r.json()}catch(e){}
}
function esc(s){return String(s??'').replace(/[&<>'"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;',"'":'&#39;','"':'&quot;'}[c]))}
function tg(){return (config&&config.telegramUrl)||'https://t.me/cryptoteambuild'}
function pol(){return (config&&config.premiumEntryPol)||362}
function tgLink(label){return `<a href="${esc(tg())}" target="_blank" rel="noopener noreferrer">${esc(label||'Open the Telegram group ↗')}</a>`}
const KB=[
{k:['what is','rm circle','about','this site','this project','crypto team build','who are you','network'],
a:()=>`The RM Circle is a team build project of the <strong>Crypto Team Build Network</strong>. We enter the RM Circle dApp at the Premium tier (${pol()} POL on Polygon) and build as one team: each member gets exactly 2 directs, then helps those 2 get their 2 — building depth instead of width. Start with the <a href="/training">training videos</a>.`},
{k:['how does it work','strategy','how it works','plan','roadmap','system','duplicate','duplication'],
a:()=>`The core strategy: <strong>get your 2, retire your link, help your 2 get their 2, upgrade with profits, repeat.</strong> Once a position has 2 directs it's qualified — we stop using its link and move the team's effort down to the next positions. The full picture is in <a href="/training">Video 1 of the training</a>, and the roadmap poster is on the <a href="/">home page</a>.`},
{k:['cost','price','entry','how much','362','fee','pol needed','expensive'],
a:()=>`Premium entry is <strong>${pol()} POL</strong> on Polygon Mainnet, plus a small extra POL balance for network gas. POL's dollar value changes with the market. Only ever use funds you can afford to lose.`},
{k:['pol','polygon','network','chain','gas','matic','mainnet'],
a:()=>`The build runs on <strong>Polygon Mainnet</strong> (chain ID 137). POL is the network's native token — it pays for your Premium entry and for transaction gas. In MetaMask, make sure Polygon is the selected network before doing anything.`},
{k:['metamask','wallet','create wallet','install','set up wallet','setup wallet'],
a:()=>`Install MetaMask only from the <a href="https://metamask.io/" target="_blank" rel="noopener noreferrer">official site</a> or your device's official app store, then create your wallet. <a href="/training">Video 2 in the training</a> walks through it step by step. Never share your Secret Recovery Phrase with anyone — not the team, not this site, not a "support agent".`},
{k:['fund','funding','buy pol','get pol','deposit','exchange','transfer','send pol'],
a:()=>`You'll need about ${pol()} POL plus a little extra for gas, on Polygon Mainnet. <a href="/training">Video 3 in the training</a> shows how to fund your wallet. Double-check you're sending POL on the Polygon network — not another chain.`},
{k:['join','buy','purchase','premium position','enter','sign up','signup','register','get started','start'],
a:()=>`Ready to join? Go to the <a href="/start">Getting Started page</a> — it always shows the current team sponsor to use. <a href="/training">Video 4 in the training</a> walks through the purchase in the dApp. Always confirm the sponsor ID in the dApp matches the one on the start page before signing.`},
{k:['sponsor','current sponsor','who do i join under','placement','whose link','referral'],
a:()=>{const s=sponsorInfo&&sponsorInfo.sponsor;return s?`The current team sponsor is <strong>ID ${esc(s.id)}${s.name?' ('+esc(s.name)+')':''}</strong> — always verify on the <a href="/start">Getting Started page</a> right before you join, because placements rotate as positions qualify. Don't use an old screenshot or saved link.`:`The current sponsor is always shown live on the <a href="/start">Getting Started page</a> — check there right before joining, because placements rotate as positions qualify.`}},
{k:['qualified','qualification','2 directs','two directs','directs','retire'],
a:()=>`A position is <strong>qualified</strong> once it has exactly 2 directs. At that point we retire its referral link — no more directs on it — and shift the team's recruiting to its two positions below. That's what keeps the build growing in depth.`},
{k:['level','levels','upgrade','scintilla','ascensus','fabrica','culmen','apex','fastigium','vertex','corona','8 levels'],
a:()=>`There are 8 Premium levels: <strong>Scintilla, Ascensus, Fabrica, Culmen, Apex, Fastigium, Vertex, Corona</strong>. Everyone starts at Scintilla. Upgrade as quickly as practical — ideally using earned POL — because the first two payments at each level are designed to help fund your next upgrade. Stay aware of your active downline's levels so you don't fall behind.`},
{k:['30 positions','goal','milestone','matrix','how many people','team size'],
a:()=>`The first major team goal is <strong>30 properly placed positions</strong>: 2 → 4 → 8 → 16 across the first four generations. Then we keep going — 32, 64, 128 and beyond.`},
{k:['moving link','why not my link','own link','my referral','use my link','extra legs'],
a:()=>`We use a <strong>moving-link strategy</strong>: one link at a time until that position qualifies with 2 directs, then the link retires and effort moves down. That's why you should always use the sponsor shown on the <a href="/start">Getting Started page</a> — not a personal or saved link. Extra directs on a qualified link create shallow legs instead of depth.`},
{k:['risk','safe','scam','lose money','guarantee','guaranteed','income','earnings','legit'],
a:()=>`Straight answer: participation involves cryptocurrency and smart-contract risk, and <strong>no income is guaranteed</strong>. Results depend on team effort, duplication, upgrades, smart-contract rules, and POL's market value. Only use funds you can afford to lose.`},
{k:['video','videos','training','watch','tutorial','learn','how to'],
a:()=>`All four training videos are on the <a href="/training">Training page</a>: (1) How the team build works, (2) Create your MetaMask wallet, (3) Funding your wallet, (4) Buying the Premium position. Watch them in order.`},
{k:['seed phrase','secret recovery','recovery phrase','private key','12 words','passphrase'],
a:()=>`<strong>Never share your Secret Recovery Phrase or private keys with anyone</strong> — not this site, not a sponsor, not "support". Anyone asking for it is trying to steal your funds. MetaMask will never ask for it either, except when you restore a wallet yourself.`},
{k:['telegram','group','chat','human','support','contact','talk','someone','admin','help me'],
a:()=>`For live help from the team, join the Telegram group: ${tgLink()}. Tell them where you're stuck and someone will walk you through it.`},
{k:['after join','next','what happens','confirmed','joined','i bought','i am in'],
a:()=>`Once your position is confirmed: tell the team your new RM Circle ID and your sponsor ID (in the Telegram group: ${tgLink('here')}) so your placement gets mapped. Then your job is simple — get your 2 directs, and when you're qualified, help your 2 get their 2.`},
{k:['waiting','queue','line','how long','when my turn'],
a:()=>{const w=sponsorInfo&&typeof sponsorInfo.waitingCount==='number'?sponsorInfo.waitingCount:null;return `${w!==null?`Right now there ${w===1?'is':'are'} <strong>${w}</strong> team placement${w===1?'':'s'} waiting behind the current sponsor. `:''}The queue moves every time a position qualifies with its 2 directs — the more the team helps each other, the faster it rotates.`}},
];
function answer(q){
const t=q.toLowerCase().replace(/[^a-z0-9 ]/g,' ');
let best=null,bestScore=0;
for(const e of KB){
let score=0;
for(const k of e.k){if(t.includes(k))score+=k.includes(' ')?2:1}
if(score>bestScore){bestScore=score;best=e}
}
if(best&&bestScore>0)return best.a();
return `I don't have a good answer for that one. The team can help you directly — ${tgLink('join our Telegram group ↗')} and ask there. You can also check the <a href="/training">training videos</a> or the <a href="/">strategy overview</a>.`;
}
const CHIPS=['How does it work?','What does it cost?','Who is the current sponsor?','How do I set up a wallet?','Is there risk?'];
const fab=document.createElement('button');
fab.className='chat-fab';fab.setAttribute('aria-label','Open team help chat');fab.innerHTML='💬';
const panel=document.createElement('div');
panel.className='chat-panel chat-hidden';
panel.innerHTML=`<div class="chat-head"><div><strong>Team Help</strong><div class="chat-sub">Ask about the RM Circle team build</div></div><button class="chat-close" aria-label="Close chat">×</button></div><div class="chat-msgs"></div><div class="chat-chips"></div><form class="chat-input"><input type="text" placeholder="Type a question…" aria-label="Your question" maxlength="300"><button type="submit" class="btn btn-primary btn-sm">Send</button></form><div class="chat-foot">Can't find it? ${tgLink('Get live help on Telegram ↗')}</div>`;
document.body.appendChild(fab);document.body.appendChild(panel);
const msgs=panel.querySelector('.chat-msgs'),chipsEl=panel.querySelector('.chat-chips'),form=panel.querySelector('.chat-input'),input=form.querySelector('input');
function addMsg(html,who){const d=document.createElement('div');d.className=`chat-msg ${who}`;if(who==='user'){d.textContent=html}else{d.innerHTML=html}msgs.appendChild(d);msgs.scrollTop=msgs.scrollHeight}
function ask(q){addMsg(q,'user');setTimeout(()=>{addMsg(answer(q),'bot');},250)}
let started=false;
function open(){
panel.classList.remove('chat-hidden');fab.classList.add('chat-hidden');input.focus();
if(!started){started=true;loadData().then(()=>{
panel.querySelector('.chat-foot').innerHTML=`Can't find it? ${tgLink('Get live help on Telegram ↗')}`;
addMsg(`Hi! I can answer questions about the RM Circle team build — the strategy, wallet setup, funding, joining, levels, and more. What do you want to know?`,'bot');
for(const c of CHIPS){const b=document.createElement('button');b.type='button';b.className='chat-chip';b.textContent=c;b.addEventListener('click',()=>{chipsEl.classList.add('chat-hidden');ask(c)});chipsEl.appendChild(b)}
})}
}
function close(){panel.classList.add('chat-hidden');fab.classList.remove('chat-hidden')}
fab.addEventListener('click',open);
panel.querySelector('.chat-close').addEventListener('click',close);
form.addEventListener('submit',e=>{e.preventDefault();const q=input.value.trim();if(!q)return;input.value='';chipsEl.classList.add('chat-hidden');ask(q)});
})();