Files
rm-circle-team-router/public/training.js
T
martbost ca4b5bf34f Members area: Circle Method lessons 2-10 + Video 8 gated behind position ownership
The product now has a lid. Server refuses the gated video FILES without a
wallet-verified member session (the Mini App bridge session counts, so
Telegram members see no locks). Lesson 1, every poster, and all join-funnel
and transparency videos stay public: free preview + trust engine. Training
page renders locked cards with a one-signature unlock (same SIWE flow as
Messages) and a members-unlocked badge. Chatbot: new members-area canned
answer, Method answer notes the preview, AI prompt routing rule now answers
prospects directly instead of sending locked links; bot lesson command notes
the unlock path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 04:10:57 -05:00

71 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(async function(){
try{
const r=await fetch('/api/public/config');
if(!r.ok)return;
const c=await r.json();
if(c.siteName){document.title=`Training | ${c.siteName}`;const bn=document.getElementById('brandName');if(bn)bn.textContent=c.siteName}
}catch(e){}
})();
// live dollar note in the card-buy callout — computed, never hand-written
fetch('/api/public/config').then(function(r){return r.json()}).then(function(c){
var el=document.getElementById('fundUsdNote');
if(el&&c.polUsd>0){var pol=(c.premiumEntryPol||362)+23;el.textContent=' — about $'+Math.round(pol*c.polUsd)+' at today’s price';}
}).catch(function(){});
// carry the member's saved id onto tools links so promo assets arrive personalized
try{var tid=localStorage.getItem('rmc.toolsId')||localStorage.getItem('ctb.myId');
if(tid&&/^\d+$/.test(tid))document.querySelectorAll('a[href="/tools"]').forEach(function(a){a.href='/tools?id='+tid;});}catch(e){}
// Members area: Circle Method lessons 2-10 + Video 8 are member-only. The
// SERVER refuses those video files without a session — this makes the page
// honest about it: locked cards with a one-signature unlock. Members arriving
// through the Telegram Mini App already carry a session and see no locks.
(async function(){
'use strict';
const gated=[...document.querySelectorAll('video')].filter(v=>{
const s=v.getAttribute('src')||'';
return (/\/v\/rmc-method-m\d+l\d+-/.test(s)&&s.indexOf('rmc-method-m1l1-')<0)||s.indexOf('/training/egift_cards.mp4')>=0;
});
if(!gated.length)return;
let member=null;
try{const r=await fetch('/api/public/msg-me');if(r.ok)member=await r.json();}catch(e){}
if(member&&member.id){
const h=document.getElementById('module-1');
if(h){const b=document.createElement('p');b.className='micro';b.style.cssText='max-width:880px;margin:6px auto 0;color:var(--ok)';b.textContent='✅ Members area unlocked — position #'+member.id;h.after(b);}
return;
}
gated.forEach(v=>{
const poster=v.getAttribute('poster')||'';
const box=document.createElement('div');
box.className='rmc-lock';
box.style.cssText='position:relative;border-radius:12px;overflow:hidden;aspect-ratio:16/9;display:flex;align-items:center;justify-content:center;background:#08131f center/cover no-repeat';
if(poster)box.style.backgroundImage="linear-gradient(rgba(4,10,17,.85),rgba(4,10,17,.85)),url('"+poster+"')";
box.innerHTML='<div style="text-align:center;padding:18px;max-width:460px">'
+'<div style="font-size:32px">🔒</div>'
+'<div style="font-weight:800;font-size:17px;margin:6px 0 4px">Members area</div>'
+'<p class="micro" style="color:var(--muted);margin:0 0 12px">This lesson comes with every team position. Sign in with the wallet that owns your position — one free signature, it can\'t move funds. Linked on Telegram? The ☰ Mini App in @RMCircleBot unlocks everything automatically.</p>'
+'<button class="btn btn-primary btn-sm rmc-unlock">🔓 I\'m a member — connect &amp; unlock</button> '
+'<a class="btn btn-secondary btn-sm" href="/start">Not yet? See how to join</a>'
+'<div class="micro rmc-lock-err" style="color:#ff7b7b;margin-top:8px"></div></div>';
v.replaceWith(box);
});
const UNLOCK_LABEL='🔓 I\'m a member — connect & unlock';
document.querySelectorAll('button.rmc-unlock').forEach(b=>b.addEventListener('click',async ev=>{
const btn=ev.currentTarget,err=btn.closest('.rmc-lock').querySelector('.rmc-lock-err');
btn.disabled=true;btn.textContent='Connecting…';
try{
const eth=await window.RMCWallet.pick();
if(!eth){err.textContent='No wallet in this browser. On a phone, open this page inside your wallet app\'s browser — or use the Telegram Mini App.';btn.disabled=false;btn.textContent=UNLOCK_LABEL;return;}
const accs=await eth.request({method:'eth_requestAccounts'});const account=accs[0];
const ch=await(await fetch('/api/public/msg-challenge',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({address:account})})).json();
if(!ch.message)throw new Error(ch.error||'Could not start sign-in.');
let hex='0x';for(const b2 of new TextEncoder().encode(ch.message))hex+=b2.toString(16).padStart(2,'0');
const sig=await eth.request({method:'personal_sign',params:[hex,account]});
const vr=await(await fetch('/api/public/msg-verify',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({address:account,signature:sig})})).json();
if(!vr.ok)throw new Error(vr.error||'Verification failed — is this the wallet that owns your position?');
location.reload();
}catch(e){err.textContent=e.message||String(e);btn.disabled=false;btn.textContent=UNLOCK_LABEL;}
}));
})();