289 lines
13 KiB
Python
289 lines
13 KiB
Python
from fastapi import HTTPException
|
||
from app.db.database import db, users_collection
|
||
from app.models.server.prank import PrankCommand, PrankCommandUpdate
|
||
from datetime import datetime, timedelta
|
||
import uuid
|
||
from app.services.server.command import CommandService
|
||
|
||
# Создаем коллекции для хранения пакостей и серверов
|
||
prank_commands_collection = db.prank_commands
|
||
game_servers_collection = db.game_servers
|
||
online_players_collection = db.online_players
|
||
|
||
class PrankService:
|
||
async def add_prank_command(self, command_data):
|
||
"""Добавление новой команды-пакости"""
|
||
# Проверяем корректность шаблона команды
|
||
if "{targetPlayer}" not in command_data.command_template:
|
||
raise HTTPException(status_code=400,
|
||
detail="Шаблон команды должен содержать {targetPlayer} для подстановки имени цели")
|
||
|
||
prank_id = str(uuid.uuid4())
|
||
|
||
# Создаем новую команду в БД
|
||
prank_command = {
|
||
"id": prank_id,
|
||
"name": command_data.name,
|
||
"description": command_data.description,
|
||
"price": command_data.price,
|
||
"command_template": command_data.command_template,
|
||
"server_ids": command_data.server_ids,
|
||
"targetDescription": command_data.targetDescription,
|
||
"globalDescription": command_data.globalDescription, # Добавить это поле
|
||
"created_at": datetime.utcnow()
|
||
}
|
||
|
||
await prank_commands_collection.insert_one(prank_command)
|
||
|
||
return {"status": "success", "id": prank_id}
|
||
|
||
async def get_all_prank_commands(self):
|
||
"""Получение списка всех команд-пакостей"""
|
||
commands = await prank_commands_collection.find().to_list(1000)
|
||
result = []
|
||
|
||
for cmd in commands:
|
||
result.append({
|
||
"id": cmd["id"],
|
||
"name": cmd["name"],
|
||
"description": cmd["description"],
|
||
"price": cmd["price"],
|
||
"command_template": cmd["command_template"],
|
||
"server_ids": cmd.get("server_ids", []),
|
||
"targetDescription": cmd.get("targetDescription"),
|
||
"globalDescription": cmd.get("globalDescription") # Добавить это поле
|
||
})
|
||
|
||
return result
|
||
|
||
async def get_prank_command(self, command_id: str):
|
||
"""Получение конкретной команды по ID"""
|
||
command = await prank_commands_collection.find_one({"id": command_id})
|
||
if not command:
|
||
raise HTTPException(status_code=404, detail="Команда не найдена")
|
||
|
||
return {
|
||
"id": command["id"],
|
||
"name": command["name"],
|
||
"description": command["description"],
|
||
"price": command["price"],
|
||
"command_template": command["command_template"],
|
||
"server_ids": command.get("server_ids", []),
|
||
"targetDescription": command.get("targetDescription"),
|
||
"globalDescription": command.get("globalDescription") # Добавить это поле
|
||
}
|
||
|
||
async def update_prank_command(self, command_id: str, update_data: PrankCommandUpdate):
|
||
"""Обновление команды-пакости"""
|
||
command = await prank_commands_collection.find_one({"id": command_id})
|
||
if not command:
|
||
raise HTTPException(status_code=404, detail="Команда не найдена")
|
||
|
||
# Готовим данные для обновления
|
||
update = {}
|
||
if update_data.name is not None:
|
||
update["name"] = update_data.name
|
||
if update_data.description is not None:
|
||
update["description"] = update_data.description
|
||
if update_data.price is not None:
|
||
update["price"] = update_data.price
|
||
if update_data.command_template is not None:
|
||
if "{targetPlayer}" not in update_data.command_template:
|
||
raise HTTPException(status_code=400,
|
||
detail="Шаблон команды должен содержать {targetPlayer} для подстановки имени цели")
|
||
update["command_template"] = update_data.command_template
|
||
if update_data.server_ids is not None:
|
||
update["server_ids"] = update_data.server_ids
|
||
if update_data.targetDescription is not None:
|
||
update["targetDescription"] = update_data.targetDescription
|
||
if update_data.globalDescription is not None: # Добавить эту проверку
|
||
update["globalDescription"] = update_data.globalDescription
|
||
|
||
if update:
|
||
result = await prank_commands_collection.update_one(
|
||
{"id": command_id},
|
||
{"$set": update}
|
||
)
|
||
|
||
if result.modified_count == 0:
|
||
raise HTTPException(status_code=500, detail="Ошибка при обновлении команды")
|
||
|
||
return {"status": "success"}
|
||
|
||
async def delete_prank_command(self, command_id: str):
|
||
"""Удаление команды-пакости"""
|
||
command = await prank_commands_collection.find_one({"id": command_id})
|
||
if not command:
|
||
raise HTTPException(status_code=404, detail="Команда не найдена")
|
||
|
||
result = await prank_commands_collection.delete_one({"id": command_id})
|
||
if result.deleted_count == 0:
|
||
raise HTTPException(status_code=500, detail="Ошибка при удалении команды")
|
||
|
||
return {"status": "success"}
|
||
|
||
async def get_all_servers(self):
|
||
"""Получение списка всех доступных серверов"""
|
||
# Проверяем и удаляем неактивные серверы (более 5 минут без данных)
|
||
current_time = datetime.utcnow()
|
||
inactive_threshold = 5 * 60 # 5 минут в секундах
|
||
|
||
# Находим серверы, которые не отправляли данные больше 5 минут
|
||
# Учитываем, что у некоторых серверов может не быть поля last_activity
|
||
inactive_servers = await game_servers_collection.find({
|
||
"last_activity": {
|
||
"$exists": True,
|
||
"$lt": current_time - timedelta(seconds=inactive_threshold)
|
||
}
|
||
}).to_list(100)
|
||
|
||
# Удаляем неактивные серверы
|
||
if inactive_servers:
|
||
server_ids = [server["id"] for server in inactive_servers]
|
||
await game_servers_collection.delete_many({"id": {"$in": server_ids}})
|
||
|
||
# Опционально: логирование удаленных серверов
|
||
for server in inactive_servers:
|
||
print(f"Удален неактивный сервер: {server['name']} (ID: {server['id']})")
|
||
|
||
# Получаем актуальный список серверов
|
||
servers = await game_servers_collection.find().to_list(100)
|
||
|
||
# Если нет зарегистрированных серверов, вернем пустой список
|
||
if not servers:
|
||
return []
|
||
|
||
result = []
|
||
for server in servers:
|
||
# Получаем количество онлайн игроков
|
||
online_count = await online_players_collection.count_documents(
|
||
{"server_id": server["id"], "is_online": True}
|
||
)
|
||
|
||
result.append({
|
||
"id": server["id"],
|
||
"name": server["name"],
|
||
"ip": server.get("ip"),
|
||
"port": server.get("port"),
|
||
"description": server.get("description", ""),
|
||
"online_players": online_count,
|
||
"max_players": server.get("max_players", 0),
|
||
"last_activity": server.get("last_activity")
|
||
})
|
||
|
||
return result
|
||
|
||
async def get_server_online_players(self, server_id: str):
|
||
"""Получение списка онлайн игроков на конкретном сервере"""
|
||
server = await game_servers_collection.find_one({"id": server_id})
|
||
if not server:
|
||
raise HTTPException(status_code=404, detail="Сервер не найден")
|
||
|
||
players = await online_players_collection.find(
|
||
{"server_id": server_id, "is_online": True}
|
||
).to_list(1000)
|
||
|
||
result = []
|
||
for player in players:
|
||
result.append({
|
||
"username": player["username"],
|
||
"uuid": player.get("uuid", ""),
|
||
"online_since": player.get("login_time")
|
||
})
|
||
|
||
return {
|
||
"server": {
|
||
"id": server["id"],
|
||
"name": server["name"]
|
||
},
|
||
"online_players": result,
|
||
"count": len(result)
|
||
}
|
||
|
||
async def execute_prank(self, username: str, command_id: str, target_player: str, server_id: str):
|
||
"""Выполнение пакости (покупка и выполнение команды)"""
|
||
# Проверяем пользователя
|
||
user = await users_collection.find_one({"username": username})
|
||
if not user:
|
||
raise HTTPException(status_code=404, detail="Пользователь не найден")
|
||
|
||
# Проверяем команду
|
||
command = await prank_commands_collection.find_one({"id": command_id})
|
||
if not command:
|
||
raise HTTPException(status_code=404, detail="Команда не найдена")
|
||
|
||
# Проверяем сервер
|
||
server = await game_servers_collection.find_one({"id": server_id})
|
||
if not server:
|
||
raise HTTPException(status_code=404, detail="Сервер не найден")
|
||
|
||
# Проверяем, доступна ли команда на данном сервере
|
||
if (command.get("server_ids") and
|
||
"*" not in command.get("server_ids", []) and
|
||
server_id not in command.get("server_ids", [])):
|
||
raise HTTPException(status_code=400, detail="Команда недоступна на выбранном сервере")
|
||
|
||
# Проверяем, онлайн ли целевой игрок
|
||
target_online = await online_players_collection.find_one({
|
||
"username": target_player,
|
||
"server_id": server_id,
|
||
"is_online": True
|
||
})
|
||
|
||
if not target_online:
|
||
raise HTTPException(status_code=400, detail=f"Игрок {target_player} не в сети на этом сервере")
|
||
|
||
# Проверяем достаточно ли монет
|
||
user_coins = user.get("coins", 0)
|
||
if user_coins < command["price"]:
|
||
raise HTTPException(status_code=400,
|
||
detail=f"Недостаточно монет. Требуется: {command['price']}, имеется: {user_coins}")
|
||
|
||
# Формируем команду для выполнения
|
||
actual_command = command["command_template"].replace("{targetPlayer}", target_player)
|
||
|
||
# Обрабатываем оба типа сообщений
|
||
target_desc = None
|
||
global_desc = None
|
||
|
||
if command.get("targetDescription"):
|
||
target_desc = command.get("targetDescription").replace("{username}", username).replace("{targetPlayer}", target_player)
|
||
|
||
if command.get("globalDescription"):
|
||
global_desc = command.get("globalDescription").replace("{username}", username).replace("{targetPlayer}", target_player)
|
||
|
||
# Отправляем команду с обоими сообщениями
|
||
command_service = CommandService()
|
||
from app.models.server.command import ServerCommand
|
||
|
||
server_command = ServerCommand(
|
||
command=actual_command,
|
||
server_ip=server.get("ip", ""),
|
||
require_online_player=True,
|
||
target_message=target_desc, # Сообщение для цели
|
||
global_message=global_desc # Сообщение для всех остальных
|
||
)
|
||
|
||
command_result = await command_service.add_command(server_command)
|
||
|
||
# Логируем выполнение пакости
|
||
log_entry = {
|
||
"user_id": user["_id"],
|
||
"username": username,
|
||
"target_player": target_player,
|
||
"command_id": command_id,
|
||
"command_name": command["name"],
|
||
"server_id": server_id,
|
||
"price": command["price"],
|
||
"executed_command": actual_command,
|
||
"executed_at": datetime.utcnow()
|
||
}
|
||
|
||
await db.prank_executions.insert_one(log_entry)
|
||
|
||
return {
|
||
"status": "success",
|
||
"message": f"Команда '{command['name']}' успешно выполнена на игроке {target_player}",
|
||
"remaining_coins": user_coins - command["price"]
|
||
}
|