"""The XP economy. Pure data + functions: easy to read, tune and test. Design goals * Consistency beats bursts (daily cap, active-days requirement for prizes). * Referrals only pay in full when the invited student becomes a REAL active user. * Nothing here can be farmed with a script: caps, idempotent events, delayed rewards. """ from typing import Optional DAILY_CAP = 25 # max XP/day from the automatic sources below CAPPED_KINDS = frozenset({"checkin", "download", "referral_join"}) PER_KIND_DAILY = {"download": 3, "referral_join": 5} # max events per day REFERRAL_JOIN_XP = 5 # invitee joined the channel + passed the gate REFERRAL_QUALIFIED_XP = 30 # invitee later proved active (see QUALIFY_* below) REFERRAL_QUALIFIED_PER_WEEK = 5 # paid referrals counted per week QUALIFY_MIN_ACTIVE_DAYS = 3 QUALIFY_WINDOW_DAYS = 14 VELOCITY_LIMIT_24H = 8 # more invitees than this in 24h from one inviter = suspicious MIN_TRUST = 70 STREAK_MILESTONES = {7: 10, 14: 25, 30: 60, 60: 120} LEVEL_THRESHOLDS = (0, 25, 75, 150, 275, 450, 700, 1050, 1550, 2300) # level n needs >= [n-1] MAX_LEVEL = len(LEVEL_THRESHOLDS) def checkin_xp(streak: int) -> int: """3 XP on day 1, +1 per extra streak day, up to 7.""" return 3 + min(max(streak - 1, 0), 4) def level_for(points: int) -> tuple[int, Optional[int]]: """-> (level, XP still needed for the next level, or None at max level).""" level = 1 for i, threshold in enumerate(LEVEL_THRESHOLDS, start=1): if points >= threshold: level = i if level >= MAX_LEVEL: return level, None return level, LEVEL_THRESHOLDS[level] - points def next_streak(last_active_iso: Optional[str], today_iso: str, current: int, yesterday_iso: str) -> int: if last_active_iso == today_iso: return current if last_active_iso == yesterday_iso: return current + 1 return 1