Files
popa_minecraft_launcher_api/app/services/dailyreward.py

144 lines
6.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo
from app.db.database import users_collection, db
coins_sessions_collection = db.coins_sessions
TZ = ZoneInfo("Asia/Yekaterinburg")
def _day_bounds_utc(now_utc: datetime):
now_local = now_utc.astimezone(TZ)
start_local = now_local.replace(hour=0, minute=0, second=0, microsecond=0)
start_utc = start_local.astimezone(timezone.utc)
next_utc = (start_local + timedelta(days=1)).astimezone(timezone.utc)
return now_local.date(), start_utc, next_utc, start_local
class DailyRewardService:
async def claim_daily(self, username: str) -> dict:
now_utc = datetime.now(timezone.utc)
today_local, start_today_utc, start_tomorrow_utc, _ = _day_bounds_utc(now_utc)
user = await users_collection.find_one({"username": username})
if not user:
return {"claimed": False, "reason": "user_not_found"}
was_online_today = await coins_sessions_collection.find_one(
{
"player_name": username,
"update_type": "coins_update",
"timestamp": {
"$gte": start_today_utc.replace(tzinfo=None),
"$lt": start_tomorrow_utc.replace(tzinfo=None),
},
}
)
if not was_online_today:
return {
"claimed": False,
"reason": "not_online_today",
"message": "Вы должны зайти на сервер сегодня, чтобы получить ежедневную награду",
}
last_claim_at = user.get("daily_last_claim_at") # ожидаем datetime (лучше хранить UTC)
last_local_day = last_claim_at.replace(tzinfo=timezone.utc).astimezone(TZ).date() if last_claim_at else None
if last_local_day == today_local:
return {"claimed": False, "reason": "already_claimed_today", "streak": user.get("daily_streak", 0)}
yesterday_local = today_local - timedelta(days=1)
prev_streak = int(user.get("daily_streak", 0) or 0)
new_streak = (prev_streak + 1) if (last_local_day == yesterday_local) else 1
# твоя новая формула: 10, 20, 30 ... до 50 (кап)
reward = min(10 + (new_streak - 1) * 10, 50)
result = await users_collection.update_one(
{
"username": username,
"$or": [
{"daily_last_claim_at": {"$exists": False}},
{"daily_last_claim_at": {"$lt": start_today_utc.replace(tzinfo=None)}},
],
},
{
"$inc": {"coins": reward},
"$set": {"daily_last_claim_at": now_utc.replace(tzinfo=None), "daily_streak": new_streak},
},
)
if result.modified_count == 0:
user2 = await users_collection.find_one({"username": username})
return {"claimed": False, "reason": "already_claimed_today", "streak": user2.get("daily_streak", 0)}
await coins_sessions_collection.insert_one({
"player_name": username,
"update_type": "daily_login",
"timestamp": now_utc.replace(tzinfo=None),
"coins_added": reward,
"streak": new_streak,
})
return {"claimed": True, "coins_added": reward, "streak": new_streak}
async def get_status(self, username: str) -> dict:
now_utc = datetime.now(timezone.utc)
today_local, start_today_utc, start_tomorrow_utc, start_today_local = _day_bounds_utc(now_utc)
user = await users_collection.find_one({"username": username})
if not user:
return {"ok": False, "reason": "user_not_found"}
was_online_today = await coins_sessions_collection.find_one({
"player_name": username,
"update_type": "coins_update",
"timestamp": {
"$gte": start_today_utc.replace(tzinfo=None),
"$lt": start_tomorrow_utc.replace(tzinfo=None),
},
})
last_claim_at = user.get("daily_last_claim_at")
last_local_day = last_claim_at.replace(tzinfo=timezone.utc).astimezone(TZ).date() if last_claim_at else None
can_claim = (last_local_day != today_local) and bool(was_online_today)
seconds_to_next = 0 if can_claim else int((start_tomorrow_utc - now_utc).total_seconds())
if seconds_to_next < 0:
seconds_to_next = 0
return {
"ok": True,
"can_claim": can_claim,
"was_online_today": bool(was_online_today),
"seconds_to_next": seconds_to_next,
"next_claim_at_utc": start_tomorrow_utc.isoformat().replace("+00:00", "Z"),
"next_claim_at_local": (start_today_local + timedelta(days=1)).isoformat(),
"streak": int(user.get("daily_streak", 0) or 0),
}
async def get_claim_days(self, username: str, limit: int = 60) -> dict:
# Берём последние N записей daily_login и превращаем в список уникальных дней по ЕКБ
cursor = coins_sessions_collection.find(
{"player_name": username, "update_type": "daily_login"},
{"timestamp": 1, "_id": 0},
).sort("timestamp", -1).limit(limit)
days = []
seen = set()
async for doc in cursor:
ts = doc.get("timestamp")
if not ts:
continue
# У тебя timestamp в Mongo — naive UTC (now_utc.replace(tzinfo=None)) :contentReference[oaicite:1]{index=1}
ts_utc = ts.replace(tzinfo=timezone.utc)
day_local = ts_utc.astimezone(TZ).date().isoformat() # YYYY-MM-DD по ЕКБ
if day_local not in seen:
seen.add(day_local)
days.append(day_local)
days.reverse() # чтобы было по возрастанию (старые → новые), если надо
return {"ok": True, "days": days, "count": len(days)}