diff --git a/lib/badge.js b/lib/badge.js index 08ac67f..2d228f6 100644 --- a/lib/badge.js +++ b/lib/badge.js @@ -9,7 +9,7 @@ const { spawn } = require('child_process'); let PUBLIC_DIR = null, CACHE_DIR = null, FONT; // per-badge ribbon position (fraction of height); tuned to the artwork -const ART = { first: 0.734, hunter: 0.753, tracker: 0.715, sweep: 0.689, lucky: 0.753, streak: 0.702, top: 0.725 }; +const ART = { first: 0.734, hunter: 0.753, tracker: 0.715, sweep: 0.689, lucky: 0.753, streak: 0.702, top: 0.725, recruiter: 0.834 }; function init(opts) { PUBLIC_DIR = opts.publicDir; CACHE_DIR = path.join(opts.dataDir, 'badges'); try { fs.mkdirSync(CACHE_DIR, { recursive: true }); } catch (e) {} } function artFile(id) { return path.join(PUBLIC_DIR, 'badges', 'badge-' + id + '.jpg'); } diff --git a/lib/social.js b/lib/social.js index 460d761..37af20b 100644 --- a/lib/social.js +++ b/lib/social.js @@ -19,6 +19,7 @@ const BADGES = [ { id: 'lucky', name: 'Lucky Find', icon: '\u{1F48E}', why: 'one drip of 0.4 POL or more' }, { id: 'streak', name: 'On a Streak', icon: '\u{1F525}', why: 'finds on three days in a row' }, { id: 'top', name: 'Top Hunter', icon: '\u{1F3C6}', why: 'won a weekly prize as #1' }, + { id: 'recruiter', name: 'Recruiter', icon: '\u{1F91D}', why: 'brought a hunter who found their first code' }, ]; const PRIZE_DEFAULTS = { weeklyPrizes: [3, 2, 1], weeklyMinFinds: 10 }; function prizeRules() { const s = rewards.settings(); return { pol: Array.isArray(s.weeklyPrizes) && s.weeklyPrizes.length ? s.weeklyPrizes.map(Number) : PRIZE_DEFAULTS.weeklyPrizes, minFinds: Number(s.weeklyMinFinds) > 0 ? Number(s.weeklyMinFinds) : PRIZE_DEFAULTS.weeklyMinFinds }; } @@ -49,6 +50,8 @@ function badgesFor(memberId, list) { const days = new Set(mine.map(p => p.day)); for (const d of days) { if (days.has(dayMinus(d, 1)) && days.has(dayMinus(d, 2))) { out.add('streak'); break; } } if (store.read('payouts', []).some(p => p.memberId === Number(memberId) && p.prize && p.prize.rank === 1 && p.status !== 'failed')) out.add('top'); + // earned by a bounty, not a match: someone they brought turned up and found their first code + if (store.read('payouts', []).some(p => p.memberId === Number(memberId) && p.ref && p.ref.kind === 'bounty' && p.status !== 'failed')) out.add('recruiter'); return BADGES.filter(b => out.has(b.id)); } diff --git a/public/badges/badge-recruiter.jpg b/public/badges/badge-recruiter.jpg new file mode 100644 index 0000000..506f445 Binary files /dev/null and b/public/badges/badge-recruiter.jpg differ