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
+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