From 688aa4eab0ed7fcf9980e38f4ac385ecc612e9ef Mon Sep 17 00:00:00 2001 From: gsd Date: Sat, 4 Mar 2023 21:47:12 +0300 Subject: [PATCH] =?UTF-8?q?=D1=87=D0=BE=D1=82=20=D1=81=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B0=D0=BB=20=D1=87=D1=82=D0=BE=20=D0=B2=D0=B0=D1=89=D0=B5=20?= =?UTF-8?q?=D0=BA=D1=80=D1=83=D1=82=D0=BE=20=D0=B0=D1=85=D1=83=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin_ext/rcon.py | 13 ++++++++++--- admin_ext/use.py | 1 + bot.py | 37 +++++++++++++++++++++++++++++++++++-- docker-compose.yaml | 4 ++-- player.py | 10 +++++++++- user_ext/servers.py | 39 +++++++++++++++++++++++++++++++-------- 6 files changed, 88 insertions(+), 16 deletions(-) diff --git a/admin_ext/rcon.py b/admin_ext/rcon.py index 7f14b10..6079aa7 100644 --- a/admin_ext/rcon.py +++ b/admin_ext/rcon.py @@ -4,17 +4,24 @@ import aiohttp class Extension: def __init__(self, core): @core.tree.command(name = "rcon", description = "Вызвать команду на сервере") - @discord.app_commands.describe(server="Нужным сервер в формате srv1", command="Команда исполнения") + @discord.app_commands.describe(servers="Нужным сервер", command="Команда исполнения") + @discord.app_commands.checks.has_role("Администратор Фактов13") + @discord.app_commands.choices(servers = core.GetServersChoice()) async def rcon_command( interaction: discord.Interaction, - server: str, + servers: discord.app_commands.Choice[str], command: str ): await interaction.response.defer(thinking=True) steam64 = await core.GetSteam64OfDiscord(interaction.user) + server = servers.value if not server in core.stats.get("servers", {}).keys(): return await interaction.followup.send(f"Сервер с таким индификатором не существует, введи существующий из предложенных:\n{' '.join(core.stats.get('servers', {}).keys())}", ephemeral=False) async with aiohttp.ClientSession(cookies={"secretkey":core.secret_key, "steam64":steam64}) as session: async with session.post(f"{core.backend_url}/api/admin/rcon?srv={server}&command={command}", ssl=False) as response: - return await interaction.followup.send(f'{await response.text()}', ephemeral=False) \ No newline at end of file + res = await response.text() + if response.status == 200: + return await interaction.followup.send(f'Ответ: {res}', ephemeral=False) + else: + return await interaction.followup.send('хуй тебе', ephemeral=False) \ No newline at end of file diff --git a/admin_ext/use.py b/admin_ext/use.py index f2ac289..bb337a1 100644 --- a/admin_ext/use.py +++ b/admin_ext/use.py @@ -3,6 +3,7 @@ import discord class Extension: def __init__(self, core): @core.tree.command(name = "use", description = "Использовать команду на игрока") + @discord.app_commands.checks.has_role("Администратор Фактов13") @discord.app_commands.describe(command="Команда исполнения", profile=core.ANY_INPUT, args="Аргумент если нужен") async def rcon_use_command( interaction: discord.Interaction, diff --git a/bot.py b/bot.py index deca2e5..ae60d8f 100644 --- a/bot.py +++ b/bot.py @@ -8,6 +8,7 @@ from discord.ext import tasks from player import * import traceback from exceptions import * +import asyncio, sys #Скрыть сообщение если надо ephemeral=True class DiscordClient(discord.Client): @@ -21,6 +22,7 @@ class DiscordClient(discord.Client): def __init__(self, backend_url, secret_key): self.backend_url = backend_url self.secret_key = secret_key + self.StartUpPreloadStats() ################################################### super().__init__(intents=discord.Intents.default()) self.tree = app_commands.CommandTree(self) @@ -43,7 +45,7 @@ class DiscordClient(discord.Client): sys.path.pop(0) async def setup_hook(self): - self.stats = await self.GetStats() + await self.GetStats() print("sync tree") if os.getenv("MAIN_DISCORD_SERVER_ID",""): await self.tree.sync(guild=discord.Object(int(os.getenv("MAIN_DISCORD_SERVER_ID")))) @@ -95,7 +97,38 @@ class DiscordClient(discord.Client): async def GetStats(self): async with aiohttp.ClientSession() as session: async with session.get(f"{os.getenv('BACKEND_URL')}/api/stats", ssl=False) as response: - return await response.json() + self.stats = await response.json() + return self.stats + + def GetServersChoice(self): + """ + @app_commands.command() + @app_commands.describe(fruits='fruits to choose from') + @app_commands.choices(fruits=[ + Choice(name='apple', value=1), + Choice(name='banana', value=2), + Choice(name='cherry', value=3), + ])s + async def fruit(interaction: discord.Interaction, fruits: Choice[int]): + await interaction.response.send_message(f'Your favourite fruit is {fruits.name}.')""" + servers = [] + if self.stats.get("servers", {}): + for server_name, server in self.stats["servers"].items(): + servers.append(app_commands.Choice(name = server["name"], value=server_name)) + else: + print("backend stats not pre loaded, use standarts") + for i in range(1,10): + servers.append(app_commands.Choice(name = f"srv{i}", value=f"srv{i}")) + return servers + + def StartUpPreloadStats(self): + print("pre load backend stats") + asyncio.get_event_loop().run_until_complete(self.GetStats()) + if not self.stats: + print("backend not working down bot") + sys.exit(1) + else: + print("backend is up, continue build app") if __name__ == "__main__": DiscordClient( diff --git a/docker-compose.yaml b/docker-compose.yaml index 325c218..922b117 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -4,7 +4,7 @@ services: container_name: facti13bot_discord_v2 env_file: - .env - extra_hosts: - - "tf2.pblr-nyk.pro:192.168.3.3" + #extra_hosts: + # - "tf2.pblr-nyk.pro:192.168.1.1" hostname: 'discord' restart: unless-stopped \ No newline at end of file diff --git a/player.py b/player.py index 288ea9e..44cd2f0 100644 --- a/player.py +++ b/player.py @@ -186,7 +186,15 @@ class Player: "secretkey":os.getenv("BACKEND_SECRETKEY"), "steam64": self.requester_steam64}) as session: async with session.post(f"{os.getenv('BACKEND_URL')}/api/admin/rcon?srv={server}&command={final_command}", ssl=False) as response: - return await response.text() + res = await response.text() + if response.status == 200: + return res + elif response.status == 404: + raise NotFoundPlayerOnServer + elif response.status == 403: + raise LowPermition + elif response.status == 406: + raise AdminLowPermition else: raise NotFoundPlayerOnServer diff --git a/user_ext/servers.py b/user_ext/servers.py index e524576..d428292 100644 --- a/user_ext/servers.py +++ b/user_ext/servers.py @@ -3,19 +3,21 @@ import discord class Extension: def __init__(self, core): @core.tree.command(name = "servers", description = "Показать статистику с серверов") - @discord.app_commands.describe(server = "ид сервера в формате srv1") + @discord.app_commands.describe(server = "Название сервера") + @discord.app_commands.choices(server = core.GetServersChoice()) async def get_servers( interaction: discord.Interaction, - server: str = "" + server: discord.app_commands.Choice[str] = None ): await interaction.response.defer(thinking=True) steam64 = await core.GetSteam64OfDiscord(interaction.user) embed = discord.Embed() if server: + server = server.value if not server in core.stats.get("servers", {}).keys(): return await interaction.followup.send(f"Сервер с таким индификатором не существует, введи существующий из предложенных:\n{' '.join(core.stats.get('servers', {}).keys())}", ephemeral=False) - embed.add_field(name=core.stats['servers'][server]['name'], value=str_server(core.stats['servers'][server]), inline=False) + embed = single_server(core.stats['servers'][server]) else: servers = [server for server in core.stats["servers"].values() if server['status'] == True and server['player_count'] > 0] if servers: @@ -38,8 +40,7 @@ class Extension: return await interaction.followup.send(embed=embed, ephemeral=False) - -def str_server(data): +def str_server(data, with_profiles = False): message = "" addr = data['address'].split(":") message += f"https://{addr[0]}/connect/{addr[1]}\n" @@ -49,6 +50,28 @@ def str_server(data): message += f"Карта: {data['map']}\n" message += f"Игроков: {data['player_count']}/{data['max_players']}\n" message += "\n" - for player in data['players']: - message += f"{player['duration']:7} | {player['score']:3} | {player['name']}\n" - return message \ No newline at end of file + for player in sorted(data['players'], key = lambda player:player["score"], reverse=True): + message += f"{player['duration']:7} | {player['score']:3} | {player['name']}{(' | ' + player['steam']['community_url']) if with_profiles else ''}\n" + return message + +def single_server(server) -> discord.Embed: + addr = server['address'].split(":") + embed = discord.Embed( + title=server['name'], + description=f"Карта: {server['map']}\nИгроков: {server['player_count']}/{server['max_players']}", + url=f"https://{addr[0]}/connect/{addr[1]}") + if len(server['players']) > 24: + count = 0 + value = "" + for player in sorted(server['players'], key = lambda player:player["score"], reverse=True): + if count >= 24: + value += f"{player['duration']:7} | {player['score']:3} | {player['name']}\n{player['steam']['community_url']}\n\n" + continue + embed.add_field(name = f"{player['duration']:7} | {player['score']:3} | {player['name']}", value=player['steam']['community_url'], inline=False) + count += 1 + if value: + embed.add_field(name = f"Прочие игроки", value=value, inline=False) + else: + for player in sorted(server['players'], key = lambda player:player["score"], reverse=True): + embed.add_field(name = f"{player['duration']:7} | {player['score']:3} | {player['name']}", value=player['steam']['community_url'], inline=False) + return embed \ No newline at end of file