1
0
forked from sass/tipibot

Change exp leveling system

This commit is contained in:
Rene Arumetsa
2026-05-13 22:43:34 +03:00
parent 15e3121d55
commit 8d7ac504ca
3 changed files with 45 additions and 16 deletions

View File

@@ -9,7 +9,7 @@ from discord import app_commands
from core import sheets from core import sheets
import strings as S import strings as S
from core.member_sync import announce_birthday, sync_member from core.member_sync import announce_birthday, sync_member, today_local
class BirthdayPages(discord.ui.View): class BirthdayPages(discord.ui.View):
@@ -44,7 +44,7 @@ def _build_birthday_pages(
Returns (pages, start_index) where start_index is the current month. Returns (pages, start_index) where start_index is the current month.
""" """
rows = sheets.get_cache() rows = sheets.get_cache()
today = datetime.date.today() today = today_local()
by_month: dict[int, list[tuple[int, str, int | None]]] = {m: [] for m in range(1, 13)} by_month: dict[int, list[tuple[int, str, int | None]]] = {m: [] for m in range(1, 13)}
@@ -298,8 +298,8 @@ def register_dev_member_commands(
for fmt in ["%d/%m/%Y", "%Y-%m-%d"]: for fmt in ["%d/%m/%Y", "%Y-%m-%d"]:
try: try:
bday = datetime.datetime.strptime(bday_str, fmt).date() bday = datetime.datetime.strptime(bday_str, fmt).date()
if 1920 <= bday.year <= datetime.date.today().year: if 1920 <= bday.year <= today_local().year:
today = datetime.date.today() today = today_local()
age = today.year - bday.year - ((today.month, today.day) < (bday.month, bday.day)) age = today.year - bday.year - ((today.month, today.day) < (bday.month, bday.day))
embed.add_field(name=S.MEMBER_UI["age_field"], value=S.MEMBER_UI["age_val"].format(age=age), inline=True) embed.add_field(name=S.MEMBER_UI["age_field"], value=S.MEMBER_UI["age_val"].format(age=age), inline=True)
break break

View File

@@ -317,16 +317,18 @@ LEVEL_ROLES: list[tuple[int, str]] = [
def get_level(exp: int) -> int: def get_level(exp: int) -> int:
"""Level = max(1, floor(sqrt(exp/6))). """Level = max(1, floor(sqrt(exp/10))).
Level 5 @ 150 EXP, 10 @ 600, 20 @ 2400, 30 @ 5400.""" Level 5 @ 250 EXP, 10 @ 1000, 20 @ 4000, 30 @ 9000."""
return max(1, int(math.sqrt(max(0, exp) / 6))) return max(1, int(math.sqrt(max(0, exp) / 10)))
def exp_for_level(level: int) -> int: def exp_for_level(level: int) -> int:
"""Minimum cumulative EXP to reach this level. level^2 * 6.""" """Minimum cumulative EXP to reach this level.
Recurrence: exp_for_level(L) = L*20 - 10 + exp_for_level(L-1), base 0.
Closed form: 10*level^2."""
if level <= 1: if level <= 1:
return 0 return 0
return level * level * 6 return 10 * level * level
def level_role_name(level: int) -> str: def level_role_name(level: int) -> str:

View File

@@ -2,9 +2,11 @@
from __future__ import annotations from __future__ import annotations
import calendar
import logging import logging
from datetime import datetime, date from datetime import datetime, date
from dataclasses import dataclass, field from dataclasses import dataclass, field
from zoneinfo import ZoneInfo
import discord import discord
@@ -13,6 +15,20 @@ from . import sheets
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
_PLACEHOLDER = {"-", "x", "n/a", "none", "ei"} _PLACEHOLDER = {"-", "x", "n/a", "none", "ei"}
_TZ = ZoneInfo("Europe/Tallinn")
def today_local() -> date:
"""Today's date in Europe/Tallinn — the bot's operational timezone, independent of host TZ."""
return datetime.now(_TZ).date()
def _shift_year_safe(d: date, year: int) -> date:
"""Move `d` to `year`; Feb 29 falls back to Feb 28 when the target year is non-leap."""
try:
return d.replace(year=year)
except ValueError:
return d.replace(year=year, day=28)
def _is_placeholder(val: str) -> bool: def _is_placeholder(val: str) -> bool:
@@ -72,7 +88,7 @@ def _parse_birthday(raw: str) -> date | None:
raw = str(raw).strip() raw = str(raw).strip()
if _is_placeholder(raw): if _is_placeholder(raw):
return None return None
today = date.today() today = today_local()
for fmt, has_year in [("%d/%m/%Y", True), ("%Y-%m-%d", True), ("%m-%d", False)]: for fmt, has_year in [("%d/%m/%Y", True), ("%Y-%m-%d", True), ("%m-%d", False)]:
try: try:
parsed = datetime.strptime(raw, fmt).date() parsed = datetime.strptime(raw, fmt).date()
@@ -90,21 +106,32 @@ def _is_birthday_soon(birthday_str: str, window_days: int | None = None) -> bool
if bday is None: if bday is None:
return False return False
window = window_days or config.BIRTHDAY_WINDOW_DAYS window = window_days or config.BIRTHDAY_WINDOW_DAYS
today = date.today() today = today_local()
this_year_bday = bday.replace(year=today.year) this_year_bday = _shift_year_safe(bday, today.year)
if this_year_bday < today: if this_year_bday < today:
this_year_bday = bday.replace(year=today.year + 1) this_year_bday = _shift_year_safe(bday, today.year + 1)
delta = (this_year_bday - today).days delta = (this_year_bday - today).days
return 0 <= delta <= window return 0 <= delta <= window
def is_birthday_today(birthday_str: str) -> bool: def is_birthday_today(birthday_str: str) -> bool:
"""Return True if today is the member's birthday (any supported date format).""" """Return True if today is the member's birthday (any supported date format).
Feb 29 babies are observed on Feb 28 in non-leap years.
"""
bday = _parse_birthday(birthday_str) bday = _parse_birthday(birthday_str)
if bday is None: if bday is None:
return False return False
today = date.today() today = today_local()
return bday.month == today.month and bday.day == today.day if bday.month == today.month and bday.day == today.day:
return True
if (
bday.month == 2 and bday.day == 29
and today.month == 2 and today.day == 28
and not calendar.isleap(today.year)
):
return True
return False
async def sync_member( async def sync_member(