Milestones: celebrate every 100 members, not just the big marks

Marty, 2026-09-22. The ladder was 500 / 1000 / 2500 / 5000 / 10000, so most of
the year would pass with nothing to cheer. It is now every 100, with the fuller
write-up kept for the half-thousands and thousands and a short cheer in
between, so the small ones do not read as a repeated speech. Both are laid out
in short lines for a phone, like the daily snapshot. The hundreds already
passed are recorded as done on the live volume, so the next post is 600.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-22 10:15:02 -05:00
parent 5c11bfa502
commit f255d096ad
+32 -11
View File
@@ -112,16 +112,37 @@ async function tick() {
async function preview() { const sc = X.siteConfig(); return compose(await gather(), sc.telegramCtaUrl); }
// ---- membership milestones (Marty, 2026-09-19): one celebratory post per mark, the moment it is crossed
const MILESTONES = [500, 1000, 2500, 5000, 10000];
// ---- membership milestones: one celebratory post per mark, the moment it is crossed.
// Every 100 members since 2026-09-22 (Marty), with the fuller write-up saved for the round
// thousands and the half-thousands so the small ones stay a quick cheer, not a repeated speech.
const STEP = 100;
const isBig = m => m % 500 === 0;
const marksUpTo = count => { const out = []; for (let m = STEP; m <= count; m += STEP) out.push(m); return out; };
const MILESTONES = marksUpTo(10000); // kept for callers that want the ladder
function composeMilestone(mark, s, cta) {
const L = [];
L.push('\u{1F389} <b>' + n(mark) + ' members on InstantAdPay.</b>');
L.push(n(mark) + ' people have joined, and here is what that looks like right now: <b>' + n(s.wallets) + '</b> wallets linked, <b>' + n(s.payoutsOn) + '</b> with payouts switched on, <b>' + n(s.lifeBuys) + '</b> purchases, and <b>' + pol(s.lifePaidWei) + ' POL</b> paid to members in the same transactions the purchases came from. Every one of those payments is on the public ledger.');
L.push('Thank you to every member who brought someone in. That is the whole engine, and it is working.');
L.push('The next ' + n(mark) + ' start today.');
L.push('<a href="https://instantadpay.com/ledger">Live ledger</a>' + (cta ? ' \u00b7 <a href="' + cta + '">Join free</a>' : ''));
return L.join('\n\n');
const ledger = '<a href="https://instantadpay.com/ledger">Live ledger</a>' + (cta ? ' \u00b7 <a href="' + cta + '">Join free</a>' : '');
const B = [];
B.push('\u{1F389} <b>' + n(mark) + ' members on InstantAdPay</b>');
if (isBig(mark)) {
B.push(n(mark) + ' people have joined. Here is what that looks like right now:');
B.push('<b>' + n(s.wallets) + '</b> wallets linked\n'
+ '<b>' + n(s.payoutsOn) + '</b> with payouts switched on\n'
+ '<b>' + n(s.lifeBuys) + '</b> purchases\n'
+ '<b>' + pol(s.lifePaidWei) + ' POL</b> paid to members\nin the same transactions');
B.push('Every one of those payments is on the public ledger.');
B.push('Thank you to every member who brought someone in. That is the whole engine, and it is working.');
B.push('The next ' + n(mark) + ' start today.');
} else {
B.push('Another hundred through the door.');
B.push('<b>' + n(s.wallets) + '</b> wallets linked\n'
+ '<b>' + n(s.payoutsOn) + '</b> with payouts on\n'
+ '<b>' + pol(s.lifePaidWei) + ' POL</b> paid to members');
B.push('Every payment on the public ledger.');
B.push('Next stop: <b>' + n(mark + STEP) + '</b>.');
}
B.push(ledger);
return B.join('\n\n');
}
async function memberCount() {
if (!(X.db && X.db.enabled())) return 0;
@@ -132,7 +153,7 @@ async function milestoneTick() {
const sc = X.siteConfig(); if (!sc.telegramBotToken) return null;
const st = state(); const done = st.milestones || {};
const count = await memberCount();
const due = MILESTONES.filter(m => count >= m && !done[m]);
const due = marksUpTo(count).filter(m => !done[m]);
if (!due.length) return null;
const mark = due[due.length - 1];
const text = composeMilestone(mark, await gather(), sc.telegramCtaUrl);
@@ -147,4 +168,4 @@ async function milestoneTick() {
}
async function previewMilestone(mark) { const sc = X.siteConfig(); return composeMilestone(Number(mark) || 500, await gather(), sc.telegramCtaUrl); }
module.exports = { init, gather, compose, post, tick, preview, milestoneTick, previewMilestone, memberCount, MILESTONES };
module.exports = { init, gather, compose, post, tick, preview, milestoneTick, previewMilestone, composeMilestone, memberCount, MILESTONES, STEP };