diff --git a/server.js b/server.js
index 52dc320..28c80a2 100644
--- a/server.js
+++ b/server.js
@@ -653,6 +653,21 @@ async function walkUpActivatedSponsor(tok) {
}
return { id: 0, name: null, skipped, reason: 'none' };
}
+// The feed line for a walk-up: who had not switched on payouts, whose referral (and purchase, when the
+// activation and the buy share a tx) routed past them, and who it belongs to now.
+async function postWalkUp(buyerId, oldRef, toId, pur) {
+ const sc = siteConfig(); if (!sc.telegramBotToken || (!sc.telegramChatId && !sc.telegramEchoChatId)) return;
+ const lostAcct = await refAccount(oldRef); if (!lostAcct) return;
+ const lostName = lostAcct.username ? '@' + lostAcct.username : lostAcct.email.replace(/@.*/, '') + '@';
+ const nm = await accounts.namesForMembers([buyerId, toId]).catch(() => ({}));
+ const who = id => '#' + id + (nm[id] ? ' @' + nm[id] : '');
+ const site = 'https://instantadpay.com/my';
+ const what = pur ? who(buyerId) + "'s $" + Math.round(pur.priceCents / 100) + ' purchase routed past them' : who(buyerId) + ' switched on payouts and their position routed past them';
+ const missed = '\u{1F62C} Missed sale: ' + lostName + ' had not switched on payouts, so ' + what + '. That member, and every payout from them, now belongs to ' + who(toId) + '.'
+ + '\nTwo minutes on the Wallet tab prevents this: ' + site.replace(/^https:\/\//, '') + '';
+ if (sc.telegramChatId && String(sc.telegramEvents || 'payouts') !== 'none') await telegramSend(sc.telegramChatId, missed, sc.telegramTopicId).catch(() => {});
+ if (sc.telegramEchoChatId && String(sc.telegramEchoEvents || 'payouts') !== 'none') await telegramSend(sc.telegramEchoChatId, '\u{1F7E0} InstantAdPay \u00b7 ' + missed, sc.telegramEchoTopicId).catch(() => {});
+}
// The referral follows the money (Marty, 2026-09-16): when a position is created on-chain under a
// different sponsor than the account's stored referrer (walk-up past an unactivated sponsor, or the
// catch-position fallback), move the account to the sponsor that actually got paid, #1 included.
@@ -668,7 +683,10 @@ 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
+ movedByTx.set(ev.tx, { buyerId: Number(ev.id) || 0, toId: onchain, oldRef: old, at: Date.now(), posted: true }); // posted below, at activation; the Purchase branch stays quiet
+ // Post it now (Marty, 2026-09-21): the post used to wait for a Purchase in the same tx, so an activation that
+ // came first (switch on payouts, buy later) was never posted. The activation is the fact; it goes out here.
+ try { const pur = chain.recentEvents(1e9).find(x => x.tx === ev.tx && x.type === 'Purchase') || null; await postWalkUp(Number(ev.id) || 0, old, onchain, pur); } catch (e) { console.error('walk-up post', e.message); }
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
@@ -978,7 +996,7 @@ async function telegramOnEvent(ev) {
missed = missedNotice.composeMissed(txEvents, who2, had, site);
}
}
- if (ev.type === 'Purchase' && movedByTx.has(ev.tx)) {
+ if (ev.type === 'Purchase' && movedByTx.has(ev.tx) && !movedByTx.get(ev.tx).posted) { // legacy path; walk-ups now post at activation
const mv = movedByTx.get(ev.tx); movedByTx.delete(ev.tx);
const lostAcct = await refAccount(mv.oldRef);
if (lostAcct) {