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:
@@ -67,4 +67,4 @@
|
||||
<p class="micro" style="text-align:center">No income is guaranteed. Cryptocurrency and smart-contract participation carry risk. Only use funds you can afford to lose.</p>
|
||||
</main>
|
||||
<footer class="wrap disclaimer">All figures are read live from the RM Circle smart contract on Polygon. Not an earnings guarantee or investment advice. Participation carries risk of loss.<div class="footer-links"><a href="/">Home</a><a href="/training">Training</a><a href="/how-pay-works">How You Get Paid</a><a href="/contract">Contract Security</a><a href="/disclaimer">Disclaimers</a></div></footer>
|
||||
<script src="/track.js"></script><script src="/rmc-wallet.js"></script><script src="/join-now.js"></script><script src="/translate.js" defer></script></body></html>
|
||||
<script src="/track.js"></script><script src="/tg-app.js"></script><script src="/rmc-wallet.js"></script><script src="/join-now.js"></script><script src="/translate.js" defer></script></body></html>
|
||||
|
||||
+2
-1
@@ -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
@@ -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
@@ -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'],
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -50,7 +50,7 @@ FACTS:
|
||||
- Current team sponsor: ${a ? `ID ${a.id}${c.showSponsorName && a.name ? ` (${a.name})` : ''}, ${a.directs}/2 directs` : 'shown on the start page'}. ${waiting} placement(s) waiting. Placements rotate as positions qualify — always verify on https://rmcircle.team/start right before joining.
|
||||
- Site pages: https://rmcircle.team/ (strategy overview + roadmap + live team stats), https://rmcircle.team/start (current sponsor + join steps), https://rmcircle.team/training (THE CIRCLE METHOD — the team's free 10-lesson course in 3 modules. M1 Get Your Two: L1 mindset, L2 warm list, L3 the conversation, L4 objections. M2 Help Your Two: L5 dashboard-as-coaching-desk, L6 first 48 hours, L7 stalled people & pass-ups, L8 timing upgrades to catches. M3 Teach the Teachers: L9 run the same play, L10 the 20-minute weekly rhythm. ROUTING RULE — answer with the lesson: how do I find people→L2 (/training#lesson-2); what do I say→L3; pyramid objection→L4; new member just joined→L6; someone stalled→L7; should I upgrade→L8; overwhelmed→L10. Deep links: /training#lesson-N — plus 7 how-to videos — team overview, wallet setup, funding, the new connect-wallet join flow on the site, the dApp backup method, how payments work, and a full 14-min Member Dashboard walkthrough — + spillover article), https://rmcircle.team/how-pay-works (the two income streams shown as a pay-flow diagram + Premium/Standard tier comparison), https://rmcircle.team/contract (plain-language security review of the verified smart contract — code can't change, no pooled funds, locked rules, honest list of operator powers), https://rmcircle.team/weekly-rhythm (printable 20-minute Weekly Rhythm routine + 4-week habit tracker from Method Lesson 10, personalized like the Fast Start sheet), https://rmcircle.team/fast-start (printable 48-Hour Fast Start checklist — personalized with the member's invite link and a scannable QR code when opened from their dashboard; prints clean black-on-white, and prints in whatever language the member selected with the 🌐 button), https://rmcircle.team/my (member dashboard — its "Your team" panel opens with an organization bar: total members in your org, generations deep, qualified count below you, POL earned below you, and its approximate USD value at an hourly-cached POL price; the matrix under it drills leg by leg), https://rmcircle.team/tools (for existing team members who want to promote — share-ready promo videos (including the “Pocket Change” curiosity hook video — 25 ways people flush pocket change weekly with nothing to show for it, then the side-hustle flip; it deliberately shows no URL so the poster's invite link in the caption/description carries the credit, and matching pocket-change post copy sits in the Social posts section), copy-paste social posts, short/long email swipes, a downloadable banner kit in every standard size, and an Official RM Circle Media library (13 vertical social videos + 15 graphics from the creators — pair them with your own invite link in the caption; each curiosity video also has a MATCHED invite link (adds ?v=<hook> to the member's /join link) that makes the landing page continue that video's hook — recommend it when members ask which link to use with a video); open it from the gold Promo Tools button on your dashboard and every post/swipe arrives pre-personalized with YOUR invite link; to write promos in their own voice, mybrandedvoice.com), https://rmcircle.team/disclaimer (affiliate/earnings/risk disclosures).
|
||||
- UPGRADING FROM THE DASHBOARD: a qualified member can upgrade their level directly on their dashboard (rmcircle.team/my/<id>) — an "Upgrade" card appears with the exact next-level cost read live from the contract; they connect the wallet that OWNS the position, confirm one transaction, done. The site never touches the funds (wallet pays the contract directly). If the wallet doesn't cover the cost, the card offers the MoonPay card-buy option. On phones, open the page inside the wallet app's browser.
|
||||
- TELEGRAM COMPANION BOT: members can link their position (dashboard → Messages → "Connect Telegram", wallet-verified) to get instant payout DMs, native Telegram delivery of team messages (reply in Telegram to answer — matrix-line rules still apply), joined-on-your-link pings, and their invite/angle links via the "links" command. This finally lets members reach their downline as real people instead of just IDs — while handles stay private (the bot relays). Linked members can also tap the bot's ☰ menu button to open the MINI APP — the full live dashboard, promo tools, and Circle Method training right inside Telegram with zero login (Telegram itself proves who they are). The website stays fully available too; the Mini App is a convenience door, not a replacement.
|
||||
- TELEGRAM COMPANION BOT: members can link their position (dashboard → Messages → "Connect Telegram", wallet-verified) to get instant payout DMs, native Telegram delivery of team messages (reply in Telegram to answer — matrix-line rules still apply), joined-on-your-link pings, and their invite/angle links via the "links" command. This finally lets members reach their downline as real people instead of just IDs — while handles stay private (the bot relays). Linked members can also tap the bot's ☰ menu button to open the MINI APP — the full live dashboard, promo tools, and Circle Method training right inside Telegram with zero login (Telegram itself proves who they are). Prospects can JOIN from the Mini App too: a member's Telegram-native invite link (the "links" command in the bot shows it) opens the sponsor's invite page right inside Telegram, and the join itself finishes in the prospect's own wallet app's secure browser — same zero-custody flow as the website. The website stays fully available too; the Mini App is a convenience door, not a replacement.
|
||||
- MESSAGES (on-site, wallet-verified): every member dashboard has a Messages panel — sign in once with the wallet that owns your position (a free signature, cannot move funds), then message your upline or anyone in your own team, or broadcast to your whole team. Spam-proof by design: messaging only works along your own matrix lines, so strangers can't message you. Unread messages show as a bell on your dashboard. Members are told the team admin can review messages for abuse. No email address needed.
|
||||
- BUYING POL WITH A CARD (for people brand new to crypto): the site links to MoonPay (moonpay.com/buy/pol) on the training page, the start page, and automatically on the join page when a connected wallet's balance is short. Guidance to give: choose POL on the POLYGON network, send it to YOUR OWN wallet address, buy about entry + gas (~385 POL). When explaining gas, use the car analogy: just like a car needs fuel to get anywhere, every blockchain transaction burns a tiny bit of POL to move — keep a little in the tank beyond the entry, because a wallet with an empty tank cannot make the trip. MoonPay is an independent company (merchant of record) — it handles ID verification and charges its own card fee (~4.5%); this site never touches or holds anyone's money. First purchases can take a few minutes to arrive.
|
||||
- LANGUAGE: always reply in the language the member writes in — translate program terms naturally and keep level names (Scintilla, Ascensus, ...) as-is. Site pages have a floating 🌐 Translate button (bottom-left) that machine-translates any page and remembers the choice.
|
||||
@@ -788,7 +788,7 @@ async function handleApi(req,res,pathname){
|
||||
const maxOrder=sponsors.reduce((m,s)=>Math.max(m,s.sortOrder||0),0);sponsors.push({id:String(id).trim(),name:String(name).trim(),parentId:String(parentId||'').trim(),directs:0,level,status:sponsors.some(s=>s.status==='active')?'waiting':'active',sortOrder:maxOrder+10,clicks:0,notes:String(notes||'').trim(),email:String(email||'').trim().slice(0,120)});sponsors=normalizeStatuses(sponsors);saveSponsors(sponsors);return json(res,201,{sponsors});
|
||||
}
|
||||
if(req.method==='PATCH'&&pathname==='/api/admin/config'){
|
||||
const b=await bodyJson(req),cur=getConfig(),next={...cur};for(const k of ['siteName','programName','bridgeHeadline','bridgeSubheadline','premiumEntryPol','dappReferralBaseUrl','telegramUrl','supportLabel','showSponsorName','showQueueProgress','bemobPostbackUrl','telegramBotToken','companionBotToken','telegramChatId','telegramTopicId','telegramRecruitTopicId','teamRootId','emailFrom','teamAlertEmail','ownerIds','ownerAlertEmail','orgRootId','tweetEnabled','tweetCtaUrl','tweetHashtags','blotatoTwitterId','dappFallbackPublic','moonpayPublicKey','moonpaySecretKey','publicRotationMode','publicRotationRootId','rotationExcludeIds'])if(Object.prototype.hasOwnProperty.call(b,k))next[k]=b[k];next.premiumEntryPol=Number(next.premiumEntryPol)||362;next.updatedAt=new Date().toISOString();writeJson(CONFIG_FILE,next);return json(res,200,{config:next});
|
||||
const b=await bodyJson(req),cur=getConfig(),next={...cur};for(const k of ['siteName','programName','bridgeHeadline','bridgeSubheadline','premiumEntryPol','dappReferralBaseUrl','telegramUrl','supportLabel','showSponsorName','showQueueProgress','bemobPostbackUrl','telegramBotToken','companionBotToken','miniAppShortName','telegramChatId','telegramTopicId','telegramRecruitTopicId','teamRootId','emailFrom','teamAlertEmail','ownerIds','ownerAlertEmail','orgRootId','tweetEnabled','tweetCtaUrl','tweetHashtags','blotatoTwitterId','dappFallbackPublic','moonpayPublicKey','moonpaySecretKey','publicRotationMode','publicRotationRootId','rotationExcludeIds'])if(Object.prototype.hasOwnProperty.call(b,k))next[k]=b[k];next.premiumEntryPol=Number(next.premiumEntryPol)||362;next.updatedAt=new Date().toISOString();writeJson(CONFIG_FILE,next);return json(res,200,{config:next});
|
||||
}
|
||||
const m=pathname.match(/^\/api\/admin\/sponsors\/([^/]+)(?:\/(increment|activate|qualify|reset|move))?$/);
|
||||
if(m){const id=decodeURIComponent(m[1]),action=m[2]||null;let sponsors=getSponsors(),idx=sponsors.findIndex(s=>s.id===id);if(idx<0)return json(res,404,{error:'Sponsor not found.'});
|
||||
|
||||
@@ -191,7 +191,8 @@ async function digestFor(memberId) {
|
||||
lines.push(`📊 READ: ${st.downCount != null ? st.downCount : '—'} in your org${delta != null ? ` (${delta >= 0 ? '+' + delta : delta} this week)` : ''} · ${Math.round(st.downPol || 0).toLocaleString()} POL earned below you`);
|
||||
lines.push(mover ? `🔥 LIFT ONE: #${mover.id} — ${mover.ascensusCost ? 'their next step is already covered by catches; one message converts it' : 'money is forming below them; a heads-up now beats a pass-up later'}. Try: msg ${mover.id} <your note>` : `🔥 LIFT ONE: nobody flagged this week — congratulate your newest member instead.`);
|
||||
lines.push(quiet ? `🕯️ CHECK ONE: #${quiet.id} — one kind line, no pressure: msg ${quiet.id} Thinking of you — no rush on anything, here if you need me.` : `🕯️ CHECK ONE: your quiet list is empty. Enjoy that.`);
|
||||
lines.push(`📤 SHARE ONE: this week's angle is "${angle}" — your matched link:\nhttps://rmcircle.team/join/${memberId}?v=${angle}`);
|
||||
const maShare = miniAppInvite(memberId, angle);
|
||||
lines.push(`📤 SHARE ONE: this week's angle is "${angle}" — your matched link:\nhttps://rmcircle.team/join/${memberId}?v=${angle}${maShare ? `\nSharing inside Telegram? Use this instead — it opens right here:\n${maShare}` : ''}`);
|
||||
lines.push(`\nThen close the tab. 20 every week beats 2 hours twice. (Lesson 10: ${lessonLink(10)})\nrhythm off to stop these · rhythm now to rerun anytime`);
|
||||
return lines.join('\n');
|
||||
}
|
||||
@@ -216,10 +217,20 @@ async function runDigests() {
|
||||
}
|
||||
|
||||
// --- member commands --------------------------------------------------------
|
||||
// t.me/<bot>/<shortname>?startapp=<id>[_<angle>] — the Telegram-native invite;
|
||||
// app.js decodes start_param and routes prospects to the sponsor's squeeze
|
||||
// page. Requires config.miniAppShortName (set after BotFather /newapp).
|
||||
function miniAppInvite(id, angle) {
|
||||
const d = load(), short = String(getConfig().miniAppShortName || '').trim();
|
||||
if (!d.u || !short) return null;
|
||||
return `https://t.me/${d.u}/${short}?startapp=${id}${angle ? '_' + angle : ''}`;
|
||||
}
|
||||
function linksText(id) {
|
||||
const b = 'https://rmcircle.team';
|
||||
const ma = miniAppInvite(id);
|
||||
return `🔗 Your links, #${id} (each one credits YOU):\n\n` +
|
||||
`Moving invite link (routes to whoever needs help next in your leg):\n${b}/join/${id}\n\n` +
|
||||
(ma ? `⚡ Telegram-native invite — opens instantly INSIDE Telegram (best for sharing in groups and DMs here):\n${ma}\n\n` : '') +
|
||||
`Angle-matched links — pick the hook that fits your person:\n` +
|
||||
`💸 Pocket Change: ${b}/join/${id}?v=pocket\n` +
|
||||
`📱 Your Phone: ${b}/join/${id}?v=phone\n` +
|
||||
|
||||
Reference in New Issue
Block a user