Files
popa_minecraft_launcher_api/app/services/coins.py
2025-12-20 12:23:35 +05:00

158 lines
6.1 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
import re
from app.db.database import users_collection
from fastapi import HTTPException
from app.db.database import db
MAX_MINUTES_PER_UPDATE = 120
coins_sessions_collection = db.coins_sessions
class CoinsService:
_AFK_PREFIX_RE = re.compile(r"^\s*\[\s*AFK\s*\]", re.IGNORECASE)
@classmethod
def _is_afk_name(cls, player_name: str) -> bool:
if not player_name:
return False
return bool(cls._AFK_PREFIX_RE.match(player_name))
async def update_player_coins(self, player_id: str, player_name: str, online_time: int, server_ip: str):
"""Обновляет монеты игрока на основе времени онлайн"""
user = await self._find_user_by_uuid(player_id)
if not user:
return
last_update = await coins_sessions_collection.find_one({
"player_id": player_id,
"server_ip": server_ip,
"update_type": "coins_update"
}, sort=[("timestamp", -1)])
now = datetime.utcnow()
# AFK: монеты не начисляем, но обязательно фиксируем тик,
# чтобы AFK-время не накопилось и не начислилось потом.
if self._is_afk_name(player_name):
await coins_sessions_collection.insert_one({
"player_id": player_id,
"player_name": player_name,
"server_ip": server_ip,
"update_type": "coins_update",
"timestamp": now,
"minutes_added": 0,
"coins_added": 0,
"note": "afk_skip"
})
return
current_coins = user.get("coins", 0)
current_total_time = user.get("total_time_played", 0)
if last_update:
seconds_since_update = int((now - last_update["timestamp"]).total_seconds())
minutes_to_reward = seconds_since_update // 60
if minutes_to_reward < 1:
return
minutes_to_reward = min(minutes_to_reward, 1)
else:
minutes_to_reward = min(online_time // 60, 5)
if minutes_to_reward > 0:
new_coins = current_coins + minutes_to_reward
new_total_time = current_total_time + (minutes_to_reward * 60)
await users_collection.update_one(
{"_id": user["_id"]},
{"$set": {"coins": new_coins, "total_time_played": new_total_time}}
)
await coins_sessions_collection.insert_one({
"player_id": player_id,
"player_name": player_name,
"server_ip": server_ip,
"update_type": "coins_update",
"timestamp": now,
"minutes_added": minutes_to_reward,
"coins_added": minutes_to_reward
})
async def _find_user_by_uuid(self, player_id: str):
"""Находит пользователя по UUID с поддержкой разных форматов"""
# Пробуем найти как есть
user = await users_collection.find_one({"uuid": player_id})
if user:
return user
# Пробуем разные форматы UUID
if '-' in player_id:
user = await users_collection.find_one({"uuid": player_id.replace('-', '')})
else:
formatted_uuid = f"{player_id[:8]}-{player_id[8:12]}-{player_id[12:16]}-{player_id[16:20]}-{player_id[20:]}"
user = await users_collection.find_one({"uuid": formatted_uuid})
return user
async def get_player_coins(self, username: str):
"""Возвращает информацию о монетах и времени игрока"""
user = await users_collection.find_one({"username": username})
if not user:
return None
total_time = user.get("total_time_played", 0)
hours, remainder = divmod(total_time, 3600)
minutes, seconds = divmod(remainder, 60)
return {
"username": username,
"coins": user.get("coins", 0),
"total_time_played": {
"seconds": total_time,
"formatted": f"{hours}ч {minutes}м {seconds}с"
}
}
async def get_balance(self, username: str) -> int:
"""Получить текущий баланс пользователя"""
user = await users_collection.find_one({"username": username})
if not user:
raise HTTPException(status_code=404, detail=f"Пользователь {username} не найден")
return user.get("coins", 0)
async def increase_balance(self, username: str, amount: int) -> int:
"""Увеличить баланс пользователя"""
if amount <= 0:
raise ValueError("Сумма должна быть положительной")
result = await users_collection.update_one(
{"username": username},
{"$inc": {"coins": amount}}
)
if result.modified_count == 0:
raise HTTPException(status_code=404, detail=f"Пользователь {username} не найден")
user = await users_collection.find_one({"username": username})
return user.get("coins", 0)
async def decrease_balance(self, username: str, amount: int) -> int:
"""Уменьшить баланс пользователя"""
if amount <= 0:
raise ValueError("Сумма должна быть положительной")
result = await users_collection.update_one(
{"username": username},
{"$inc": {"coins": -amount}} # Уменьшаем на отрицательное значение
)
if result.modified_count == 0:
raise HTTPException(status_code=404, detail=f"Пользователь {username} не найден")
user = await users_collection.find_one({"username": username})
return user.get("coins", 0)