Proof feed: one message per purchase, and it says InstantAdPay

Marty, reading the feed on 25 September: "I see the level 1 payout, I see
a level 2 payout, but I don't see who purchased something and what they
bought." And in the main Crypto Team Build group the same lines carried no
product name at all, so nobody knew what they were from.

feedgroup.js composes one message from every event of a purchase
transaction: the buyer and the package on top, the level payouts under it
with their dollar equivalents, then who was passed over and why. It is
triggered on AdminPaid, the last event the contract emits for a purchase,
which is how the missed-payout notice already knows the whole transaction
is indexed. Tier payouts and purchases are no longer posted one line at a
time. In payouts mode a purchase that paid no member (root or unsponsored
buyer) stays silent, as before. Every post now opens with the InstantAdPay
header in the main group as well as the payments topic.

Composed against three real transactions from the live index before
shipping: three tiers, a double pass-up, and a root purchase. QA member
walk: 0 bugs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-25 07:02:59 -05:00
parent 742c271469
commit d2a3f162d0
2 changed files with 56 additions and 6 deletions
+17 -6
View File
@@ -37,7 +37,8 @@ const releases = require('./releases');
const ledger = require('./ledger');
const videosweep = require('./videosweep');
const sbcheck = require('./sbcheck');
const marks = require('./marks'); // one-time claims shared across running instances (deploy overlap) // Google Safe Browsing screen on advertiser destinations
const marks = require('./marks');
const feedgroup = require('./feedgroup'); // one proof-feed message per purchase: buyer, package, then the tier payouts // one-time claims shared across running instances (deploy overlap) // Google Safe Browsing screen on advertiser destinations
const updates = require('./updates');
const audit = require('./audit'); // counter audit: views vs delivery logs, charges vs shows (Marty, 2026-09-15) // member update emails from Admin > Releases (Marty, 2026-09-14)
const leaderboard = require('./leaderboard');
@@ -1072,11 +1073,19 @@ async function telegramOnEvent(ev) {
const who = id => '#' + id + (names[id] ? ' @' + names[id] : '');
const cc = chain.getConfig();
const tx = (cc.explorer ? cc.explorer.replace(/\/+$/, '') : 'https://polygonscan.com') + '/tx/' + ev.tx;
const build = (mode) => {
const build = async (mode) => {
let line = null;
if (ev.type === 'TierPaid') line = '\u{1F4B8} Level ' + ev.tier + ' payout: <b>' + weiToPol(ev.amountWei) + ' POL</b> \u2192 ' + who(ev.recipientId);
// Tier payouts and the purchase itself are no longer posted one by one: they are composed
// into ONE message on AdminPaid, the last event of every purchase tx, so the feed reads
// "who bought what, then who was paid" (Marty, 2026-09-25). In payouts mode a purchase that
// paid no member (root or unsponsored buyer) stays silent, as before.
if (ev.type === 'AdminPaid') {
const txEvents = chain.recentEvents(1e9).filter(x => x.tx === ev.tx);
const ids = new Set(); txEvents.forEach(x => { for (const k of ['buyerId', 'recipientId', 'skippedId']) if (x[k]) ids.add(x[k]); });
const nm = await accounts.namesForMembers([...ids]).catch(() => ({}));
line = feedgroup.compose(txEvents, id => '#' + id + (nm[id] ? ' @' + nm[id] : ''), weiToPol, { requirePayout: mode === 'payouts' });
}
else if (ev.type === 'BuyerCounted' && mode !== 'payouts') line = '\u2B50 ' + who(ev.sponsorId) + ' now has <b>' + ev.newCount + '</b> qualifying buyer' + (ev.newCount === 1 ? '' : 's') + (ev.newCount === 2 ? ' \u00b7 level 2 open' : ev.newCount === 5 ? ' \u00b7 level 3 open' : '');
else if (ev.type === 'Purchase' && mode !== 'payouts') line = '\u{1F9FE} ' + who(ev.buyerId) + ' bought a $' + Math.round(ev.priceCents / 100) + ' package';
else if (ev.type === 'AwardPaid') line = '\u{1F4B8} Award payout: <b>' + weiToPol(ev.amountWei) + ' POL</b> \u2192 ' + who(ev.toId);
else if (ev.type === 'MemberActivated' && mode === 'all') line = '\u{1F91D} ' + who(ev.id) + ' switched on payouts';
if (!line) return null;
@@ -1112,8 +1121,10 @@ async function telegramOnEvent(ev) {
}
}
} 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); }
// the main group used to get bare payout lines with no product name: nobody knew what they were from
const brand = t => '\u{1F7E0} <b>InstantAdPay</b>\n' + t;
if (sc.telegramChatId) { const t = await build(String(sc.telegramEvents || 'payouts')); if (t) await telegramSend(sc.telegramChatId, brand(t), sc.telegramTopicId); if (missed && String(sc.telegramEvents || 'payouts') !== 'none') await telegramSend(sc.telegramChatId, missed, sc.telegramTopicId).catch(() => {}); }
if (sc.telegramEchoChatId) { const t = await build(String(sc.telegramEchoEvents || 'payouts')); if (t) await telegramSend(sc.telegramEchoChatId, brand(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