Referral rewards: pay a hunter for bringing someone who actually hunts

PolHunter retains well and recruits badly. On 2026-09-22 it had its best day,
105 finds from 37 hunters, and had sent three people to InstantAdPay in its
whole life, all three on one member's link. Earning and sharing were separate
actions and only one of them paid.

Bringing someone is now the best-paid thing on the board, and the card sits
first in the mission list rather than on a promo page nobody opens. A flat 3
POL the first time someone they brought finds a code, then 20% on top of that
person's finds for their first 30 days, paid from the pool and never taken out
of the newcomer's drip.

Who counts as "brought by me" is the sponsor they actually joined
InstantAdPay under, handed over in the sign-in payload, so the bounty and the
commission always land on the same person. The bounty only pays for a member
who is genuinely new, once per person, and referral drips have their own daily
ceiling so a good referral day can never starve the finds.

Also: findsToday defaulted its day argument to undefined and silently returned
zero for every caller that omitted it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-23 04:34:22 -05:00
parent bed75d1b0f
commit 1c9e99f91e
7 changed files with 231 additions and 11 deletions
+6 -4
View File
@@ -32,10 +32,12 @@ function draw(min, max, skew) {
// prize drips (weekly prizes) are paid from the same wallet but never count against the daily pool
// a hunter's finds today (Central), prizes excluded
function findsToday(memberId, day) { return store.read('payouts', []).filter(p => p.memberId === Number(memberId) && p.day === day && p.status !== 'failed' && !p.prize).length; }
// day defaults to today: called without it the comparison was against undefined and silently
// returned 0 for everyone, which would read as "no finds yet" to any new caller
function findsToday(memberId, day) { const d = day || ctDay(); return store.read('payouts', []).filter(p => p.memberId === Number(memberId) && p.day === d && p.status !== 'failed' && !p.prize && !p.ref).length; }
function daily(memberId) { const s = settings(); const limit = Math.max(1, Number(s.missionsPerDay) || 3); const done = findsToday(memberId, ctDay()); return { limit, done, left: Math.max(0, limit - done) }; }
function paidToday(day) {
return store.read('payouts', []).filter(p => p.day === day && p.status !== 'failed' && !p.prize).reduce((n, p) => n + p.pol, 0);
return store.read('payouts', []).filter(p => p.day === day && p.status !== 'failed' && !p.prize && !p.ref).reduce((n, p) => n + p.pol, 0);
}
// has this member completed this mission TODAY (Central)? Missions reset at midnight Central with the codes and
@@ -43,12 +45,12 @@ function paidToday(day) {
// two accounts sharing a wallet get one drip per mission per day between them.
function completed(memberId, missionId, wallet) {
const w = wallet ? String(wallet).toLowerCase() : null; const day = ctDay();
return store.read('payouts', []).some(p => p.missionId === missionId && p.day === day && p.status !== 'failed' && !p.prize && (p.memberId === Number(memberId) || (w && p.wallet && String(p.wallet).toLowerCase() === w)));
return store.read('payouts', []).some(p => p.missionId === missionId && p.day === day && p.status !== 'failed' && !p.prize && !p.ref && (p.memberId === Number(memberId) || (w && p.wallet && String(p.wallet).toLowerCase() === w)));
}
// the mission ids this member (or wallet) has done today
function doneToday(memberId, wallet) {
const w = wallet ? String(wallet).toLowerCase() : null; const day = ctDay();
return new Set(store.read('payouts', []).filter(p => p.day === day && p.status !== 'failed' && !p.prize && (p.memberId === Number(memberId) || (w && p.wallet && String(p.wallet).toLowerCase() === w))).map(p => p.missionId));
return new Set(store.read('payouts', []).filter(p => p.day === day && p.status !== 'failed' && !p.prize && !p.ref && (p.memberId === Number(memberId) || (w && p.wallet && String(p.wallet).toLowerCase() === w))).map(p => p.missionId));
}
// record a completion; decide paid-today vs queued