Payments feeds: post who missed a share and who got it (on-chain pass-ups and off-chain walk-ups)
Marty, 2026-09-16: make missing a payout visible. On a TierPaid with hops>0 the PassedUp events of the same tx name the skipped positions, with the reason and how many qualifying buyers they had; the post says the amount and who received it instead. On a Purchase whose buyer was routed past an unactivated sponsor (movedByTx set by sponsorSyncOnEvent), the post names the sponsor who missed the whole sale and the upline who now owns that buyer for good. Both go to the main feed and the shared payments topic unless the events mode is 'none'. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -619,6 +619,8 @@ async function sponsorSyncOnEvent(ev) {
|
||||
const ref = spAcct && spAcct.code ? spAcct.code : String(onchain);
|
||||
const old = acct.sponsorRef || '';
|
||||
await accounts.setSponsorRef(acct.email, ref);
|
||||
movedByTx.set(ev.tx, { buyerId: Number(ev.id) || 0, toId: onchain, oldRef: old, at: Date.now() }); // read by telegramOnEvent on the Purchase in the same tx
|
||||
if (movedByTx.size > 200) { const k = movedByTx.keys().next().value; movedByTx.delete(k); }
|
||||
console.log('referral moved to where the pay landed:', acct.email, JSON.stringify(old), '->', ref, '(#' + onchain + ')');
|
||||
// Notices, now that it is a fact on the chain (Marty, 2026-09-16): everyone between the stored
|
||||
// referrer and the paid sponsor who was simply not activated is told they lost this referral for
|
||||
@@ -638,6 +640,7 @@ async function sponsorSyncOnEvent(ev) {
|
||||
if (spAcct && lost.length) sponsorGainNudge(spAcct, nameOf(acct), lost.map(nameOf)).catch(() => {});
|
||||
} catch (e) { console.error('sponsor move notices', e.message); }
|
||||
}
|
||||
const movedByTx = new Map(); // tx -> { buyerId, toId, oldRef } for purchases routed past an unactivated sponsor
|
||||
async function refAccount(ref) {
|
||||
const t = String(ref || '').trim().toLowerCase(); if (!t) return null;
|
||||
let a = await accounts.byCode(t); if (!a) a = await accounts.byUsername(t);
|
||||
@@ -883,8 +886,41 @@ async function telegramOnEvent(ev) {
|
||||
if (!line) return null;
|
||||
return line + ' \u00b7 <a href="' + tx + '">verify</a>' + (sc.telegramCtaUrl ? '\n<a href="' + sc.telegramCtaUrl + '">Join free</a>' : '');
|
||||
};
|
||||
if (sc.telegramChatId) { const t = build(String(sc.telegramEvents || 'payouts')); if (t) await telegramSend(sc.telegramChatId, t, sc.telegramTopicId); }
|
||||
// Missed-payout lines (Marty, 2026-09-16): when a share is passed over, say who missed it and who got it.
|
||||
// (a) on-chain pass-up: the TierPaid with hops>0 follows the PassedUp events of the same tx, already indexed.
|
||||
// (b) off-chain walk-up past an unactivated sponsor: recorded by sponsorSyncOnEvent, posted on the Purchase.
|
||||
let missed = null;
|
||||
try {
|
||||
const site = 'https://instantadpay.com/my';
|
||||
if (ev.type === 'TierPaid' && Number(ev.hops) > 0) {
|
||||
const all = chain.recentEvents(1e9);
|
||||
const sk = all.filter(x => x.type === 'PassedUp' && x.tx === ev.tx && x.tier === ev.tier);
|
||||
if (sk.length) {
|
||||
const nm2 = await accounts.namesForMembers(sk.map(x => x.skippedId)).catch(() => ({}));
|
||||
const need = ev.tier === 2 ? 2 : 5;
|
||||
const parts = sk.map(x => {
|
||||
const had = all.filter(y => y.type === 'BuyerCounted' && y.sponsorId === x.skippedId && y.block < ev.block).length;
|
||||
const why = x.reason === 'unqualified' ? 'not qualified for level ' + ev.tier + ' yet: ' + had + ' of ' + need + ' qualifying buyers' : x.reason === 'send-failed' ? 'the wallet refused the payment' : String(x.reason || '');
|
||||
return '#' + x.skippedId + (nm2[x.skippedId] ? ' @' + nm2[x.skippedId] : '') + ' (' + why + ')';
|
||||
});
|
||||
missed = '\u{1F62C} <b>Missed payout</b>: ' + parts.join(' and ') + ' passed over on ' + who(ev.buyerId) + "'s purchase. <b>" + weiToPol(ev.amountWei) + ' POL</b> went to ' + who(ev.recipientId) + ' instead.'
|
||||
+ '\nQualify before the next one: <a href="' + site + '">' + site.replace(/^https:\/\//, '') + '</a>';
|
||||
}
|
||||
}
|
||||
if (ev.type === 'Purchase' && movedByTx.has(ev.tx)) {
|
||||
const mv = movedByTx.get(ev.tx); movedByTx.delete(ev.tx);
|
||||
const lostAcct = await refAccount(mv.oldRef);
|
||||
if (lostAcct) {
|
||||
const lostName = lostAcct.username ? '@' + lostAcct.username : lostAcct.email.replace(/@.*/, '') + '@';
|
||||
const nm3 = await accounts.namesForMembers([mv.toId]).catch(() => ({}));
|
||||
missed = '\u{1F62C} <b>Missed sale</b>: ' + lostName + ' had not switched on payouts, so ' + who(ev.buyerId) + "'s $" + Math.round(ev.priceCents / 100) + ' purchase routed past them. That buyer, and every payout from them, now belongs to #' + mv.toId + (nm3[mv.toId] ? ' @' + nm3[mv.toId] : '') + ' for good.'
|
||||
+ '\nTwo minutes on the Wallet tab prevents this: <a href="' + site + '#wallet">' + site.replace(/^https:\/\//, '') + '</a>';
|
||||
}
|
||||
}
|
||||
} catch (e) { console.error('missed-payout post', e.message); }
|
||||
if (sc.telegramChatId) { const t = build(String(sc.telegramEvents || 'payouts')); if (t) await telegramSend(sc.telegramChatId, t, sc.telegramTopicId); if (missed && String(sc.telegramEvents || 'payouts') !== 'none') await telegramSend(sc.telegramChatId, missed, sc.telegramTopicId).catch(() => {}); }
|
||||
if (sc.telegramEchoChatId) { const t = build(String(sc.telegramEchoEvents || 'payouts')); if (t) await telegramSend(sc.telegramEchoChatId, '\u{1F7E0} <b>InstantAdPay</b> \u00b7 ' + t, sc.telegramEchoTopicId); }
|
||||
if (missed && sc.telegramEchoChatId && String(sc.telegramEchoEvents || 'payouts') !== 'none') await telegramSend(sc.telegramEchoChatId, '\u{1F7E0} <b>InstantAdPay</b> \u00b7 ' + missed, sc.telegramEchoTopicId).catch(() => {});
|
||||
}
|
||||
// holding-tank arrivals -> one digest line in the shared payments topic (Marty, 2026-09-12): who is
|
||||
// waiting for a sponsor, by username, so builders go adopt them. Runs every 15 min, posts only
|
||||
|
||||
Reference in New Issue
Block a user