Holding tank: no releasing an adoptee within 3 days of pickup, dropped adoptions count toward the two-adoption limit, releases posted to the feed and Telegram

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-13 13:50:28 -05:00
parent b9a70dba1a
commit 0c58b80184
5 changed files with 24 additions and 6 deletions
+1 -1
View File
@@ -78,7 +78,7 @@ FACTS:
- Live formats: display banners (per impression), text ads (per impression), full-screen LOGIN ADS (per day: right after a member signs in they land on a sponsor interstitial — they click "Open Ad", the advertiser's page opens in a NEW tab, a countdown runs on the interstitial, and at zero a "Go to dashboard" button appears. Just a CTA link is enough; an optional banner image can be the clickable creative. No framing requirement since it opens in its own tab), WATCH-TO-EARN VIDEO ADS (advertiser uploads an MP4/WebM or gives a direct https .mp4/.webm link and picks a required watch length — 10s/30s/60s — which sets the per-view price; viewers watch in an escape-proof player under Earn credits > Watch videos, the watch time is enforced on the server clock, and they earn credits per completed watch; you never see your own videos), and solo ads. Banner ads also require a size (standard IAB sizes like 728x90, 300x250). Coming: featured rotation with disclosed rotation size, verified-visit packs.
- WALLETS + BUYING POL (Training > Wallets and buying POL, /wallets, members only): preferred MetaMask (recommended; extra accounts for Qualified Start), Phantom (PHANTOM GOTCHA: Polygon is OFF by default; Settings > Active Networks > turn on Polygon, otherwise connect/buy fails or shows the wrong network; seen with Jim Watts' EB team 2026-09-13), SafePal, Coinbase Wallet; Trust works but blocks buys spending most of its POL (keep ~2x). MoonPay flow: connect wallet, Buy packages > "Buy POL with a card" opens MoonPay with POL on Polygon + the member's address prefilled; card/Apple Pay/Google Pay; first-time ID check; minimum order ~$30; buy package cost + 2-3 POL for fees; POL arrives in minutes; then buy. Exchanges: withdraw POL on the Polygon network. Never MATIC on Ethereum, never share the recovery phrase.
- INTRO VIDEO ON THE WALL: Profile > social links has an "Intro video" field (YouTube, Vimeo or direct .mp4 link). It embeds on the member's public wall page (/wall/<username>) right under their bio, above the three-level line and the join button.
- HOLDING TANK (Members > My line > Holding tank card): free members who joined with no sponsor wait there; a member who has switched on payouts AND bought their own $20+ package can Adopt one (first come, max 2 open adoptions, 7-day window; if the person never links a wallet or buys, they fall back into the tank; a person can be adopted twice at most). Adopting sets the sponsor, opens a chat and emails the member; their first purchase then binds to the adopter on-chain. Members can also "Release to tank" one of their own free referrals (pay it forward). Admin sees the tank under Members.
- HOLDING TANK (Members > My line > Holding tank card): free members who joined with no sponsor wait there; a member who has switched on payouts AND bought their own $20+ package can Adopt one (first come, max 2 open adoptions, 7-day window; if the person never links a wallet or buys, they fall back into the tank; a person can be adopted twice at most). Adopting sets the sponsor, opens a chat and emails the member; their first purchase then binds to the adopter on-chain. Members can also "Release to tank" one of their own free referrals (pay it forward), but NOT someone they adopted less than 3 days ago: an adoption is a commitment, and a dropped adoption still counts toward that person's two-adoption lifetime limit. Releases are posted to the feed and Telegram like pickups. Admin sees the tank under Members.
- DAILY CLAIM STREAK: finishing the daily ad set and claiming pays 5 credits on day 1, 7 on day 2, 10 from day 3, and 25 on every 7th consecutive day; miss a day and it restarts. After the set, verified visits (up to 20 a day, 1 credit each) keep earning, and the credits are meant to be spent on a campaign.
- 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.
- HOLDING TANK ALERTS: when new members land in the tank, a note at the top of every member's Overview names them (usernames) and a post goes to the team's Telegram payments topic; adopt from My line > Holding tank (your own $20 package required).
+1
View File
@@ -296,6 +296,7 @@
// site-wide activity (not about me): joins, tank, adoptions, purchases, payouts, qualifications
if (ev.type === 'Joined') { communityToast('👋 ' + ev.name + ' just joined' + (ev.tank ? ' and is waiting for a sponsor in the holding tank' : '')); liveRefresh(); return; }
if (ev.type === 'Adopted') { communityToast('🤝 ' + ev.sponsor + ' picked up ' + ev.member + ' from the holding tank'); liveRefresh(); return; }
if (ev.type === 'Released') { communityToast('🪣 ' + ev.sponsor + ' returned ' + ev.member + ' to the holding tank'); liveRefresh(); return; }
const mine = MYID && (ev.recipientId === MYID || ev.toId === MYID || ev.sponsorId === MYID || ev.skippedId === MYID || ev.buyerId === MYID);
if (!mine) {
if (ev.type === 'Purchase' && ev.buyerId) communityToast('🧾 ' + nm(ev.buyerId) + ' just bought a $' + Math.round((ev.priceCents || 0) / 100) + ' package');
+1 -1
View File
@@ -915,7 +915,7 @@
<script src="/assets/common.js?v=20260913a"></script>
<script src="/assets/wallet.js?v=20260911a"></script>
<script src="/assets/promo.js?v=20260911a"></script>
<script src="/assets/my.js?v=20260913e"></script>
<script src="/assets/my.js?v=20260913f"></script>
<script src="/assets/chat.js?v=20260907l"></script>
</body>
</html>
+9
View File
@@ -1279,6 +1279,15 @@ const server = http.createServer(async (req, res) => {
if (!s || !s.email) return json(res, 401, { error: 'Sign in first.' });
const b = await readBody(req);
const r = await tank.release(s.email, b.email);
if (r.ok) { // the feed shows releases too, so a pickup-and-drop is visible for what it is (Marty, 2026-09-13)
pushFeed({ type: 'Released', sponsor: r.ownerName, member: r.memberName, ts: Date.now() });
try {
const sc = siteConfig();
const clean = t => String(t || '').replace(/[<>&]/g, '');
if (sc.telegramBotToken && sc.telegramEchoChatId)
telegramSend(sc.telegramEchoChatId, '\u{1FAA3} <b>InstantAdPay</b> \u00b7 <b>' + clean(r.ownerName) + '</b> returned <b>' + clean(r.memberName) + '</b> to the holding tank', sc.telegramEchoTopicId);
} catch (e) {}
}
return json(res, r.error ? 400 : 200, r);
}
if (p === '/api/my/tank/contacted' && req.method === 'POST') { // sponsor reached the lead off-site: reset the rescue clock
+12 -4
View File
@@ -13,7 +13,8 @@ const db = require('./db');
const CAP_OPEN = 2; // open adoptions per adopter
const TTL_MS = 7 * 86400000; // an adoption's window to convert
const MAX_ADOPTIONS = 2; // per adoptee, lifetime
const MAX_ADOPTIONS = 2; // per adoptee, lifetime (dropped adoptions count too, since 2026-09-13)
const HOLD_MS = 3 * 24 * 3600 * 1000; // an adoption is a commitment: no releasing someone you picked up less than 3 days ago (Marty, 2026-09-13)
const MIN_OWN_BUY_CENTS = 2000; // adopter must have bought a $20+ package themselves
const WARN_DAYS = 10; // dormant lead: sponsor warned after this many days without contact
const RESCUE_DAYS = 14; // ...and the lead moves to the tank after this many (warning at least 4 days old)
@@ -131,13 +132,20 @@ async function release(ownerEmail, directEmail) {
const toks = [owner.code, owner.username, owner.memberId ? String(owner.memberId) : null].filter(Boolean).map(String);
if (!toks.includes(String(direct.sponsorRef || ''))) return { error: 'That member is not in your line.' };
if (direct.memberId) return { error: 'That member has already bought; on-chain sponsorship cannot move.' };
// picked up from the tank recently? then it is not theirs to drop yet (Bradley churned three in 80 seconds, 2026-09-13)
const openAds = (await impl().open(null)).filter(ad => ad.adoptee === d);
const young = openAds.find(ad => Date.now() - ad.ts < HOLD_MS);
if (young) {
const hrs = Math.ceil((HOLD_MS - (Date.now() - young.ts)) / 3600000);
return { error: 'You picked ' + nameOf(direct) + ' up from the tank ' + Math.max(1, Math.round((Date.now() - young.ts) / 3600000)) + ' hour(s) ago. An adoption is a commitment: message them, help them link a wallet, and if it goes nowhere you can release them in ' + hrs + ' hour(s).' };
}
const r = await accounts.setSponsorRef(d, '');
if (r.error) return r;
// an open adoption of this person closes as released (it no longer counts toward their two)
// an open adoption of this person closes as dropped, and a dropped adoption still counts toward their two
let closed = 0;
for (const ad of await impl().open(null)) if (ad.adoptee === d) { await impl().setStatus(ad.id, 'released'); closed++; }
for (const ad of openAds) { await impl().setStatus(ad.id, 'dropped'); closed++; }
if (!closed) await impl().add({ adoptee: d, adopter: o, ts: Date.now(), expires: Date.now(), status: 'released', note: 'released to the tank' });
return { ok: true };
return { ok: true, adopted: closed > 0, ownerName: nameOf(owner), memberName: nameOf(direct) };
}
// open adoptions past their window: converted ones close as done; the rest fall