Mini App join funnel: startapp sponsor attribution + wallet-app handoff

- /app decodes startapp=<sponsorId>[_<angle>] -> unlinked visitors land on the
  sponsor squeeze page /join/<ref>?src=miniapp (linked members still go to
  their dashboard); no-invite prospects get a gold Join button -> /join-now
- tg-app.js: sync __rmcInTg flag; external links route out of the webview via
  openLink/openTelegramLink (wallet deep links reach the wallet app reliably)
- join-now.js in the webview leads with the MetaMask/Trust deep links (no
  injected wallet can exist there); ref + src survive into the wallet browser
- tgbot: links command + weekly digest include the t.me/<bot>/<short>?startapp
  Telegram-native invite once config.miniAppShortName is set (new PATCH key)
- Synced chat.js canned answer + AI prompt (prospects can join from the app)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-08-23 06:32:51 -05:00
parent cd361a1eaa
commit f876351391
8 changed files with 60 additions and 7 deletions
+2 -1
View File
@@ -14,7 +14,8 @@
<p style="color:var(--muted);max-width:420px;margin:10px auto">This app shows YOUR live dashboard — payments, team, coaching. To prove a position is yours, link it once with your wallet (a free signature, it can't move funds):</p>
<p style="color:var(--muted);max-width:420px;margin:10px auto;text-align:left">1. Open <b>rmcircle.team/my</b> in your browser<br>2. Enter your member ID → <b>Messages</b> → sign in with your wallet<br>3. Tap <b>“Connect Telegram”</b> — it deep-links right back to this bot</p>
<p style="margin:18px 0 8px"><button id="btn-open-site" class="btn btn-primary">Open the dashboard to link ↗</button></p>
<p><button id="btn-browse" class="btn">Just looking? See how the team works</button></p>
<p><button id="btn-join" class="btn btn-primary" style="background:linear-gradient(135deg,var(--gold),var(--gold2))">Not a member yet? Join the team</button></p>
<p><button id="btn-browse" class="btn">Just looking? See how it works</button></p>
</div>
<div id="st-error" style="display:none">
<h1 style="font-size:22px">Hmm, that didn't verify</h1>
+15 -1
View File
@@ -14,6 +14,7 @@
var url = location.origin + '/my';
if (tg && tg.openLink) tg.openLink(url); else location.href = url;
});
on('btn-join', function () { location.href = '/join-now?src=miniapp'; });
on('btn-browse', function () { location.href = '/start'; });
on('btn-retry', function () { location.reload(); });
@@ -24,12 +25,25 @@
try { tg.expand(); } catch (e) {}
try { tg.setHeaderColor('#071421'); tg.setBackgroundColor('#071421'); } catch (e) {}
// Invite attribution: t.me/<bot>/<short>?startapp=<sponsorId>[_<angle>]
// routes a prospect to the sponsor's personalized squeeze page. The webview
// session keeps ?src=miniapp so the join records where it came from.
var ref = null, angle = null;
try {
var sp = (tg.initDataUnsafe && tg.initDataUnsafe.start_param) || '';
var m = /^(\d{1,15})(?:[_-]([a-z]{2,12}))?$/.exec(sp);
if (m) { ref = m[1]; angle = m[2] || null; }
} catch (e) {}
fetch('/api/public/tg-webapp-auth', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ initData: tg.initData })
}).then(function (r) { return r.json(); }).then(function (d) {
if (d && d.ok && d.linked) { location.replace('/my/' + d.id); return; }
if (d && d.ok) { show('st-unlinked'); return; }
if (d && d.ok) {
if (ref) { location.replace('/join/' + ref + '?src=miniapp' + (angle ? '&v=' + angle : '')); return; }
show('st-unlinked'); return;
}
var el = document.getElementById('err-detail');
if (el && d && d.error) el.textContent = d.error;
show('st-error');
+1 -1
View File
@@ -36,7 +36,7 @@
{k:['pyramid','ponzi','pyramid scheme','ponzi scheme','mlm','recruiting scheme','is this a scheme'],
a:()=>`A pyramid or Ponzi scheme funnels everyone's money to a central company and pays earlier joiners out of later joiners' deposits — and you can't verify any of it. This is the opposite: <strong>no company holds the money</strong>. A public smart contract on Polygon sends each payment person-to-person in the same transaction it arrives, and you can read the code and every payout yourself on-chain — nothing pooled, nothing hidden, rules that can't be changed. It <em>is</em> a team build, so it takes real effort and carries real crypto risk — not a passive investment. But you don't have to trust anyone; verify it at <a href="/contract">rmcircle.team/contract</a>. No income is guaranteed.`},
{k:['telegram bot','connect telegram','payout ping','telegram notification','message my downline','contact my downline','reach my downline','mini app','miniapp','telegram app','dashboard in telegram'],
a:()=>`Link your position to our Telegram companion bot: open your <a href="/my">dashboard</a> → Messages → sign in with your wallet → tap "Connect Telegram". Once linked you get an instant DM whenever your position catches a payment, team messages reach you natively in Telegram (reply right there to answer), you're pinged when someone joins on your link, and "links" gives you all your invite links. Linked members can also tap the bot's ☰ menu button to open the <strong>Mini App</strong> — your full live dashboard, promo tools, and the Circle Method right inside Telegram, no login needed. Messaging still follows your matrix lines only — same rules as the site.`},
a:()=>`Link your position to our Telegram companion bot: open your <a href="/my">dashboard</a> → Messages → sign in with your wallet → tap "Connect Telegram". Once linked you get an instant DM whenever your position catches a payment, team messages reach you natively in Telegram (reply right there to answer), you're pinged when someone joins on your link, and "links" gives you all your invite links. Linked members can also tap the bot's ☰ menu button to open the <strong>Mini App</strong> — your full live dashboard, promo tools, and the Circle Method right inside Telegram, no login needed. The bot's "links" command includes a <strong>Telegram-native invite link</strong> that opens your invite page instantly inside Telegram — great for sharing in groups and DMs; the prospect's join finishes in their own wallet app (zero custody, as always). Messaging still follows your matrix lines only — same rules as the site.`},
{k:['cash out','cashout','spend my crypto','withdraw','off ramp','off-ramp','gift card','giftcard','get my money out','turn into cash','convert to dollars'],
a:()=>`Three good paths, easiest first: (1) <strong>E-gift cards</strong> — send POL to CWallet (cwallet.com), swap to a US-dollar token there (their internal swaps are virtually free), and buy gift cards for brands you already use — groceries, gas, Amazon. eGifter (egifter.com) also takes crypto directly. (2) <strong>Straight cash-out</strong> via a regulated exchange in your country (Coinbase, Kraken…): send, sell, withdraw to your bank. (3) <strong>Keep it working</strong> — many members leave catches in the wallet to fund their next level. Full guide: <a href="/training#spending">Spending what you earn</a>. Honest notes: those are independent custodial services — only move what you're about to spend; taxes may apply where you live; not financial advice.`},
{k:['circle method','recruiting course','how do i recruit','recruiting training','get my two','get your two','how to invite','module 1','lessons'],
+9
View File
@@ -191,6 +191,15 @@ fetch('/api/public/config').then(function(r){return r.json()}).then(function(c){
}
document.addEventListener('DOMContentLoaded',function(){
loadSponsor();
// Telegram Mini App webview: no wallet can ever be injected here, so lead
// with the deep links immediately — the join finishes in the wallet app's
// own browser (same page, sponsor + attribution carried in the URL).
if(window.__rmcInTg){
showWalletHelp();
var hd=$('walletHelp')&&$('walletHelp').firstElementChild;
if(hd)hd.textContent='📱 One tap to finish in your wallet app.';
log('You’re in Telegram — joining happens in your wallet app’s secure browser. Tap your wallet above; this page reopens there with your sponsor already set.','ok');
}
$('connectBtn').addEventListener('click',connect);
$('joinBtn').addEventListener('click',doJoin);
var fb=$('fundBtn'); if(fb)fb.addEventListener('click',openMoonpay);
+18
View File
@@ -7,6 +7,7 @@
try { inTg = sessionStorage.getItem('rmcTg') === '1'; } catch (e) {}
if (!inTg && (window.TelegramWebviewProxy !== undefined || /tgWebApp(Data|Platform)/.test(location.hash))) inTg = true;
if (!inTg) return;
window.__rmcInTg = 1; // synchronous flag for page scripts (join-now.js etc.)
try { sessionStorage.setItem('rmcTg', '1'); } catch (e) {}
function boot() {
@@ -23,6 +24,23 @@
bb.show();
bb.onClick(function () { history.back(); });
}
// External links must LEAVE the webview: wallet deep links (MetaMask/Trust)
// have to reach the wallet app, t.me links belong to Telegram itself, and
// explorers etc. open in the system browser. Same-origin links stay inside.
document.addEventListener('click', function (ev) {
var a = ev.target && ev.target.closest && ev.target.closest('a[href]');
if (!a) return;
var href = a.href || '';
if (!/^https?:/i.test(href)) return;
var ext = false;
try { ext = new URL(href).origin !== location.origin; } catch (e) {}
if (!ext) return;
ev.preventDefault();
try {
if (/^https:\/\/t\.me\//i.test(href) && tg.openTelegramLink) tg.openTelegramLink(href);
else tg.openLink(href);
} catch (e) { location.href = href; }
}, true);
}
if (window.Telegram && window.Telegram.WebApp) { boot(); return; }
var s = document.createElement('script');