import ssl import discord from discord import app_commands from discord.ext import commands import os, sys import aiohttp from datetime import datetime from discord.ext import tasks from discord.app_commands.errors import * from player import * import traceback from exceptions import * import asyncio, sys, argparse from aiohttp.client_exceptions import ClientConnectionError import ws #Скрыть сообщение если надо ephemeral=True class DiscordClient(commands.Bot): main_server_id = 0 ANY_INPUT = "ссылка на стим | имя игрока | стимид | ид бана через #" discord2steam_cache = {} backend_url = "" secret_key = "" stats = {} show_stats_prev = 0 loaded_extensions = {} api_down = True def __init__(self, backend_url, secret_key): self.backend_url = backend_url self.secret_key = secret_key self.StartUpPreloadStats() ################################################### super().__init__(command_prefix="!", intents=discord.Intents.all()) #self.tree = app_commands.CommandTree(self) self.load_extensions(['user_ext', 'admin_ext', 'other_ext']) ################################################### self.setup_events() def load_extensions(self, extensions_path): if type(extensions_path) == str: extensions_path = [extensions_path] for path in extensions_path: print(f"Load extensions from: {path}") sys.path.insert(0, path) for extension in os.listdir(path): extension, ext = os.path.splitext(extension) if ext != ".py": continue print(f"Loading: {extension}") self.loaded_extensions[extension] = __import__(extension).Extension(self) sys.path.pop(0) async def setup_hook(self): await self.GetStats() print("sync tree") if os.getenv("MAIN_DISCORD_SERVER_ID",""): self.main_server_id = int(os.getenv("MAIN_DISCORD_SERVER_ID")) await self.tree.sync(guild=discord.Object(self.main_server_id)) await self.tree.sync() print(f"[init] check exts for have prepairs") for ext_name, ext in self.loaded_extensions.items(): if getattr(ext, "prepair", None): print(f"[init.prepair] {ext_name} have prepair") self.loop.create_task(self.loaded_extensions[ext_name].prepair()) print(f"[init] check exts for have primary task") for ext_name, ext in self.loaded_extensions.items(): if getattr(ext, "task", None): print(f"[init.task] {ext_name} have task") self.loop.create_task(self.loaded_extensions[ext_name].task()) print(f"[init] check exts for have secondary task") for ext_name, ext in self.loaded_extensions.items(): if getattr(ext, "task_secondary", None): print(f"[init.task] {ext_name} have secondary task") self.loop.create_task(self.loaded_extensions[ext_name].task_secondary()) def setup_events(self): @self.event async def on_ready(): print(f'Logged in as {self.user} (ID: {self.user.id})') print("Active guilds:") for guild in self.guilds: print(guild) @self.tree.error async def on_app_command_error(interaction, error): #надо сюда ебануть ошибки дискорда на даунов что делают не то что надо if isinstance(error, MissingRole): return await interaction.followup.send("Эту команду можно выполнить только на сервере где есть проверка ролей доступа!", ephemeral=True) elif isinstance(error, CommandNotFound): return await interaction.followup.send("У меня нет такой команды, возможно и есть...", ephemeral=True) elif isinstance(error.original, CannotCastToSteamID): return await interaction.followup.send("Не возможно найти такой профиль, попробуй написать иные данные!", ephemeral=True) elif isinstance(error.original, discord.app_commands.errors.NoPrivateMessage): return await interaction.followup.send("Вызванная тобой команда не работает в личных сообщениях", ephemeral=False) elif isinstance(error.original, LowPermition): return await interaction.followup.send("Это не для тебя и не для таких как ты сделано...", ephemeral=True) elif isinstance(error.original, AdminLowPermition): return await interaction.followup.send("Нельзя сделать это действие, ибо ты крут, но не настолько чтоб это совершить", ephemeral=True) elif isinstance(error.original, NotFoundPlayerOnServer): return await interaction.followup.send("Игрок не найден на серверах", ephemeral=True) elif isinstance(error.original, UnknownBackendResponse): traceback.print_exc() return await interaction.followup.send(f"Ошибка на стороне сервера в исполнении говнокода, стоит подождать или позвать помощь. Ответ сервера: {error.original.status_code}", ephemeral=False) elif isinstance(error.original, discord.errors.NotFound): return await interaction.followup.send("Слишком долгий ответ со стороны сервера, причины:\n1) Возможно бекенд сдох\n2)Cлишком долгий незапланированный ответ с сервера\n3)Стоит позвать помощь", ephemeral=True) elif isinstance(error.original, NeedDiscordAuthOfSteam): return await interaction.followup.send("У тебя не привязан профиль стима к дискорду, ничего подобного делать тебе нельзя", ephemeral=True) elif isinstance(error.original, ClientConnectionError): return await interaction.followup.send("Сервис фактов13 не дал ответ, вероятно он умер, поэтому стоит немного подождать и может что-то изменится", ephemeral=True) traceback.print_exc() return await interaction.followup.send("Возникла необратимая ошибка, хз что случилось. Попробуй еще раз!", ephemeral=True) async def GetSteam64OfDiscord(self, user, no_cache = False): if user.id in self.discord2steam_cache and not no_cache: return self.discord2steam_cache[user.id] async with aiohttp.ClientSession(cookies={"secretkey":self.secret_key}) as session: async with session.get(f"{self.backend_url}/api/discord?discord_id={user.id}", ssl=False) as response: try: steamid_response = await response.json() except: raise NeedDiscordAuthOfSteam if response.status == 200 and steamid_response != None: self.discord2steam_cache[user.id] = steamid_response["steam64"] else: raise NeedDiscordAuthOfSteam return self.discord2steam_cache[user.id] async def GetPlayer(self, profile, requester_steam64, load_profile = True): player = Player(profile, requester_steam64, self.stats) await player.GetSteamID() if load_profile: await player.LoadProfile() return player async def GetStats(self): try: async with aiohttp.ClientSession() as session: async with session.get(f"{os.getenv('BACKEND_URL')}/api/stats", ssl=False) as response: self.stats = await response.json() self.api_down = False except Exception as e: self.api_down = True raise e 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") def utime2human(self, utime): return datetime.fromtimestamp(utime).strftime('%H:%M:%S %d.%m.%Y') if __name__ == "__main__": print(f"Build date: {os.getenv('BUILDDATE', 'not setted in env')}") parser = argparse.ArgumentParser() parser.add_argument("--import-test", action="store_true", default=None) args = parser.parse_args() if args.import_test: DiscordClient("","") sys.exit(0) def run(): DiscordClient( os.getenv("BACKEND_URL"), os.getenv("BACKEND_SECRETKEY") ).run(os.getenv("DISCORD_TOKEN")) ws.SERVICE_NAME = "discord_bot" ws.START_AFTER_CONNECT = run wsc = ws.WS(os.getenv("BACKEND_URL"), os.getenv("BACKEND_SECRETKEY")) wsc.run()