Leaderboard: company account excluded from rankings. Weekly member email: credits unspent, streak, contest rank, tank, line (sponsors by default; memberWeeklyEmail=1 for everyone)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -229,20 +229,53 @@ async function nudgeTick() {
|
||||
catch (e) { console.error('nudge', acct.email, e.message); }
|
||||
if (nudges >= 40) break; // spread the load across ticks
|
||||
}
|
||||
// weekly digest: every sponsor with at least one direct
|
||||
// weekly member email (Marty, 2026-09-14, modelled on the mailer.gold weekly): credits sitting unspent,
|
||||
// the streak, the contest standings with the member's own rank, the tank, and the line for sponsors
|
||||
let lbWeek = null; try { lbWeek = X.lb ? await X.lb().view('week') : null; } catch (e) {}
|
||||
let tankN = 0; try { tankN = X.tank ? (await X.tank.waiting()).length : 0; } catch (e) {}
|
||||
for (const acct of all) {
|
||||
if (!acct.email) continue;
|
||||
const lastD = await impl().lastDigest(acct.email);
|
||||
if (now - lastD < 7 * DAY) continue;
|
||||
if (now - (acct.lastSeen || acct.created || 0) > 45 * DAY) { await impl().setDigest(acct.email, now); continue; } // gone quiet for six weeks: leave them to the nudges
|
||||
const view = await coachView(acct.email);
|
||||
if (!view.directs.length) { await impl().setDigest(acct.email, now); continue; }
|
||||
const week = view.directs.filter(d => now - d.joined < 7 * DAY).length;
|
||||
const lines = view.directs.slice(0, 25).map(d => '- ' + d.name + ': ' + d.label + (d.stalled ? ' (quiet ' + d.quietDays + ' days)' : '') + '. Next: ' + d.next);
|
||||
const actions = view.directs.filter(d => d.stalled).slice(0, 3).map(d => '- Message ' + d.name + ': "' + d.say.replace('{{name}}', d.name.replace(/^@/, '')) + '"');
|
||||
const body = 'Your line this week:\n' + week + ' joined in the last 7 days. ' + view.stalled + ' of your ' + view.directs.length + ' directs have gone quiet.\n\n'
|
||||
+ lines.join('\n') + '\n\n' + (actions.length ? 'Three things to do today:\n' + actions.join('\n') + '\n\n' : '')
|
||||
+ 'Open My line to message anyone in one click: https://instantadpay.com/my#line\n\nInstantAdPay';
|
||||
try { await mailer.send(acct.email, 'Your line this week: ' + view.stalled + ' to nudge', body); digests += 1; } catch (e) { console.error('digest', acct.email, e.message); }
|
||||
// audience: sponsors with directs (as before) unless the admin switched the weekly on for every member
|
||||
const everyone = X.siteConfig && String(X.siteConfig().memberWeeklyEmail || '0') === '1';
|
||||
if (!everyone && !view.directs.length) { await impl().setDigest(acct.email, now); continue; }
|
||||
const name = acct.username ? '@' + acct.username : 'there';
|
||||
const parts = ['Hi ' + name + ', your week on InstantAdPay:'];
|
||||
// credits
|
||||
try {
|
||||
const ids = [acct.memberId, ...(await accounts.positions(acct.email)).map(p => p.memberId)].filter(Boolean);
|
||||
const bal = await X.ads.balances(ids, acct.email);
|
||||
if (bal.available > 0) parts.push('- You have ' + bal.available.toLocaleString() + ' ad credits sitting unspent. That is ' + bal.available.toLocaleString() + ' cents of delivery doing nothing. Campaigns > New campaign, point it at your invite link: https://instantadpay.com/my#campaigns');
|
||||
else if (bal.inCampaigns > 0) parts.push('- All ' + bal.inCampaigns.toLocaleString() + ' of your credits are working in live campaigns. Good.');
|
||||
} catch (e) {}
|
||||
// streak
|
||||
try {
|
||||
const st = await X.ads.viewStatus(acct.email);
|
||||
if (st.streakDay > 1) parts.push('- Your claim streak is on day ' + st.streakDay + '. Today\'s claim pays ' + (st.claimCredits || 0) + ' credits; miss a day and it restarts at 5.');
|
||||
else parts.push('- Five ads and a claim a day is the free way in: 5, 7, 10, then 25 credits every seventh day in a row: https://instantadpay.com/my#earn');
|
||||
} catch (e) {}
|
||||
// contest
|
||||
if (lbWeek && lbWeek.top) {
|
||||
const me = lbWeek.top.find(r => r.name === '@' + acct.username) || null;
|
||||
let mine = me; if (!mine) { try { mine = (await X.lb().view('week', acct.email)).me; } catch (e) {} }
|
||||
parts.push('- Referral contest this week (' + (lbWeek.prize || 'credits to the top 3') + '): ' + (lbWeek.top.length ? lbWeek.top.slice(0, 3).map(r => r.rank + '. ' + r.name + ' (' + r.sales + ' sold)').join(', ') : 'no sales yet, first sale takes the top spot') + '.'
|
||||
+ (mine ? ' You are #' + mine.rank + ' with ' + mine.sales + ' sold.' : ' You are not on the board yet; one $20 package bought by someone you sponsor puts you there.') + ' https://instantadpay.com/leaderboard');
|
||||
}
|
||||
// tank
|
||||
if (tankN > 0 && acct.memberId) parts.push('- ' + tankN + ' member' + (tankN === 1 ? ' is' : 's are') + ' waiting for a sponsor in the holding tank. Adopt one from My line: https://instantadpay.com/my#line');
|
||||
// line (sponsors only)
|
||||
if (view.directs.length) {
|
||||
const week = view.directs.filter(d => now - d.joined < 7 * DAY).length;
|
||||
parts.push('- Your line: ' + view.directs.length + ' direct' + (view.directs.length === 1 ? '' : 's') + ', ' + week + ' joined this week, ' + view.stalled + ' gone quiet.');
|
||||
const actions = view.directs.filter(d => d.stalled).slice(0, 3).map(d => ' Message ' + d.name + ': "' + d.say.replace('{{name}}', d.name.replace(/^@/, '')) + '"');
|
||||
if (actions.length) parts.push(actions.join('\n'));
|
||||
}
|
||||
parts.push('\nTwenty minutes, in order: the set, one message to your line, the tank, one conversation outward. https://instantadpay.com/blog/the-twenty-minute-day\n\nInstantAdPay · https://instantadpay.com/my\nNo income is guaranteed. Credits are advertising, not money.');
|
||||
const subject = lbWeek && lbWeek.top && lbWeek.top.length && lbWeek.top[0].name === '@' + acct.username ? 'You are #1 this week on InstantAdPay' : 'Your week on InstantAdPay: credits, streak, contest';
|
||||
try { await mailer.send(acct.email, subject, parts.join('\n')); digests += 1; } catch (e) { console.error('digest', acct.email, e.message); }
|
||||
await impl().setDigest(acct.email, now);
|
||||
if (digests >= 30) break;
|
||||
}
|
||||
@@ -250,7 +283,8 @@ async function nudgeTick() {
|
||||
return { nudges, digests };
|
||||
}
|
||||
|
||||
function init(opts) {
|
||||
let X = {}; // extra refs for the weekly member email (ads, tank, lb getter), 2026-09-14
|
||||
function init(opts) { X = arguments[0] || {};
|
||||
DATA_DIR = opts.dataDir; chain = opts.chain; accounts = opts.accounts; mailer = opts.mailer;
|
||||
J.load();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user