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"] }