From e699513636008bbc796aa5d2d85d52d510401396 Mon Sep 17 00:00:00 2001 From: martbost Date: Mon, 14 Sep 2026 10:15:44 -0500 Subject: [PATCH] My line: show who sponsored each person on every row, all three levels Co-Authored-By: Claude Fable 5.1 --- chatbot.js | 1 + public/assets/my.js | 2 +- public/assets/site.css | 1 + public/my.html | 4 ++-- server.js | 6 ++++++ 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/chatbot.js b/chatbot.js index 3d88e64..cf0414f 100644 --- a/chatbot.js +++ b/chatbot.js @@ -83,6 +83,7 @@ FACTS: - BLOG (public, instantadpay.com/blog): Marty's coaching and teaching articles on building a line, advertising that pays, and daily habits; each article has its own page and can be shared; RSS at /blog/feed.xml. Members who want to write their own articles: not offered today. - ACHIEVEMENT BADGES ON TELEGRAM (2026-09-13): when a member unlocks Spark/Surge/Circuit/Nexus, their personalised badge image (username on the ribbon) is posted automatically to the team's Telegram payments topic and the main group, once per badge; members cannot trigger posts themselves (the 'Post to Telegram' button is admin-only); 'Share' opens a picker (X, Facebook, Telegram, WhatsApp, LinkedIn, Massifly (copies the post and opens the feed composer), the phone's own share menu, copy link, save image) that shares the member's public badge page instantadpay.com/b//, which shows the badge and their join link. - PROMO TOOLKIT BY BADGE + AI COPY ENGINE (2026-09-14): Promo tools > AI Copy Engine shows the ladder: Free (links, posts, swipes, banners, wall, objections, shorts, badge pages), Spark (payouts on: one-tap campaign templates aimed at the member's link, printable handout with their QR at /handout/), Surge (first qualifying buyer; the AI Copy Engine is live: posts, DMs, follow-ups, objection replies, emails, story posts, team broadcasts in the member's name with their link, honesty rules built in; 20 free generations a month), Circuit (60 free; Video Maker renders every promo video and short with the member's own end card and QR, hosted for download; split tester compares join angles by views, joins, buyers), Nexus (150 free; Leader Ops: three-level team triage with stalled flags and one-click nudges, AI-drafted team broadcasts, credit grants from the leader's earned pool to anyone in their line, co-branded join page showing the sponsor's bio, and the member's own partner code: welcome credits funded from the leader's pool at each redemption, plus a partner kit page /partners?ref=&promo=CODE). After the free allowance each generation costs 10 ad credits from the earned pool. Credits are advertising, never money. +- MY LINE SPONSOR LINE (2026-09-14): every row on My line, all three levels, shows who sponsored that person ("sponsored by @name", or "you" for directs). - MY LINE ACTIVITY DROP-DOWN (2026-09-14): every row on My line, all three levels, has an Activity button that opens that person's activity: last seen, joined, stage and next step, ads viewed today and claim streak, campaigns active, link views in 30 days and joins, directs and qualifying buyers, wallet/payouts, badges, and a Working or Quiet verdict. Read-only, phone friendly. - GETTING STARTED CARD (2026-09-14): the top of every new member's Overview shows four steps (username, link wallet, switch on payouts, first package) with the current one explained and one button that opens the right tab and highlights the control (Wallet tab > Link wallet; then Activate payouts). Encouragement only: it can be hidden, and members who only want to view ads and earn credits are never blocked. It disappears once all four are done. - LEADERBOARD + REFERRAL CONTEST (2026-09-14): instantadpay.com/leaderboard ranks members by ad packages sold to people they directly sponsor (dollar value, read from the chain; a member's own linked positions and second accounts never count). Periods: this week (Monday to Sunday, Central time), this month, all time. Weekly and monthly winners are recorded automatically at rollover, announced in Telegram, and the credit prizes set by the admin (a ladder: 1st 1,000 / 2nd 500 / 3rd 250 weekly, 5,000 / 2,500 / 1,000 monthly by default) are granted automatically to those positions; the prize text is shown on the page and on the Overview's Leaderboard card, which also shows the member's own rank. Prizes are credits or packages, never cash. diff --git a/public/assets/my.js b/public/assets/my.js index 44d2772..3aa40f9 100644 --- a/public/assets/my.js +++ b/public/assets/my.js @@ -1403,7 +1403,7 @@ el.innerHTML = r.levels.map(L => !L.members.length ? '' : '
Level ' + L.level + ' ยท ' + L.members.length + (L.level === 1 ? ' direct' : '') + '
' + L.members.map(m => '
' + esc(m.name) + (m.own ? ' yours' : '') + '' - + (m.email ? '' + esc(m.email) + '' : '#' + m.memberId + '') + + '' + (m.email ? esc(m.email) : (m.memberId ? '#' + m.memberId + '' : '')) + (m.sponsor ? 'sponsored by ' + esc(m.sponsor) + '' : '') + '' + '' + (m.earnedWei && m.earnedWei !== '0' ? '+' + IAP.fmtPol(m.earnedWei) + ' POL' : '0.00 POL') + '' + '' + new Date(m.joined).toLocaleDateString() + '' diff --git a/public/assets/site.css b/public/assets/site.css index aebe671..7c50601 100644 --- a/public/assets/site.css +++ b/public/assets/site.css @@ -457,6 +457,7 @@ textarea{resize:vertical;font:inherit} .lin-row .nm{font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} .lin-row .em{font-size:12px;color:var(--mint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis} .lin-row .id{font-family:var(--mono);font-size:11px;color:var(--muted)} +.lin-row .sp{display:block;font-size:11.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis} .lin-row .em{min-width:0} .lin-row .dt{font-size:12px;color:var(--muted);text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap} .lin-row .chat-msg-btn{justify-self:end} .lin-row .lin-act-btn{justify-self:end;white-space:nowrap} diff --git a/public/my.html b/public/my.html index c1cbe7d..5964b66 100644 --- a/public/my.html +++ b/public/my.html @@ -5,7 +5,7 @@ Member area | InstantAdPay - + @@ -984,7 +984,7 @@ - + diff --git a/server.js b/server.js index 17368fd..5c82fb5 100644 --- a/server.js +++ b/server.js @@ -1656,8 +1656,14 @@ const server = http.createServer(async (req, res) => { if (ev.type === 'BuyerCounted' && myIds.has(ev.sponsorId) && ev.newBuyerId) qualified.add(ev.newBuyerId); } } + // who sponsored each person (Marty, 2026-09-14): resolve sponsorRef tokens (username, share code or member id) against the line itself + const meAcct = await accounts.byEmail(s.email); const byTok = {}; + const label = a => a.username ? '@' + a.username : (a.memberId ? 'member #' + a.memberId : 'member'); + for (const a of [meAcct, ...levels.flatMap(L => L.members)].filter(Boolean)) { const n = label(a); if (a.username) byTok[String(a.username).toLowerCase()] = n; if (a.code) byTok[String(a.code).toLowerCase()] = n; if (a.memberId) byTok[String(a.memberId)] = n; } + const sponsorOf = m => { const t = String(m.sponsorRef || '').toLowerCase(); if (!t) return null; if (meAcct && (t === String(meAcct.username || '').toLowerCase() || t === String(meAcct.code || '').toLowerCase() || t === String(meAcct.memberId || ''))) return 'you'; return byTok[t] || ('@' + t); }; const out = levels.map(L => ({ level: L.level, members: L.members.map(m => ({ memberId: m.memberId || 0, + sponsor: sponsorOf(m), name: m.username ? '@' + m.username : m.memberId ? 'member #' + m.memberId : 'member', ref: m.code || null, // opens the activity drop-down (Clinton's ask, 2026-09-14): any of the three levels email: L.level === 1 ? m.email : null, // directs only