You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

204 lines
9.1 KiB

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()