Добавил проект с учетом .gitignore
This commit is contained in:
810
src/CoreFun.py
Normal file
810
src/CoreFun.py
Normal file
@@ -0,0 +1,810 @@
|
||||
import disnake
|
||||
from disnake.ext import commands
|
||||
from disnake.ext import tasks
|
||||
import requests
|
||||
import numpy as np
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
import copy
|
||||
import datetime
|
||||
import math
|
||||
import random
|
||||
import inspect
|
||||
import json
|
||||
import shutil
|
||||
import re
|
||||
|
||||
from constants.rimagochi_constants import *
|
||||
from constants.global_constants import *
|
||||
from data.TOKENS import TOKENS
|
||||
from database.db_classes import all_data as DataBaseClasses
|
||||
from managers.DataBaseManager import DatabaseManager
|
||||
from database.settings import config
|
||||
|
||||
from sqlalchemy.orm import declarative_base, relationship
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy.schema import CreateTable
|
||||
|
||||
class AnyBots(commands.Bot):
|
||||
'''
|
||||
|
||||
|
||||
Any bot class
|
||||
|
||||
|
||||
'''
|
||||
def __init__(self, DataBaseManager):
|
||||
super().__init__(
|
||||
command_prefix="=",
|
||||
intents=disnake.Intents.all()
|
||||
)
|
||||
self.DataBaseManager = DataBaseManager
|
||||
self.costrolecreate = constants['costrolecreate']
|
||||
self.costrolerenewal = constants['costrolerenewal']
|
||||
self.dailycrumbs = constants['dailycrumbs']
|
||||
self.casinospinslimit = constants['casinospinslimit']
|
||||
|
||||
async def on_ready(self, inherited = False):
|
||||
self.krekchat = await self.fetch_guild(constants["krekchat"])
|
||||
print(self.krekchat.name)
|
||||
self.sponsors = [disnake.utils.get(self.krekchat.roles, id=i) for i in constants["sponsors"]]
|
||||
self.mutes = [disnake.utils.get(self.krekchat.roles, id=i) for i in constants["mutes"]]
|
||||
self.ban_role = disnake.utils.get(self.krekchat.roles, id=constants["ban_role"])
|
||||
self.me = disnake.utils.get(self.krekchat.roles, id=constants["me"])
|
||||
self.moder = disnake.utils.get(self.krekchat.roles, id=constants["moder"])
|
||||
self.curator = disnake.utils.get(self.krekchat.roles, id=constants["curator"])
|
||||
self.everyone = disnake.utils.get(self.krekchat.roles, id=constants["everyone"])
|
||||
self.staff = disnake.utils.get(self.krekchat.roles, id=constants["staff"])
|
||||
self.level_roles = [disnake.utils.get(self.krekchat.roles, id=i) for i in constants["level_roles"]]
|
||||
# lists
|
||||
self.moderators = [disnake.utils.get(self.krekchat.roles, id=i) for i in constants["moderators"]]
|
||||
# /lists
|
||||
self.bots_talk_protocol_channel_id = constants["bots_talk_protocol_channel"]
|
||||
self.databases_backups_channel_id = constants["databases_backups_channel"]
|
||||
await self.change_presence(status=disnake.Status.online, activity=disnake.Game("Работаю"))
|
||||
|
||||
if not inherited:
|
||||
print(f"{datetime.datetime.now().strftime('%H:%M:%S %d-%m-%Y')}:: KrekFunBot activated")
|
||||
|
||||
async def RimagochiUserUpdate(self, member, session = None):
|
||||
if session is None:
|
||||
raise Exception("Реализация RimagochiUserUpdate без session ещё не сделана")
|
||||
|
||||
else:
|
||||
await self.UserUpdate(member, session = session)
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['rimagochi_users'] as rimagochi_users_models:
|
||||
stmt = self.DataBaseManager.select(rimagochi_users_models).where(rimagochi_users_models.id == member.id)
|
||||
rimagochi_user = (await session.execute(stmt)).scalars().first()
|
||||
|
||||
if rimagochi_user is None:
|
||||
rimagochi_user = rimagochi_users_models(id = member.id, settings = rimagochi_default_settings)
|
||||
session.add(rimagochi_user)
|
||||
|
||||
async def CasinoUserUpdate(self, member, session = None):
|
||||
if session is None:
|
||||
raise Exception("Реализация CasinoUserUpdate без session ещё не сделана")
|
||||
|
||||
else:
|
||||
await self.UserUpdate(member, session = session)
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['casino_user_account'] as casino_user_account_models:
|
||||
stmt = self.DataBaseManager.select(casino_user_account_models).where(casino_user_account_models.id == member.id)
|
||||
casino_user = (await session.execute(stmt)).scalars().first()
|
||||
|
||||
if casino_user is None:
|
||||
casino_user = casino_user_account_models(id = member.id)
|
||||
session.add(casino_user)
|
||||
|
||||
async def UserUpdate(self, member, session = None):
|
||||
|
||||
if session is None:
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['users'] as users_model:
|
||||
stmt = self.DataBaseManager.select(users_model).where(users_model.id == member.id)
|
||||
user = (await session.execute(stmt)).scalars().first()
|
||||
|
||||
if user is None:
|
||||
user = users_model(id = member.id)
|
||||
session.add(user)
|
||||
|
||||
return 0
|
||||
else:
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['users'] as users_model:
|
||||
stmt = self.DataBaseManager.select(users_model).where(users_model.id == member.id)
|
||||
user = (await session.execute(stmt)).scalars().first()
|
||||
|
||||
if user is None:
|
||||
user = users_model(id = member.id)
|
||||
session.add(user)
|
||||
return user
|
||||
|
||||
async def DeleteRole(self, role_id: int):
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['roles_custom'] as roles_custom:
|
||||
stmt = self.DataBaseManager.select(roles_custom).where(
|
||||
roles_custom.id == role_id
|
||||
).with_for_update()
|
||||
role = (await session.execute(stmt)).scalars().first()
|
||||
if not role is None:
|
||||
await session.delete(role)
|
||||
|
||||
krekchat = await self.fetch_guild(constants["krekchat"])
|
||||
role = krekchat.get_role(role_id)
|
||||
if not role:
|
||||
return [True, "Кастомная роль успешно удалена из базы данных, но не найдена на сервере"]
|
||||
# deleting from server
|
||||
await role.delete()
|
||||
return [True, "Кастомная роль успешно удалена"]
|
||||
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['roles_prize'] as roles_prize:
|
||||
async with self.DataBaseManager.models['roles_static'] as roles_static:
|
||||
stmt = self.DataBaseManager.select(roles_prize).where(
|
||||
roles_prize.id == role_id
|
||||
).with_for_update()
|
||||
prize_role = (await session.execute(stmt)).scalars().first()
|
||||
if not prize_role is None:
|
||||
await session.delete(prize_role)
|
||||
stmt = self.DataBaseManager.select(roles_static).where(
|
||||
roles_static.id == role_id
|
||||
).with_for_update()
|
||||
static_role = (await session.execute(stmt)).scalars().first()
|
||||
if not static_role is None:
|
||||
await session.delete(static_role)
|
||||
krekchat = await self.fetch_guild(constants["krekchat"])
|
||||
role = krekchat.get_role(role_id)
|
||||
if not role:
|
||||
return [True, "Призовая роль успешно удалена из базы данных, но не найдена на сервере"]
|
||||
await role.delete()
|
||||
return [True, "Призовая роль успешно удалена"]
|
||||
|
||||
return [False, "Такой роли не существует в базе данных"]
|
||||
|
||||
def PartitioningEmbeder(self, arr):
|
||||
result = []
|
||||
for i in range(len(arr)):
|
||||
if i % 5 == 0:
|
||||
result.append([])
|
||||
result[-1].append(arr[i])
|
||||
return result
|
||||
|
||||
async def CalculateRimagochiBattleStrength(self, users_animals: list):
|
||||
sum_strenght = 0
|
||||
for animal in users_animals:
|
||||
if not animal.in_battle_slots:
|
||||
continue
|
||||
animal_model = copy.deepcopy(rimagochi_animals[animal.model_animal_id])
|
||||
for gene in animal.genes:
|
||||
genedata = rimagochi_genes[gene]
|
||||
for effect in genedata['effects'].keys():
|
||||
animal_model['params'][effect] += genedata['effects'][effect]
|
||||
sum_strenght = sum_strenght + ((animal_model['params']['damage']*animal_model['params']['health'])+(animal_model['params']['damage']*animal_model['params']['health']*animal.level/4))
|
||||
return sum_strenght
|
||||
|
||||
async def LevelRolesGiver(self, member, level):
|
||||
try:
|
||||
c = 0
|
||||
if level >= 1: c += 1
|
||||
if level >= 5: c += 1
|
||||
if level >= 55: c += 1
|
||||
if level >= 91: c += 1
|
||||
for role in range(1, len(self.level_roles)):
|
||||
if role == c:
|
||||
await member.add_roles(self.level_roles[role])
|
||||
continue
|
||||
await member.remove_roles(self.level_roles[role])
|
||||
except:
|
||||
pass
|
||||
|
||||
def HexToRgb(self, value):
|
||||
value = value.lstrip('#')
|
||||
lv = len(value)
|
||||
return tuple(int(value[i:i + lv // 3], 16) for i in range(0, lv, lv // 3))
|
||||
|
||||
def find_for_arr(self, arr, elem):
|
||||
for i in range(len(arr)):
|
||||
if arr[i] == elem:
|
||||
return i
|
||||
return -1
|
||||
|
||||
def AnimalLevelAdder(self, exp, level):
|
||||
if(level+1<=max(rimagochi_constants["animals_exp_to_levels"].keys())):
|
||||
if exp>=rimagochi_constants["animals_exp_to_levels"][level+1]:
|
||||
exp = exp - rimagochi_constants["animals_exp_to_levels"][level+1]
|
||||
level += 1
|
||||
if (level+1<=max(rimagochi_constants["animals_exp_to_levels"].keys())):
|
||||
return exp, level, rimagochi_constants["animals_exp_to_levels"][level+1] - exp
|
||||
else:
|
||||
return exp, level, float('inf')
|
||||
else:
|
||||
return exp, level, rimagochi_constants["animals_exp_to_levels"][level+1] - exp
|
||||
else:
|
||||
return exp, level, float('inf')
|
||||
|
||||
def CalculateLevel(self, messages, voiceactivity):
|
||||
n = (messages + (voiceactivity / (180))) / 4
|
||||
# return math.log((messages+(voiceactivity/(30)))+1, 1.11)
|
||||
if n < 40:
|
||||
return n / 3
|
||||
else:
|
||||
return math.log(n, 1.05) - 62
|
||||
|
||||
def TimeFormater(self, time_str: str = "", days: float = 0, hours: float = 0, minutes: float = 0, now_timestamp = None):
|
||||
"""
|
||||
Форматирует строку времени в timestamp и разложенное время
|
||||
Поддерживает форматы: 1d2h30m, 1д2ч30мин, 1.5d, 1 день 2 часа 30 минут и т.п.
|
||||
Возвращает объект класса FormatedTime
|
||||
"""
|
||||
|
||||
class FormatedTime:
|
||||
def __init__(self, time_units):
|
||||
self.time_units = time_units
|
||||
|
||||
self.days, self.hours, self.minutes = time_units['d'], time_units['h'], time_units['m']
|
||||
delta = datetime.timedelta(days=self.days, hours=self.hours, minutes=self.minutes)
|
||||
self.future_time = 0
|
||||
if now_timestamp is None:
|
||||
self.future_time = datetime.datetime.now() + delta
|
||||
else:
|
||||
self.future_time = datetime.datetime.fromtimestamp(now_timestamp) + delta
|
||||
self.timestamp = self.future_time.timestamp()
|
||||
|
||||
def __float__(self):
|
||||
return self.timestamp
|
||||
|
||||
def __int__(self):
|
||||
return int(self.timestamp)
|
||||
|
||||
def __repr__(self):
|
||||
result = []
|
||||
if self.days != 0:
|
||||
result.append(f"{self.days} дней")
|
||||
if self.hours != 0:
|
||||
result.append(f"{self.hours} часов")
|
||||
if self.minutes != 0:
|
||||
result.append(f"{self.minutes} минут")
|
||||
|
||||
return ", ".join(result) + f" [{self.timestamp}]"
|
||||
|
||||
def to_dict(self):
|
||||
return self.time_units
|
||||
|
||||
time_units = {'d': 0, 'h': 0, 'm': 0}
|
||||
if any([days, hours, minutes]):
|
||||
time_units = {'d': days, 'h': hours, 'm': minutes}
|
||||
else:
|
||||
time_str = time_str.lower().replace(' ', '').replace(',', '.')
|
||||
|
||||
replacements = {
|
||||
r'дней?': 'd',
|
||||
r'день': 'd',
|
||||
r'д': 'd',
|
||||
r'часов?': 'h',
|
||||
r'час': 'h',
|
||||
r'ч': 'h',
|
||||
r'минут?': 'm',
|
||||
r'мин': 'm',
|
||||
r'м': 'm',
|
||||
}
|
||||
|
||||
for pattern, replacement in replacements.items():
|
||||
time_str = re.sub(pattern, replacement, time_str, flags=re.IGNORECASE)
|
||||
|
||||
pattern = r'(\d+(\.\d+)?)([dhm])'
|
||||
matches = re.findall(pattern, time_str)
|
||||
|
||||
for value, _, unit in matches:
|
||||
time_units[unit] += float(value)
|
||||
|
||||
return FormatedTime(time_units)
|
||||
|
||||
class ErrorOutHelper:
|
||||
def __init__(self, send_function, err_name: str = "", err_description: str = "", ephemeral: bool = False, echo: bool = False, thumbnail = None):
|
||||
self.err_name = err_name
|
||||
self.err_description = err_description
|
||||
self.send_function = send_function
|
||||
self.ephemeral = ephemeral
|
||||
self.echo = echo
|
||||
self.thumbnail = thumbnail
|
||||
self.colour = 0xff0000
|
||||
|
||||
async def out(self, err_description: str = "", err_name: str = "", d: str = "", n: str = ""):
|
||||
if d:
|
||||
err_description = d
|
||||
if n:
|
||||
err_name = n
|
||||
|
||||
embed = disnake.Embed(title="", description="", colour = self.colour)
|
||||
if err_name:
|
||||
embed.title = err_name
|
||||
else:
|
||||
embed.title = self.err_name
|
||||
|
||||
if err_description:
|
||||
embed.description = err_description
|
||||
else:
|
||||
embed.description = self.err_description
|
||||
|
||||
if not self.thumbnail is None:
|
||||
embed.set_thumbnail(url = self.thumbnail)
|
||||
|
||||
if self.echo:
|
||||
print(f"{embed.title}: {embed.description}")
|
||||
if 'ephemeral' in inspect.signature(self.send_function).parameters:
|
||||
await self.send_function(embed = embed, ephemeral = self.ephemeral)
|
||||
else:
|
||||
await self.send_function(embed = embed)
|
||||
|
||||
class AdminBot(AnyBots):
|
||||
'''
|
||||
|
||||
|
||||
Admin`s bot class
|
||||
|
||||
|
||||
'''
|
||||
def __init__(self, DataBase, stop_event, *, task_start = True):
|
||||
super().__init__(DataBase)
|
||||
self.task_start = task_start
|
||||
self.stop_event = stop_event
|
||||
|
||||
async def on_ready(self):
|
||||
await super().on_ready(inherited = True)
|
||||
|
||||
if self.task_start:
|
||||
self.VoiceXpAdder.cancel()
|
||||
self.CheckDataBase.cancel()
|
||||
|
||||
self.VoiceXpAdder.start()
|
||||
self.CheckDataBase.start()
|
||||
|
||||
print(f"{datetime.datetime.now().strftime('%H:%M:%S %d-%m-%Y')}:: KrekFunLoopsBot activated")
|
||||
|
||||
async def BotOff(self):
|
||||
self.VoiceXpAdder.cancel()
|
||||
self.CheckDataBase.cancel()
|
||||
|
||||
self.stop_event.set()
|
||||
|
||||
async def on_disconnect(self):
|
||||
if self.stop_event.is_set():
|
||||
pass
|
||||
else:
|
||||
print(f"{datetime.datetime.now().strftime('%H:%M:%S %d-%m-%Y')}:: Соединение с дискордом разорвано")
|
||||
await self.BotOff()
|
||||
|
||||
async def check_bt_channel(self):
|
||||
async def moder_dataparser(data: dict):
|
||||
if not 'type' in data.keys():
|
||||
raise json.JSONDecodeError()
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['users'] as users_model:
|
||||
if data['type'] == "punishment":
|
||||
options = data['options']
|
||||
ratings = {'textmute': 10, 'voicemute': 10, 'warning': 5, 'ban': 50, 'reprimand': 15, 'newnick': 0}
|
||||
user = await session.get(users_model, options['member'], with_for_update = True)
|
||||
if user is None:
|
||||
user = users_model(id = options['member'], carma = -ratings[options['severity']])
|
||||
session.add(user)
|
||||
else:
|
||||
user.carma -= ratings[options['severity']]
|
||||
|
||||
elif data['type'] == "unpunishment":
|
||||
options = data['options']
|
||||
ratings = {'textmute': 10, 'voicemute': 10, 'warning': 5, 'ban': 50, 'reprimand': 15, 'newnick': 0}
|
||||
user = await session.get(users_model, options['member'], with_for_update = True)
|
||||
if user is None:
|
||||
user = users_model(id = options['member'], carma = ratings[options['severity']])
|
||||
session.add(user)
|
||||
else:
|
||||
user.carma += ratings[options['severity']]
|
||||
|
||||
elif data['type'] == "complaint":
|
||||
options = data['options']
|
||||
|
||||
attack_member = await session.get(users_model, options['attack_member'], with_for_update = True)
|
||||
if attack_member is None:
|
||||
attack_member = users_model(id = options['attack_member'], carma = 10 if options['accepted'] else -1)
|
||||
session.add(attack_member)
|
||||
else:
|
||||
attack_member.carma += 10 if options['accepted'] else -1
|
||||
|
||||
moderator = await session.get(users_model, options['moderator'], with_for_update = True)
|
||||
if moderator is None:
|
||||
moderator = users_model(id = options['moderator'], carma = 10)
|
||||
session.add(moderator)
|
||||
else:
|
||||
moderator.carma += 10
|
||||
|
||||
else:
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
krekchat = await self.fetch_guild(self.krekchat.id)
|
||||
bt_channel = await krekchat.fetch_channel(self.bots_talk_protocol_channel_id)
|
||||
last_msg = None
|
||||
|
||||
while True:
|
||||
stopflag = False
|
||||
batch = await bt_channel.history(limit=10, before=last_msg).flatten()
|
||||
for message in batch:
|
||||
msg_stopflag = False
|
||||
for reaction in message.reactions:
|
||||
if reaction.emoji == "✅" or reaction.emoji == "❎":
|
||||
msg_stopflag = True
|
||||
break
|
||||
else:
|
||||
try:
|
||||
data = json.loads(message.content)
|
||||
result = 0
|
||||
if not 'sender' in data:
|
||||
raise json.JSONDecodeError()
|
||||
if data['sender'] == "ModBot":
|
||||
result = await moder_dataparser(data)
|
||||
if result:
|
||||
raise json.JSONDecodeError()
|
||||
await message.add_reaction("✅")
|
||||
except json.JSONDecodeError as e:
|
||||
await message.add_reaction("❎")
|
||||
except Exception as exc:
|
||||
print(data, exc)
|
||||
if msg_stopflag:
|
||||
stopflag = True
|
||||
break
|
||||
last_msg = message
|
||||
if stopflag:
|
||||
break
|
||||
|
||||
async def give_crumbs_counter(self, incoming_crumbs: float, member, sponsor_roles, carma = 0):
|
||||
modifier = (carma / 100) + (bool(set(sponsor_roles) & set(member.roles)) + 1)
|
||||
add_crumbs = (incoming_crumbs * modifier) if incoming_crumbs * modifier >= 0 else 0
|
||||
return add_crumbs
|
||||
|
||||
async def on_message(self, msg):
|
||||
if msg.author.id == 479210801891115009 and msg.content == "botsoff":
|
||||
await msg.reply(embed=disnake.Embed(description=f'Бот отключён', colour=0xff9900))
|
||||
await self.BotOff()
|
||||
if msg.guild is None:
|
||||
return
|
||||
if msg.channel.id == self.bots_talk_protocol_channel_id:
|
||||
await self.check_bt_channel()
|
||||
if msg.author.bot:
|
||||
return
|
||||
crumb_per_word = 1 / 2
|
||||
text = msg.content
|
||||
while " " in text:
|
||||
text = text.replace(" ", " ")
|
||||
add_crumbs = len(text.split(" ")) * crumb_per_word
|
||||
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['users'] as users_model:
|
||||
stmt = self.DataBaseManager.select(users_model).where(users_model.id == msg.author.id)
|
||||
user = (await session.execute(stmt)).scalars().first()
|
||||
|
||||
if user is None:
|
||||
user = users_model(id = msg.author.id, period_messages = 1, summary_messages = 1, crumbs = (await self.give_crumbs_counter(incoming_crumbs = add_crumbs, sponsor_roles = self.sponsors, member = msg.author)))
|
||||
session.add(user)
|
||||
period_messages, period_voice_activity = 1, 0
|
||||
|
||||
else:
|
||||
stmt = self.DataBaseManager.update(users_model).where(users_model.id == msg.author.id).values(
|
||||
period_messages = users_model.period_messages + 1,
|
||||
summary_messages = users_model.summary_messages + 1,
|
||||
crumbs = users_model.crumbs + (await self.give_crumbs_counter(incoming_crumbs = add_crumbs, sponsor_roles = self.sponsors, member = msg.author, carma = user.carma))
|
||||
)
|
||||
await session.execute(stmt)
|
||||
period_messages, period_voice_activity = user.period_messages + 1, user.period_voice_activity
|
||||
|
||||
await self.LevelRolesGiver(msg.author, self.CalculateLevel(period_messages, period_voice_activity))
|
||||
|
||||
'''if msg.channel.id == 1228525235024695328:
|
||||
embed = disnake.Embed(
|
||||
title='Новые работы',
|
||||
description="\n".join("## "+str(i) for i in msg.attachments)+"\n**"+str(msg.content)+"**\nАвтор: "+str(msg.author)+" "+str(msg.author.id),
|
||||
color=0x2F3136
|
||||
)
|
||||
if len(msg.attachments)>0:
|
||||
await msg.attachments[0].save("timelycontent.png")
|
||||
embed.set_image(file = disnake.File(fp="timelycontent.png"))
|
||||
mirror = self.get_channel(1228525202107793519)
|
||||
await mirror.send(embed=embed)
|
||||
await msg.reply(embed=disnake.Embed(description=f'Ваша работа отправлена на проверку и будет опубликована в течение суток', colour=0x2F3136))
|
||||
await msg.delete()
|
||||
os.remove("timelycontent.png")'''
|
||||
|
||||
@tasks.loop(seconds=60)
|
||||
async def VoiceXpAdder(self):
|
||||
try:
|
||||
channels = await self.krekchat.fetch_channels()
|
||||
async with self.DataBaseManager.session() as session:
|
||||
for channel in channels:
|
||||
if (not isinstance(channel, disnake.VoiceChannel)) or channel.id == 1250314784914669598:
|
||||
continue
|
||||
channel = self.get_channel(channel.id)
|
||||
for member in channel.members:
|
||||
if member.bot:
|
||||
continue
|
||||
add_crumbs = 5
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['users'] as users_model:
|
||||
stmt = self.DataBaseManager.select(users_model).where(users_model.id == member.id)
|
||||
user = (await session.execute(stmt)).scalars().first()
|
||||
|
||||
if user is None:
|
||||
user = users_model(
|
||||
id = member.id,
|
||||
period_voice_activity = 60,
|
||||
summary_voice_activity = 60,
|
||||
crumbs = (await self.give_crumbs_counter(incoming_crumbs = add_crumbs, sponsor_roles = self.sponsors, member = member))
|
||||
)
|
||||
session.add(user)
|
||||
period_messages, period_voice_activity = 0, 60
|
||||
|
||||
else:
|
||||
stmt = self.DataBaseManager.update(users_model).where(users_model.id == member.id).values(
|
||||
period_voice_activity = users_model.period_voice_activity + 60,
|
||||
summary_voice_activity = users_model.summary_voice_activity + 60,
|
||||
crumbs = users_model.crumbs + (await self.give_crumbs_counter(incoming_crumbs = add_crumbs, sponsor_roles = self.sponsors, member = member, carma = user.carma))
|
||||
)
|
||||
await session.execute(stmt)
|
||||
period_messages, period_voice_activity = user.period_messages, user.period_voice_activity + 60
|
||||
await self.LevelRolesGiver(member, self.CalculateLevel(period_messages, period_voice_activity))
|
||||
|
||||
except Exception as e:
|
||||
print(f"{datetime.datetime.now().strftime('%H:%M:%S %d-%m-%Y')}:: err VoiceXpAdder: {e}")
|
||||
|
||||
@tasks.loop(seconds=3600)
|
||||
async def CheckDataBase(self):
|
||||
try:
|
||||
roles_for_delete = []
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['roles_custom'] as roles_custom:
|
||||
|
||||
stmt = self.DataBaseManager.select(roles_custom).join(roles_custom.creator).options(
|
||||
self.DataBaseManager.contains_eager(roles_custom.creator)
|
||||
).where(roles_custom.renewal_date <= datetime.datetime.now().timestamp()).with_for_update()
|
||||
roles = (await session.execute(stmt)).scalars().all()
|
||||
|
||||
for role in roles:
|
||||
if role.renewal_enabled:
|
||||
if role.creator.crumbs >= self.costrolerenewal:
|
||||
async with self.DataBaseManager.models['transaction_history_crumbs'] as transaction_history_crumbs:
|
||||
transaction = transaction_history_crumbs(sender_id = role.creator.id, amount = self.costrolerenewal, description = f"Содержание роли {role.id}")
|
||||
session.add(transaction)
|
||||
role.creator.crumbs -= self.costrolerenewal
|
||||
await role.renewal_date_update(self.TimeFormater)
|
||||
continue
|
||||
else:
|
||||
roles_for_delete.append(role.id)
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['users'] as users_model:
|
||||
stmt = self.DataBaseManager.update(users_model).where(users_model.staff_salary > 0).values(
|
||||
crumbs = users_model.crumbs+users_model.staff_salary
|
||||
)
|
||||
await session.execute(stmt)
|
||||
for role_id in roles_for_delete:
|
||||
await self.DeleteRole(role_id)
|
||||
# создание бэкапов
|
||||
backup_file = await self.DataBaseManager.pg_dump()
|
||||
krekchat = await self.fetch_guild(self.krekchat.id)
|
||||
backups_channel = await krekchat.fetch_channel(self.databases_backups_channel_id)
|
||||
if "Backup failed" in backup_file:
|
||||
await backups_channel.send(content=backup_file)
|
||||
else:
|
||||
await backups_channel.send(content=f"Бэкап бд за {datetime.datetime.now()}:", file=disnake.File(backup_file))
|
||||
|
||||
except Exception as e:
|
||||
print(f"err CheckDataBase: {e}")
|
||||
|
||||
async def init_db():
|
||||
DataBaseEngine = create_async_engine(
|
||||
config.Settings().DB_URL,
|
||||
pool_size=20,
|
||||
max_overflow=10,
|
||||
pool_recycle=300,
|
||||
pool_pre_ping=True,
|
||||
#echo=True,
|
||||
)
|
||||
async with DataBaseEngine.begin() as conn:
|
||||
await conn.run_sync(DataBaseClasses['base'].metadata.create_all)
|
||||
|
||||
return DatabaseManager(DataBaseEngine, DataBaseClasses)
|
||||
|
||||
# async def db_migration(DB_MANAGER):
|
||||
# new_DataBase = DB_MANAGER
|
||||
# DataBase = await old_DatabaseManager.connect("data/fun.db")
|
||||
# await DataBase.execute("PRAGMA journal_mode=WAL")
|
||||
# await DataBase.execute("PRAGMA synchronous=NORMAL")
|
||||
# try:
|
||||
# async with new_DataBase.engine.begin() as conn:
|
||||
# await conn.run_sync(new_DataBase.metadata.drop_all)
|
||||
# await conn.run_sync(new_DataBase.metadata.create_all)
|
||||
# async with new_DataBase.session() as session:
|
||||
# users = [DB_MANAGER.model_classes['users'](
|
||||
# id = userid, crumbs = crumbs, summary_messages = messages, summary_voice_activity = voiceactivity,
|
||||
# carma = carma, staff_salary = staffsalary, last_daily_crumbs_date = lastdailycrumbsdate
|
||||
# )
|
||||
# for userid, crumbs, messages, voiceactivity, carma, rolesinventory, staffsalary, sellingroles, ownchannels, timedroles, lastdailycrumbsdate\
|
||||
# in await DataBase.SelectBD('users')
|
||||
# ]
|
||||
# async with session.begin():
|
||||
# session.add_all(users)
|
||||
|
||||
# roles_custom = [DB_MANAGER.model_classes['roles_custom'](
|
||||
# id = roleid, creator_id = creatorid, cost = cost, renewal_date = renewaldate, renewal_enabled = renewal, date_of_creation = dateofcreation
|
||||
# )
|
||||
# for roleid, creatorid, dateofcreation, cost, sales, renewaldate, renewal in await DataBase.SelectBD('selling_roles')
|
||||
# ]
|
||||
# async with session.begin():
|
||||
# session.add_all(roles_custom)
|
||||
|
||||
# received_roles_custom = [DB_MANAGER.model_classes['received_roles_custom'](
|
||||
# role_id = roleid, user_id = int(user)
|
||||
# )
|
||||
# for roleid, creatorid, dateofcreation, cost, sales, renewaldate, renewal in await DataBase.SelectBD('selling_roles')
|
||||
# for user in sales.split(":")
|
||||
# ]
|
||||
# async with session.begin():
|
||||
# session.add_all(received_roles_custom)
|
||||
|
||||
# roles_prize = [DB_MANAGER.model_classes['roles_prize'](
|
||||
# id = roleid
|
||||
# )
|
||||
# for roleid, surrendered in await DataBase.SelectBD('prize_roles')
|
||||
# ]
|
||||
# async with session.begin():
|
||||
# session.add_all(roles_prize)
|
||||
|
||||
# received_roles_prize = [DB_MANAGER.model_classes['received_roles_prize'](
|
||||
# role_id = roleid, user_id = int(user)
|
||||
# )
|
||||
# for roleid, surrendered in await DataBase.SelectBD('prize_roles')
|
||||
# for user in surrendered.split(":")
|
||||
# ]
|
||||
# async with session.begin():
|
||||
# session.add_all(received_roles_prize)
|
||||
|
||||
# roles_static = [DB_MANAGER.model_classes['roles_static'](
|
||||
# id = roleid,
|
||||
# description = description
|
||||
# )
|
||||
# for roleid, description in await DataBase.SelectBD('uncustom_roles')
|
||||
# ]
|
||||
# async with session.begin():
|
||||
# session.add_all(roles_static)
|
||||
|
||||
# transaction_history_crumbs = [DB_MANAGER.model_classes['transaction_history_crumbs'](
|
||||
# sender_id = sender if sender != 0 else None,
|
||||
# recipient_id = recipient if recipient != 0 else None,
|
||||
# amount = amount, commission_fraction = commission,
|
||||
# description = comment, transaction_time = transactiontime
|
||||
# )
|
||||
# for sender, recipient, amount, commission, comment, transactiontime in await DataBase.SelectBD('history')
|
||||
# ]
|
||||
# async with session.begin():
|
||||
# session.add_all(transaction_history_crumbs)
|
||||
|
||||
# casino_user_account = [DB_MANAGER.model_classes['casino_user_account'](
|
||||
# id = int(userid), spins_today_count = spinstoday, last_reset_time = lastreset
|
||||
# )
|
||||
# for userid, spinstoday, lastreset in await DataBase.SelectBD('casino_limits')
|
||||
# ]
|
||||
# async with session.begin():
|
||||
# session.add_all(casino_user_account)
|
||||
|
||||
# rimagochi_users = [DB_MANAGER.model_classes['rimagochi_users'](
|
||||
# id = userid, items = json.loads(items), genes = json.loads(genes), wins = wins, settings = json.loads(settings)
|
||||
# )
|
||||
# for userid, animals, items, genes, battleslots, wins, settings in await DataBase.SelectBD('rimagochi_users')
|
||||
# if userid != 920423544691761213 and userid != 1354557962168959077
|
||||
# ]
|
||||
# async with session.begin():
|
||||
# session.add_all(rimagochi_users)
|
||||
|
||||
# rimagochi_animals = []
|
||||
# for userid, animals, items, genes, battleslots, wins, settings in await DataBase.SelectBD('rimagochi_users'):
|
||||
# rimagochi_animals += [DB_MANAGER.model_classes['rimagochi_animals'](
|
||||
# id = int(item['id'][:4]), model_animal_id = item['animal_id'], genes = item['genes'], items = item['used_items'],
|
||||
# last_feeding_time = item['last_feeding'], first_today_feed_time = item['first_feed_today_time'],
|
||||
# feed_today_count = item['feed_today'], experience = item['exp'], level = item['level'], wins = item['wins'],
|
||||
# initial_owner_id = int(item['id'].split("_")[1]), owner_id = userid, in_battle_slots = item['id'] in json.loads(battleslots).keys()
|
||||
# )
|
||||
# for key, item in json.loads(animals).items()
|
||||
# ]
|
||||
# async with session.begin():
|
||||
# session.add_all(rimagochi_animals)
|
||||
|
||||
# finally:
|
||||
# await DataBase.close()
|
||||
|
||||
# raise Exception("Миграция БД завершена. требуется переименовывание файлов")
|
||||
|
||||
|
||||
async def run_bot(bot, token, stop_event):
|
||||
try:
|
||||
await bot.start(token)
|
||||
except Exception as e:
|
||||
print(f"Бот {bot.user.name if hasattr(bot, 'user') else 'Unknown'} упал с ошибкой: {e}")
|
||||
stop_event.set() # Сигнализируем об остановке
|
||||
|
||||
async def monitor_stop(stop_event, bots):
|
||||
await stop_event.wait()
|
||||
print(f"{datetime.datetime.now().strftime('%H:%M:%S %d-%m-%Y')}:: Получен сигнал остановки, завершаю всех ботов...")
|
||||
|
||||
for bot in bots:
|
||||
if not bot.is_closed():
|
||||
try:
|
||||
await bot.close()
|
||||
except Exception as e:
|
||||
print(f"Ошибка при закрытии бота: {e}")
|
||||
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
async def main():
|
||||
stop_event = asyncio.Event()
|
||||
DataBase = None
|
||||
all_bots = []
|
||||
admin_bot = None
|
||||
|
||||
try:
|
||||
DataBase = await init_db()
|
||||
|
||||
# Инициализация ботов
|
||||
admin_bot = AdminBot(DataBase, stop_event)
|
||||
economy_bot = AnyBots(DataBase)
|
||||
rimagochi_bot = AnyBots(DataBase)
|
||||
all_bots = [economy_bot, admin_bot, rimagochi_bot]
|
||||
|
||||
# Загрузка когов
|
||||
economy_bot.load_extension("cogs.economy")
|
||||
economy_bot.load_extension("cogs.roles")
|
||||
economy_bot.load_extension("cogs.designer")
|
||||
admin_bot.load_extension("cogs.admin")
|
||||
rimagochi_bot.load_extension("cogs.rimagochi")
|
||||
|
||||
# Запуск монитора остановки и ботов
|
||||
monitor_task = asyncio.create_task(monitor_stop(stop_event, all_bots))
|
||||
bot_tasks = [
|
||||
asyncio.create_task(run_bot(economy_bot, TOKENS["KrekFunBot"], stop_event)),
|
||||
asyncio.create_task(run_bot(admin_bot, TOKENS["KrekAdminBot"], stop_event)),
|
||||
asyncio.create_task(run_bot(rimagochi_bot, TOKENS["KrekRimagochiBot"], stop_event))
|
||||
]
|
||||
|
||||
await asyncio.gather(*bot_tasks, monitor_task)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("Боты остановлены по запросу пользователя")
|
||||
except Exception as e:
|
||||
print(f"Произошла критическая ошибка: {e}")
|
||||
finally:
|
||||
await admin_bot.BotOff()
|
||||
|
||||
for bot in all_bots:
|
||||
if not bot.is_closed():
|
||||
await bot.close()
|
||||
|
||||
await DataBase.close()
|
||||
|
||||
current_task = asyncio.current_task()
|
||||
pending = [t for t in asyncio.all_tasks() if t is not current_task and not t.done()]
|
||||
for task in pending:
|
||||
task.cancel()
|
||||
await asyncio.gather(*pending, return_exceptions=True)
|
||||
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
259
src/cogs/admin.py
Normal file
259
src/cogs/admin.py
Normal file
@@ -0,0 +1,259 @@
|
||||
|
||||
import disnake
|
||||
from disnake.ext import commands
|
||||
from disnake.ext import tasks
|
||||
import requests
|
||||
import numpy as np
|
||||
import aiohttp
|
||||
|
||||
import asyncio
|
||||
import sqlite3
|
||||
import sys
|
||||
import os
|
||||
import copy
|
||||
import datetime
|
||||
import math
|
||||
import random
|
||||
import json
|
||||
import shutil
|
||||
from constants.global_constants import *
|
||||
from constants.rimagochi_constants import *
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(MainAdminModule(bot))
|
||||
|
||||
class MainAdminModule(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.DataBaseManager = client.DataBaseManager
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
print(f'KrekFunBot admin module activated')
|
||||
self.krekchat = await self.client.fetch_guild(constants["krekchat"])
|
||||
self.me = disnake.utils.get(self.krekchat.roles, id=constants["me"])
|
||||
|
||||
|
||||
@commands.slash_command(name="bot_fun_off")
|
||||
async def BotFunOff(self, ctx: disnake.ApplicationCommandInteraction):
|
||||
if self.me in ctx.author.roles:
|
||||
await ctx.send(embed=disnake.Embed(description=f'Бот отключён', colour=0xff9900), ephemeral=True)
|
||||
await self.client.BotOff()
|
||||
else:
|
||||
await ctx.send(embed=disnake.Embed(description=f'Не допустимо', colour=0xff9900), ephemeral=True)
|
||||
|
||||
|
||||
@commands.slash_command(name="зарегистрировать_призовую_роль")
|
||||
async def RegisterAPrizeRoleSlash(self, ctx, role: disnake.Role):
|
||||
await ctx.response.defer(ephemeral=True)
|
||||
if not self.me in ctx.author.roles:
|
||||
await ctx.edit_original_message(
|
||||
embed=disnake.Embed(description=f'Эта команда доступна только администратору', colour=0x2F3136))
|
||||
return
|
||||
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['roles_prize'] as roles_prize_model:
|
||||
stmt = self.DataBaseManager.select(roles_prize_model).where(roles_prize_model.id == role.id)
|
||||
role_prize = (await session.execute(stmt)).scalars().first()
|
||||
if role_prize is None:
|
||||
prize_role = roles_prize_model(id = role.id)
|
||||
session.add(prize_role)
|
||||
await ctx.edit_original_message(embed=disnake.Embed(description=f'Теперь роль {role.mention} зарегистрирована!',colour=0x2F3136))
|
||||
else:
|
||||
await ctx.edit_original_message(embed=disnake.Embed(description=f'Эта роль уже зарегистрирована!',colour=0x2F3136))
|
||||
|
||||
|
||||
|
||||
@commands.slash_command(name="выдать_призовую_роль")
|
||||
async def GiveAPrizeRoleSlash(self, ctx, role: disnake.Role, member: disnake.Member):
|
||||
await ctx.response.defer(ephemeral=True)
|
||||
if not self.me in ctx.author.roles:
|
||||
await ctx.edit_original_message(embed=disnake.Embed(description=f'Эта команда доступна только администратору', colour=0x2F3136))
|
||||
return
|
||||
|
||||
async with self.DataBaseManager.session() as session:
|
||||
await self.client.UserUpdate(member, session = session)
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['roles_prize'] as roles_prize_model:
|
||||
|
||||
stmt = self.DataBaseManager.select(roles_prize_model).where(roles_prize_model.id == role.id)
|
||||
role_prize = (await session.execute(stmt)).scalars().first()
|
||||
if role_prize is None:
|
||||
await ctx.edit_original_message(embed=disnake.Embed(description=f'Данная роль не зарегистрирована как призовая', colour=0x2F3136))
|
||||
return 1
|
||||
|
||||
async with self.DataBaseManager.models['received_roles_prize'] as received_roles_prize_model:
|
||||
stmt = self.DataBaseManager.select(received_roles_prize_model).where(
|
||||
received_roles_prize_model.role_id == role.id,
|
||||
received_roles_prize_model.user_id == member.id
|
||||
)
|
||||
received_roles_prize = (await session.execute(stmt)).scalars().first()
|
||||
if not received_roles_prize is None:
|
||||
await ctx.edit_original_message(embed=disnake.Embed(description=f'У {member.mention} уже есть роль {role.mention}', colour=0x2F3136))
|
||||
return 1
|
||||
|
||||
received_roles_prize = received_roles_prize_model(role_id = role.id, user_id = member.id)
|
||||
session.add(received_roles_prize)
|
||||
|
||||
embed = disnake.Embed(description=f'Роль {role.mention} успешно передана {member.mention}!', colour=0x2F3136)
|
||||
await member.add_roles(role)
|
||||
await ctx.edit_original_message(embed=embed)
|
||||
|
||||
|
||||
@commands.slash_command(name="забрать_призовую_роль")
|
||||
async def TakeAwayAPrizeRoleSlash(self, ctx, role: disnake.Role, member: disnake.Member):
|
||||
await ctx.response.defer(ephemeral=True)
|
||||
if not self.me in ctx.author.roles:
|
||||
await ctx.edit_original_message(embed=disnake.Embed(description=f'Эта команда доступна только администратору', colour=0x2F3136))
|
||||
return
|
||||
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['received_roles_prize'] as received_roles_prize_model:
|
||||
stmt = self.DataBaseManager.select(received_roles_prize_model).where(
|
||||
received_roles_prize_model.user_id == member.id,
|
||||
received_roles_prize_model.role_id == role.id
|
||||
).with_for_update()
|
||||
user_role = (await session.execute(stmt)).scalars().first()
|
||||
if user_role is None:
|
||||
await ctx.edit_original_message(embed=disnake.Embed(description=f'Данный пользователь не связан с этой ролью', colour=0x2F3136))
|
||||
return
|
||||
|
||||
await session.delete(user_role)
|
||||
embed = disnake.Embed(description=f'Роль {role.mention} удалена из инвентаря {member.mention}!', colour=0x2F3136)
|
||||
await member.remove_roles(role)
|
||||
await ctx.edit_original_message(embed=embed)
|
||||
|
||||
|
||||
@commands.slash_command(name="удалить_роль")
|
||||
async def DeleteRoleSlash(self, ctx: disnake.AppCmdInter, roleid: str):
|
||||
if not self.me in ctx.author.roles:
|
||||
await ctx.send(embed=disnake.Embed(description=f'Эта команда доступна только администратору', colour=0x2F3136))
|
||||
return
|
||||
try:
|
||||
res = await self.client.DeleteRole(int(roleid))
|
||||
await ctx.send(embed=disnake.Embed(description=f'{res[1]}', colour=0xff9900), ephemeral=True)
|
||||
except ValueError:
|
||||
await ctx.send(embed=disnake.Embed(description=f'Введён неверный id', colour=0xff9900), ephemeral=True)
|
||||
|
||||
|
||||
@commands.slash_command(name="установить_описание_роли")
|
||||
async def RoleDescriptoinSetSlash(self, ctx, role: disnake.Role, description: str):
|
||||
await ctx.response.defer(ephemeral=True)
|
||||
if not self.me in ctx.author.roles:
|
||||
await ctx.edit_original_message(embed=disnake.Embed(description=f'Эта команда доступна только администратору', colour=0x2F3136))
|
||||
return
|
||||
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['roles_static'] as roles_static_model:
|
||||
stmt = self.DataBaseManager.select(roles_static_model).where(
|
||||
roles_static_model.id == role.id
|
||||
).with_for_update()
|
||||
role_static = (await session.execute(stmt)).scalars().first()
|
||||
if role_static is None:
|
||||
role = roles_static_model(id = role.id, description = description)
|
||||
session.add(role)
|
||||
else:
|
||||
role_static.description = description
|
||||
await ctx.edit_original_message(embed=disnake.Embed(description=f'Для роли {role.mention} успешно установлено описание\n```{description}```', colour=0x2F3136))
|
||||
return
|
||||
|
||||
@commands.slash_command(name = "изменить_параметр")
|
||||
async def ChangeParamSlash(self, ctx: disnake.AppCmdInter, member: disnake.Member, vector: int, parameter: str = commands.Param(description="Какой параметр хотите изменить?",
|
||||
name="параметр",
|
||||
choices=['крошки', 'сообщения', 'секунды в голосовом канале', 'репутация', 'зарплата'])):
|
||||
if not self.me in ctx.author.roles:
|
||||
await ctx.edit_original_message(embed=disnake.Embed(description=f'Эта команда доступна только администратору', colour=0x2F3136))
|
||||
return
|
||||
|
||||
async with self.DataBaseManager.session() as session:
|
||||
await self.client.UserUpdate(member, session = session)
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['users'] as user_model:
|
||||
stmt = self.DataBaseManager.select(user_model).where(
|
||||
user_model.id == member.id
|
||||
).with_for_update()
|
||||
user = (await session.execute(stmt)).scalars().first()
|
||||
count = 0
|
||||
match parameter:
|
||||
case 'крошки':
|
||||
user.crumbs += vector
|
||||
count = user.crumbs
|
||||
case 'сообщения':
|
||||
user.period_messages += vector
|
||||
user.summary_messages += vector
|
||||
count = user.period_messages
|
||||
case 'секунды в голосовом канале':
|
||||
user.period_voice_activity += vector
|
||||
user.summary_voice_activity += vector
|
||||
count = user.period_voice_activity
|
||||
case 'репутация':
|
||||
user.carma += vector
|
||||
count = user.carma
|
||||
case 'зарплата':
|
||||
user.staff_salary += vector
|
||||
count = user.staff_salary
|
||||
await ctx.send(embed=disnake.Embed(description=f'Количество {parameter} пользователя {member.mention} успешно изменено до `{count}`', colour=0xff9900), ephemeral=True)
|
||||
|
||||
@commands.slash_command(name = "дать_зверя")
|
||||
async def GiveRimagochiAnimalSlash(self, ctx: disnake.AppCmdInter, member: disnake.Member, animal_id: int):
|
||||
if not self.me in ctx.author.roles:
|
||||
await ctx.edit_original_message(embed=disnake.Embed(description=f'Эта команда доступна только администратору', colour=0x2F3136))
|
||||
return
|
||||
|
||||
if not animal_id in rimagochi_animals.keys():
|
||||
await ctx.send(embed=disnake.Embed(description=f'Некорректный идентификатор животного', colour=0xff9900), ephemeral=True)
|
||||
return
|
||||
|
||||
async with self.DataBaseManager.session() as session:
|
||||
await self.client.RimagochiUserUpdate(member, session = session)
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['rimagochi_animals'] as rimagochi_animals_model:
|
||||
animal = rimagochi_animals_model(model_animal_id = animal_id, initial_owner_id = ctx.author.id, owner_id = member.id)
|
||||
session.add(animal)
|
||||
await ctx.send(embed=disnake.Embed(description=f'{member.mention} успешно получил `{rimagochi_animals[animal_id]["name"]}`', colour=0xff9900), ephemeral=True)
|
||||
|
||||
@commands.slash_command(name = "удалить_зверя")
|
||||
async def RemoveRimagochiAnimalSlash(self, ctx: disnake.AppCmdInter, inventory_id: int):
|
||||
if not self.me in ctx.author.roles:
|
||||
await ctx.edit_original_message(embed=disnake.Embed(description=f'Эта команда доступна только администратору', colour=0x2F3136))
|
||||
return
|
||||
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['rimagochi_animals'] as rimagochi_animals_model:
|
||||
stmt = self.DataBaseManager.select(rimagochi_animals_model).where(
|
||||
rimagochi_animals_model.id == inventory_id
|
||||
).with_for_update()
|
||||
animal = (await session.execute(stmt)).scalars().first()
|
||||
|
||||
if animal is None:
|
||||
await ctx.send(embed=disnake.Embed(description=f'Животного с таким идентификатором не обнаружено', colour=0x2F3136), ephemeral=True)
|
||||
return 1
|
||||
else:
|
||||
await session.delete(animal)
|
||||
await ctx.send(embed=disnake.Embed(description=f"<@{animal.owner_id}> лишился `{rimagochi_animals[animal.model_animal_id]['name']}` с id `{inventory_id}`",
|
||||
colour=0xff9900), ephemeral=True)
|
||||
return 0
|
||||
|
||||
@commands.slash_command(name = "температура")
|
||||
async def RaspberryTemperature(self, ctx: disnake.AppCmdInter):
|
||||
if not self.me in ctx.author.roles:
|
||||
await ctx.send(embed=disnake.Embed(description=f'Эта команда доступна только администратору', colour=0x2F3136))
|
||||
return
|
||||
def redgreen(temper, minimum, maximum):
|
||||
if temper == ((maximum+minimum)/2):
|
||||
return [255, 255, 0]
|
||||
if temper > ((maximum+minimum)/2):
|
||||
return [255, max(0, min(int((1-((temper-minimum)/(maximum-minimum)))*255), 255)), 0]
|
||||
if temper < ((maximum+minimum)/2):
|
||||
return [max(0, min(int(((temper-minimum)/(maximum-minimum))*255), 255)), 255, 0]
|
||||
try:
|
||||
with open('/sys/class/thermal/thermal_zone0/temp', 'r') as f:
|
||||
temp = f.read()
|
||||
except:
|
||||
temp = "0"
|
||||
await ctx.send(embed=disnake.Embed(description=f"Температура CPU {str(int(temp) / 1000)} °C", colour=disnake.Color.from_rgb(*redgreen(int(temp)/1000, 30, 70))))
|
||||
124
src/cogs/designer.py
Normal file
124
src/cogs/designer.py
Normal file
@@ -0,0 +1,124 @@
|
||||
import disnake
|
||||
from disnake.ext import commands
|
||||
from disnake.ext import tasks
|
||||
import requests
|
||||
import numpy as np
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
import copy
|
||||
import datetime
|
||||
import math
|
||||
import random
|
||||
import json
|
||||
import shutil
|
||||
import imageio
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from io import BytesIO
|
||||
import textwrap
|
||||
from constants.global_constants import *
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(MainDesignerModule(bot))
|
||||
|
||||
class MainDesignerModule(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.DataBaseManager = client.DataBaseManager
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
self.krekchat = await self.client.fetch_guild(constants["krekchat"])
|
||||
self.sponsors = [disnake.utils.get(self.krekchat.roles, id=i) for i in constants["sponsors"]]
|
||||
self.me = disnake.utils.get(self.krekchat.roles, id=constants["me"])
|
||||
print(f'KrekFunBot designer module activated')
|
||||
|
||||
@commands.slash_command(name = "профиль", description="Ваш профиль на сервере")
|
||||
async def Profile(self, ctx: disnake.AppCmdInter,
|
||||
member: disnake.Member = commands.Param(description="Чей профиль хотите посмотреть?", name="участник", default=None),
|
||||
old_style: bool = commands.Param(description="Если у вас не грузятся картинки, это лучший вариант", name="старый_стиль", default=False)):
|
||||
if not member:
|
||||
member = ctx.author
|
||||
|
||||
await ctx.response.defer()
|
||||
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['users'] as users_model:
|
||||
user = await session.get(users_model, member.id)
|
||||
|
||||
if user is None:
|
||||
await self.client.ErrorOutHelper(send_function = ctx.edit_original_message).out(n="Ошибка профиля", d=f"{'Вас' if member == ctx.author else 'Этого пользователя'} пока нет в базе данных{', напишите хотя бы одно сообщение' if member == ctx.author else ''}")
|
||||
return
|
||||
|
||||
rating = users_model.period_messages + (users_model.period_voice_activity / 180.0)
|
||||
subquery = (
|
||||
self.DataBaseManager.select(
|
||||
users_model.id,
|
||||
self.DataBaseManager.func.row_number().over(order_by=[self.DataBaseManager.desc(rating)]).label('rank')
|
||||
)
|
||||
.subquery()
|
||||
)
|
||||
stmt = (
|
||||
self.DataBaseManager.select(subquery.c.rank)
|
||||
.where(subquery.c.id == member.id)
|
||||
)
|
||||
user_rank = (await session.execute(stmt)).scalar()
|
||||
|
||||
stmt = self.DataBaseManager.select(self.DataBaseManager.models['profile_design_inventory'].m).where(
|
||||
self.DataBaseManager.models['profile_design_inventory'].m.user_id == member.id,
|
||||
self.DataBaseManager.models['profile_design_inventory'].m.is_active == True
|
||||
)
|
||||
|
||||
design = (await session.execute(stmt)).scalars().first()
|
||||
if design is None:
|
||||
design = await session.get(self.DataBaseManager.models['profile_design'].m, 1)
|
||||
else:
|
||||
design = design.design
|
||||
|
||||
if old_style:
|
||||
embed = disnake.Embed(title=f"Профиль **{member.display_name}**", description=f'')
|
||||
|
||||
embed.colour = 0x2F3136
|
||||
embed.set_thumbnail(url=member.avatar)
|
||||
|
||||
level = self.client.CalculateLevel(user.period_messages, user.period_voice_activity)
|
||||
embed.add_field(name=f"Уровень",
|
||||
value=f"`{int(level)}`\n|{'█' * int((level % 1) * 35) + '░' * (35 - int((level % 1) * 35))}|\n",
|
||||
inline=False)
|
||||
if ctx.guild is None:
|
||||
embed.add_field(name=f"Баланс ", value=f"`{int(user.crumbs)} крошек`", inline=True)
|
||||
else:
|
||||
modify = max(0, (bool(await user.in_role(roles = self.sponsors, member = member))+1) + (user.carma / 100))
|
||||
embed.add_field(name=f"Баланс " + ("" if modify == 1 else f"(x{float(modify):.02n})"), value=f"`{int(user.crumbs)} крошек`", inline=True)
|
||||
embed.add_field(name=f"Текстовая активность", value=f"`{user.period_messages} сообщений`", inline=True)
|
||||
embed.add_field(name=f"Голосовая активность", value=f"`{round(user.period_voice_activity / (60))} минут`", inline=True)
|
||||
embed.add_field(name=f"Репутация", value=f"`{int(user.carma)}`", inline=True)
|
||||
embed.add_field(name=f"Топ", value=f"`{user_rank}`", inline=True)
|
||||
if user.staff_salary != 0:
|
||||
embed.add_field(name=f"Зарплата", value=f"`{user.staff_salary:.02n} крошек в час`", inline=True)
|
||||
await ctx.edit_original_message(embed=embed)
|
||||
else:
|
||||
data = {}
|
||||
|
||||
if design.render_profile_code is None:
|
||||
await self.client.ErrorOutHelper(send_function = ctx.edit_original_message).out(n="Ошибка профиля", d=f"Для этой темы профиля не определена функция render. Свяжитесь с разработчиком для решения этой проблемы")
|
||||
return
|
||||
|
||||
avatar_asset = member.avatar or member.default_avatar
|
||||
avatar_bytes = await avatar_asset.read()
|
||||
avatar_buffer = BytesIO(avatar_bytes)
|
||||
avatar_image = Image.open(avatar_buffer).convert("RGBA")
|
||||
data['avatar'] = avatar_image
|
||||
data['user'] = user
|
||||
data['place_in_top'] = user_rank
|
||||
data['crumbs_modify'] = max(0, (bool(await user.in_role(roles = self.sponsors, member = member))+1) + (user.carma / 100)) if ctx.guild is not None else 1
|
||||
data['nick'] = member.display_name
|
||||
data['level'] = self.client.CalculateLevel(user.period_messages, user.period_voice_activity)
|
||||
namespace = globals().copy()
|
||||
|
||||
if design.type == "PNG":
|
||||
await ctx.edit_original_message(file=disnake.File(fp=design.render_profile(data, namespace), filename="profile.png"))
|
||||
elif design.type == "GIF":
|
||||
await ctx.edit_original_message(file=disnake.File(fp=design.render_profile(data, namespace), filename="profile.gif"))
|
||||
453
src/cogs/economy.py
Normal file
453
src/cogs/economy.py
Normal file
@@ -0,0 +1,453 @@
|
||||
import disnake
|
||||
from disnake.ext import commands
|
||||
from disnake.ext import tasks
|
||||
import requests
|
||||
import numpy as np
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
import copy
|
||||
import datetime
|
||||
import math
|
||||
import random
|
||||
import json
|
||||
import shutil
|
||||
from constants.global_constants import *
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(MainEconomyModule(bot))
|
||||
|
||||
class MainEconomyModule(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.DataBaseManager = client.DataBaseManager
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
self.krekchat = await self.client.fetch_guild(constants["krekchat"])
|
||||
self.sponsors = [disnake.utils.get(self.krekchat.roles, id=i) for i in constants["sponsors"]]
|
||||
self.me = disnake.utils.get(self.krekchat.roles, id=constants["me"])
|
||||
print(f'KrekFunBot economy module activated')
|
||||
|
||||
@commands.slash_command(name = "статистика", description="Статистика отображает все данные о пользователе")
|
||||
async def UserStatistic(self, ctx: disnake.AppCmdInter,
|
||||
member: disnake.Member = commands.Param(description="Чью статистику хотите посмотреть?", name="участник", default=None)):
|
||||
if not member:
|
||||
member = ctx.author
|
||||
|
||||
if ctx.guild is None:
|
||||
embed = disnake.Embed(title=f"Статистика **{member.name}**", description=f'')
|
||||
elif member.nick != None:
|
||||
embed = disnake.Embed(title=f"Статистика **{member.nick}**", description=f'')
|
||||
else:
|
||||
embed = disnake.Embed(title=f"Статистика **{member.name}**", description=f'')
|
||||
|
||||
embed.set_thumbnail(url=member.avatar)
|
||||
|
||||
await ctx.response.defer()
|
||||
|
||||
users_model = self.DataBaseManager.models['users'].m
|
||||
rimagochi_users_model = self.DataBaseManager.models['rimagochi_users'].m
|
||||
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
user = await session.get(users_model, member.id)
|
||||
if user is None:
|
||||
await self.client.ErrorOutHelper(send_function = ctx.edit_original_message, err_name = "Ошибка статистики").out(d="Такого пользователя нет в базе данных")
|
||||
return
|
||||
|
||||
stmt = self.DataBaseManager.select(users_model).options(
|
||||
self.DataBaseManager.selectinload(users_model.custom_roles),
|
||||
self.DataBaseManager.joinedload(users_model.creation_role),
|
||||
self.DataBaseManager.selectinload(users_model.prize_roles),
|
||||
self.DataBaseManager.selectinload(users_model.sender_in_crumbs_transactions),
|
||||
self.DataBaseManager.selectinload(users_model.recipient_in_crumbs_transactions),
|
||||
self.DataBaseManager.joinedload(users_model.casino_account),
|
||||
).where(
|
||||
users_model.id == member.id
|
||||
)
|
||||
user = (await session.execute(stmt)).scalars().first()
|
||||
|
||||
stmt = self.DataBaseManager.select(rimagochi_users_model).options(
|
||||
self.DataBaseManager.selectinload(rimagochi_users_model.animals),
|
||||
self.DataBaseManager.selectinload(rimagochi_users_model.battle_slots_animals)
|
||||
).where(
|
||||
rimagochi_users_model.id == member.id
|
||||
)
|
||||
rimagochi_user = (await session.execute(stmt)).scalars().first()
|
||||
|
||||
embed.add_field(name=f"Модуль экономики", value=f"", inline=False)
|
||||
embed.add_field(name=f"крошки", value=f"{user.crumbs}", inline=False)
|
||||
embed.add_field(name=f"сообщения (в общем)", value=f"{user.summary_messages}", inline=False)
|
||||
embed.add_field(name=f"минут в голосовом канале (в общем)", value=f"{user.summary_voice_activity/60}", inline=False)
|
||||
embed.add_field(name=f"сообщения (после сброса)", value=f"{user.period_messages}", inline=False)
|
||||
embed.add_field(name=f"минут в голосовом канале (после сброса)", value=f"{user.period_voice_activity/60}", inline=False)
|
||||
embed.add_field(name=f"репутация", value=f"{user.carma}", inline=False)
|
||||
embed.add_field(name=f"зарплата", value=f"{user.staff_salary}", inline=False)
|
||||
embed.add_field(name=f"время получения последней ежедневной награды", value=f"<t:{int(user.last_daily_crumbs_date)}:F>", inline=False)
|
||||
embed.add_field(name=f"время последней активности", value=f"<t:{int(user.last_activity_date)}:F>", inline=False)
|
||||
embed.add_field(name=f"количество записей в истории об отправлениях (общая сумма)", value=f"{len(user.sender_in_crumbs_transactions)} ({sum([write.amount for write in user.sender_in_crumbs_transactions]+[0])})", inline=False)
|
||||
embed.add_field(name=f"количество записей в истории о получениях (общая сумма)", value=f"{len(user.recipient_in_crumbs_transactions)} ({sum([write.amount for write in user.recipient_in_crumbs_transactions]+[0])})", inline=False)
|
||||
embed.add_field(name=f"наличие записей в казино", value=f"{not user.casino_account is None}", inline=False)
|
||||
if not user.casino_account is None:
|
||||
embed.add_field(name=f"количество круток сегодня (может быть не актуальным)", value=f"{user.casino_account.spins_today_count}", inline=False)
|
||||
embed.add_field(name=f"время последнего сброса лимитов", value=f"<t:{int(user.casino_account.last_reset_time)}:F>", inline=False)
|
||||
|
||||
embed.add_field(name=f"Ролевой модуль", value=f"", inline=False)
|
||||
embed.add_field(name=f"количество кастомных ролей (общая стоимость)", value=f"{len(user.custom_roles)} ({sum([role.cost for role in user.custom_roles]+[0])})", inline=False)
|
||||
embed.add_field(name=f"количество призовых ролей", value=f"{len(user.prize_roles)}", inline=False)
|
||||
embed.add_field(name=f"созданная кастомная роль", value=(f"<@&{user.creation_role.id}>" if not user.creation_role is None else "None"), inline=False)
|
||||
|
||||
embed.add_field(name=f"Модуль rimagochi", value=f"", inline=False)
|
||||
embed.add_field(name=f"наличие аккаунта rimagochi", value=f"{not rimagochi_user is None}", inline=False)
|
||||
if not rimagochi_user is None:
|
||||
embed.add_field(name=f"количество зверей", value=f"{len(rimagochi_user.animals)}", inline=False)
|
||||
embed.add_field(name=f"количество генов", value=f"{sum([int(i) for i in rimagochi_user.genes.values()])}", inline=False)
|
||||
embed.add_field(name=f"количество предметов", value=f"{sum([int(i) for i in rimagochi_user.items.values()])}", inline=False)
|
||||
embed.add_field(name=f"количество побед", value=f"{rimagochi_user.wins}", inline=False)
|
||||
await ctx.edit_original_message(embed=embed)
|
||||
|
||||
@commands.slash_command(description="Показывает топ участников сервера по заданному параметру (по умолчанию - уровень)", name="топ")
|
||||
async def TopSlash(self, ctx: disnake.AppCmdInter, parameter: str = commands.Param(description="По какому критерию хотите увидеть топ?",
|
||||
name="критерий", default = 'по уровню',
|
||||
choices=['по уровню', 'по количеству сообщений', 'по времени в голосовых каналах', 'по количеству крошек'])):
|
||||
await ctx.response.defer()
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['users'] as users_model:
|
||||
order_by = None
|
||||
match parameter:
|
||||
case "по уровню":
|
||||
order_by = [self.DataBaseManager.desc(users_model.period_messages + (users_model.period_voice_activity / 180))]
|
||||
case "по количеству сообщений":
|
||||
order_by = [self.DataBaseManager.desc(users_model.period_messages), self.DataBaseManager.desc(users_model.period_voice_activity)]
|
||||
case "по времени в голосовых каналах":
|
||||
order_by = [self.DataBaseManager.desc(users_model.period_voice_activity), self.DataBaseManager.desc(users_model.period_messages)]
|
||||
case "по количеству крошек":
|
||||
order_by = [self.DataBaseManager.desc(users_model.crumbs), self.DataBaseManager.desc(users_model.period_messages), self.DataBaseManager.desc(users_model.period_voice_activity)]
|
||||
stmt = self.DataBaseManager.select(users_model).order_by(*order_by).limit(20)
|
||||
users = (await session.execute(stmt)).scalars().all()
|
||||
|
||||
embed = disnake.Embed(title=f"Общий топ {parameter}", description=f'', colour=0x2F3136)
|
||||
users_counter = 0
|
||||
for user in users:
|
||||
try:
|
||||
member = await self.krekchat.fetch_member(user.id)
|
||||
except disnake.NotFound:
|
||||
continue
|
||||
member_name = None
|
||||
if member.nick != None:
|
||||
member_name = member.nick
|
||||
elif member.display_name != None:
|
||||
member_name = member.display_name
|
||||
else:
|
||||
member_name = member.name
|
||||
match parameter:
|
||||
case "по уровню":
|
||||
embed.add_field(name=f"{users_counter + 1}) {member_name}",
|
||||
value=f"{member.mention} - {int(self.client.CalculateLevel(user.period_messages, user.period_voice_activity))} уровень",
|
||||
inline=False)
|
||||
case "по количеству сообщений":
|
||||
embed.add_field(name=f"{users_counter + 1}) {member_name}",
|
||||
value=f"{member.mention} - {user.period_messages} сообщений",
|
||||
inline=False)
|
||||
case "по времени в голосовых каналах":
|
||||
embed.add_field(name=f"{users_counter + 1}) {member_name}",
|
||||
value=f"{member.mention} - {int(user.period_voice_activity/60)} минут",
|
||||
inline=False)
|
||||
case "по количеству крошек":
|
||||
embed.add_field(name=f"{users_counter + 1}) {member_name}",
|
||||
value=f"{member.mention} - {int(user.crumbs)} крошек",
|
||||
inline=False)
|
||||
users_counter += 1
|
||||
|
||||
if users_counter >= 10:
|
||||
break
|
||||
|
||||
await ctx.edit_original_message(embed=embed)
|
||||
|
||||
|
||||
@commands.slash_command(name="ежедневная")
|
||||
async def DailySlash(self, ctx):
|
||||
pass
|
||||
@DailySlash.sub_command(
|
||||
description=f"Получайте награду в крошках каждый день",
|
||||
name="награда")
|
||||
async def DailyCrumbsSub(self, ctx: disnake.AppCmdInter):
|
||||
await ctx.response.defer()
|
||||
error_helper = self.client.ErrorOutHelper(send_function = ctx.edit_original_message, err_name = "Ошибка начисления ежедневной награды")
|
||||
if ctx.guild is None:
|
||||
await error_helper.out(d="Эта команда не работает в личных сообщениях!")
|
||||
return
|
||||
async with self.DataBaseManager.session() as session:
|
||||
await self.client.UserUpdate(member = ctx.author, session = session)
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['users'] as users_model:
|
||||
stmt = self.DataBaseManager.select(users_model).where(users_model.id == ctx.author.id).with_for_update()
|
||||
user = (await session.execute(stmt)).scalars().first()
|
||||
|
||||
result = await user.claim_daily_crumbs(daily_constant = constants['dailycrumbs'], member = ctx.author, sponsor_roles = self.sponsors)
|
||||
|
||||
if result['success']:
|
||||
history = self.DataBaseManager.models['transaction_history_crumbs'].m(recipient_id = ctx.author.id, amount = result['count'], description = f"Ежедневная награда за {result['date'].strftime('%d.%m.%Y')}")
|
||||
session.add(history)
|
||||
await ctx.edit_original_message(embed=disnake.Embed(description=result['output'], colour=0x2F3136))
|
||||
return
|
||||
else:
|
||||
await error_helper.out(d=result['output'])
|
||||
return
|
||||
|
||||
|
||||
@commands.slash_command(name="история", description="Показывает историю транзакций")
|
||||
async def HistoryCrumbsSlash(self, ctx: disnake.AppCmdInter):
|
||||
pass
|
||||
@HistoryCrumbsSlash.sub_command(description="Показывает историю транзакций", name="транзакций")
|
||||
async def HistoryCrumbsSub(self, ctx: disnake.AppCmdInter, member: disnake.Member = commands.Param(
|
||||
description="Чью историю хотите посмотреть?(только для администраторов)",
|
||||
name="участник", default=None)):
|
||||
if not (member != None and self.me in ctx.author.roles):
|
||||
member = ctx.author
|
||||
class HistoryButtons(disnake.ui.View):
|
||||
def __init__(self, ctx, transactions, member, embed):
|
||||
super().__init__(timeout=180)
|
||||
self.transactions = transactions
|
||||
self.ctx = ctx
|
||||
self.embed = embed
|
||||
self.member = member
|
||||
self.page = 1
|
||||
self.maxpage = len(transactions) if len(transactions) > 0 else 1
|
||||
|
||||
self.left.disabled = (self.page == 1)
|
||||
self.right.disabled = (self.page == self.maxpage)
|
||||
|
||||
@disnake.ui.button(label="<", custom_id="left", style=disnake.ButtonStyle.secondary)
|
||||
async def left(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
return
|
||||
self.page -= 1
|
||||
self.left.disabled = (self.page == 1)
|
||||
self.right.disabled = (self.page == self.maxpage)
|
||||
self.embed = await EmbedHistoryChanger(self.transactions, self.embed, self.page, self.member)
|
||||
await inter.response.edit_message(embed=self.embed, view=self)
|
||||
|
||||
@disnake.ui.button(label=">", custom_id="right", style=disnake.ButtonStyle.secondary)
|
||||
async def right(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
return
|
||||
self.page += 1
|
||||
self.left.disabled = (self.page == 1)
|
||||
self.right.disabled = (self.page == self.maxpage)
|
||||
self.embed = await EmbedHistoryChanger(self.transactions, self.embed, self.page, self.member)
|
||||
await inter.response.edit_message(embed=self.embed, view=self)
|
||||
|
||||
async def on_timeout(self):
|
||||
for child in self.children:
|
||||
if isinstance(child, (disnake.ui.Button, disnake.ui.BaseSelect)):
|
||||
child.disabled = True
|
||||
await self.ctx.edit_original_message(view=self)
|
||||
async def EmbedHistoryChanger(transactions, embed, selfpage, member):
|
||||
embed.clear_fields()
|
||||
if len(transactions) == 0:
|
||||
embed.add_field(name=f"В истории пока ничего нет", value=f"", inline=False)
|
||||
return embed
|
||||
embed.add_field(name=f"", value=f"", inline=False)
|
||||
page = transactions[selfpage - 1]
|
||||
maxpage = len(transactions) if len(transactions) > 0 else 1
|
||||
c = 1
|
||||
for transaction in transactions[selfpage - 1]:
|
||||
if transaction.recipient_id == member.id:
|
||||
string = (
|
||||
f"{c+5*(selfpage-1)}) <:A_g_wplus:606920277443608593> **{int(transaction.amount)} крошек** {f' (комиссия: {int(transaction.commission_fraction * 100)}%)'if transaction.commission_fraction != 0 else ''}\n"
|
||||
f"{f'Отправитель: <@{transaction.sender_id}>' if not transaction.sender_id is None else ''}\n"
|
||||
f"Время транзакции: <t:{int(transaction.transaction_time)}:f>\n"
|
||||
f"> {transaction.description}"
|
||||
)
|
||||
else:
|
||||
string = (
|
||||
f"{c+5*(selfpage-1)}) <:A_g_wmins:606920287044239362> **{int(transaction.amount)} крошек** {f' (комиссия: {int(transaction.commission_fraction * 100)}%)'if transaction.commission_fraction != 0 else ''}\n"
|
||||
f"{f'Получатель: <@{transaction.recipient_id}>' if not transaction.recipient_id is None else ''}\n"
|
||||
f"Время транзакции: <t:{int(transaction.transaction_time)}:f>\n"
|
||||
f"> {transaction.description}"
|
||||
)
|
||||
embed.add_field(name=f"", value=string, inline=False)
|
||||
|
||||
c += 1
|
||||
embed.add_field(name=f"", value=f"Страница {selfpage}/{maxpage}", inline=False)
|
||||
return embed
|
||||
await ctx.response.defer(ephemeral = True)
|
||||
embed = disnake.Embed(title=f'История транзакций {member.name}', description=f"", colour=0x2F3136)
|
||||
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['transaction_history_crumbs'] as transaction_history_crumbs_model:
|
||||
stmt = self.DataBaseManager.select(transaction_history_crumbs_model).where(
|
||||
self.DataBaseManager.or_(
|
||||
transaction_history_crumbs_model.sender_id == member.id,
|
||||
transaction_history_crumbs_model.recipient_id == member.id
|
||||
)
|
||||
).order_by(self.DataBaseManager.desc(transaction_history_crumbs_model.id))
|
||||
transactions = (await session.execute(stmt)).scalars().all()
|
||||
|
||||
readyarray = self.client.PartitioningEmbeder(transactions)
|
||||
view = HistoryButtons(ctx, readyarray, member, embed)
|
||||
|
||||
embed = await EmbedHistoryChanger(readyarray, embed, 1, member)
|
||||
await ctx.edit_original_message(embed=embed, view=view)
|
||||
|
||||
|
||||
@commands.slash_command(name="казино",
|
||||
description="Хотите иметь очень много крошек? Тогда, эта команда точно не для вас)")
|
||||
async def CasinoSlash(self, ctx: disnake.AppCmdInter,
|
||||
count: commands.Range[int, 1, ...] = commands.Param(description="Сколько крошек хотите поставить на кон?", name="ставка"),
|
||||
possibility: commands.Range[int, 1, 99] = commands.Param(
|
||||
description="Шанс на победу. Чем выше, тем меньше крошек вы получите после победы(целое число от 1 до 99)",
|
||||
name="шанс", default=50),
|
||||
quantity: int = commands.Param(
|
||||
description=f"Количество круток(целое число от 1 до {constants['casinospinslimit']}({constants['casinospinslimit']*2} для спонсоров))",
|
||||
name="количество", default=1)):
|
||||
await ctx.response.defer()
|
||||
error_helper = self.client.ErrorOutHelper(send_function = ctx.edit_original_message, err_name = "Ошибка казино")
|
||||
if ctx.guild is None:
|
||||
await error_helper.out(d="Эта команда не работает в личных сообщениях!")
|
||||
return
|
||||
def get_dynamic_fee(count: int) -> float:
|
||||
if count < 1_000:
|
||||
return 0
|
||||
elif count < 5_000:
|
||||
return 0.1
|
||||
elif count < 10_000:
|
||||
return 0.2
|
||||
elif count < 25_000:
|
||||
return 0.3
|
||||
elif count < 50_000:
|
||||
return 0.4
|
||||
elif count < 75_000:
|
||||
return 0.5
|
||||
elif count < 100_000:
|
||||
return 0.6
|
||||
elif count < 125_000:
|
||||
return 0.7
|
||||
elif count < 150_000:
|
||||
return 0.8
|
||||
else:
|
||||
return 0.9
|
||||
def calculate_multiplier(possibility: float, count: int) -> float:
|
||||
fee = get_dynamic_fee(count)
|
||||
multiplier = ((0.909/possibility)-0.918)*(1-fee)
|
||||
return multiplier*count
|
||||
|
||||
async with self.DataBaseManager.session() as session:
|
||||
await self.client.CasinoUserUpdate(member = ctx.author, session = session)
|
||||
async with session.begin():
|
||||
users_model = self.DataBaseManager.models['users'].m
|
||||
casino_account_model = self.DataBaseManager.models['casino_user_account'].m
|
||||
stmt = self.DataBaseManager.select(users_model).where(users_model.id == ctx.author.id).with_for_update()
|
||||
user = (await session.execute(stmt)).scalars().first()
|
||||
stmt = self.DataBaseManager.select(casino_account_model).where(casino_account_model.id == ctx.author.id).with_for_update()
|
||||
casino_user = (await session.execute(stmt)).scalars().first()
|
||||
user_is_sponsor = await user.in_role(member = ctx.author, roles = self.sponsors)
|
||||
casinospinslimit = constants['casinospinslimit']
|
||||
if quantity <= 0:
|
||||
await error_helper.out(d=f'Количество круток должно быть > 0')
|
||||
return
|
||||
if quantity > casinospinslimit * (user_is_sponsor + 1) and (not self.me in ctx.author.roles):
|
||||
await error_helper.out(d=f'Вы не можете крутить казино больше {casinospinslimit * (user_is_sponsor + 1)} раз в день!')
|
||||
return
|
||||
if not 0 < possibility < 100:
|
||||
await error_helper.out(d=f'Шанс должен быть от 1% до 99%')
|
||||
return
|
||||
possibility = possibility / 100
|
||||
if count <= 0:
|
||||
await error_helper.out(d=f'Ставка должна быть больше 0')
|
||||
return
|
||||
|
||||
if user.crumbs < count * quantity:
|
||||
await error_helper.out(d=f'Для такой ставки вам необходимо иметь {quantity*count} крошек')
|
||||
return
|
||||
|
||||
now = datetime.datetime.now().timestamp()
|
||||
|
||||
if now - casino_user.last_reset_time >= 86400:
|
||||
casino_user.spins_today_count = 0
|
||||
casino_user.last_reset_time = now
|
||||
|
||||
if casino_user.spins_today_count + quantity > casinospinslimit * (user_is_sponsor + 1) and (not self.me in ctx.author.roles):
|
||||
if casino_user.spins_today_count >= casinospinslimit * (user_is_sponsor + 1):
|
||||
await error_helper.out(d=f'У вас не осталось круток, возобновление лимита будет <t:{casino_user.last_reset_time + 86400}:R>')
|
||||
else:
|
||||
await error_helper.out(d=f'У вас осталось только {casinospinslimit * (user_is_sponsor + 1) - (casino_user.spins_today_count)} круток, возобновление лимита будет <t:{casino_user.last_reset_time + 86400}:R>')
|
||||
return
|
||||
|
||||
results = [random.random() < possibility for _ in range(quantity)]
|
||||
totalwins = sum(results)
|
||||
totalcrumbs = totalwins * calculate_multiplier(possibility, count) - (quantity - totalwins) * count
|
||||
|
||||
casino_user.spins_today_count += quantity
|
||||
|
||||
user.crumbs += totalcrumbs
|
||||
if int(totalcrumbs)>0:
|
||||
history = self.DataBaseManager.models['transaction_history_crumbs'].m(recipient_id = ctx.author.id, amount = int(totalcrumbs), description = f"Выигрыш в казино с шансом {int(possibility * 100)}% и ставкой {count} крошек ({totalwins} побед, {quantity-totalwins} поражений)")
|
||||
session.add(history)
|
||||
embed=disnake.Embed(title=f'Вы в плюсе!', description=f"Ваш выигрыш составил {int(totalcrumbs)}!", colour=0x2F3136)
|
||||
elif int(totalcrumbs) == 0:
|
||||
embed=disnake.Embed(title=f'Вы вышли в 0',description=f"", colour=0x2F3136)
|
||||
else:
|
||||
history = self.DataBaseManager.models['transaction_history_crumbs'].m(sender_id = ctx.author.id, amount = int(abs(totalcrumbs)), description = f"Проигрыш в казино с шансом {int(possibility * 100)}% и ставкой {count} крошек ({totalwins} побед, {quantity-totalwins} поражений)")
|
||||
session.add(history)
|
||||
embed=disnake.Embed(title=f'Вы в минусе(', description=f"Ваш проигрыш составил {int(-totalcrumbs)}", colour=0x2F3136)
|
||||
|
||||
embed.description += (f"\n-# У вас осталось {casinospinslimit * (user_is_sponsor + 1) - (casino_user.spins_today_count)} круток")
|
||||
await ctx.edit_original_message(embed = embed)
|
||||
|
||||
|
||||
@commands.slash_command(name="отдать")
|
||||
async def GiveCrumbsSlash(self, ctx):
|
||||
pass
|
||||
@GiveCrumbsSlash.sub_command(description=f"Передайте свои крошки другому участнику сервера (комиссия {int(constants['givecrumbscommission']*100)}%)", name="крошки")
|
||||
async def GiveCrumbsSub(self, ctx: disnake.AppCmdInter,
|
||||
member: disnake.Member = commands.Param(description="Кому вы хотите перевести крошки?",
|
||||
name="адресат"),
|
||||
count: int = commands.Param(description="Сколько крошек вы хотите перевести?",
|
||||
name="количество"),
|
||||
comment: str = commands.Param(description="Комментарий к переводу", name="комментарий",
|
||||
default="Перевод"),
|
||||
commission: float = commands.Param(description="Доля комиссии(доступно только админам)",
|
||||
name="комиссия", default=None)):
|
||||
error_helper = self.client.ErrorOutHelper(send_function = ctx.response.send_message, err_name = "Ошибка перевода")
|
||||
if ctx.guild is None:
|
||||
await error_helper.out(d="Эта команда не работает в личных сообщениях!")
|
||||
return
|
||||
if comment != "Перевод":
|
||||
comment = "Перевод | " + comment
|
||||
if (commission == None) or (not self.me in ctx.author.roles):
|
||||
commission = constants['givecrumbscommission']
|
||||
if count <= 0:
|
||||
await error_helper.out(d="Количество крошек должно быть больше 0")
|
||||
return
|
||||
users_model = self.DataBaseManager.models['users'].m
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
author_data = await session.get(users_model, ctx.author.id, with_for_update = True)
|
||||
member_data = await session.get(users_model, member.id, with_for_update = True)
|
||||
|
||||
if author_data is None:
|
||||
await error_helper.out(d=f'Вас нет в базе данных')
|
||||
return
|
||||
|
||||
if member_data is None:
|
||||
await error_helper.out(d=f'Такого пользователя нет в базе данных')
|
||||
return
|
||||
|
||||
if author_data.crumbs - count < 0:
|
||||
await error_helper.out(d=f'У вас не хватает крошек')
|
||||
return
|
||||
|
||||
history_write = self.DataBaseManager.models["transaction_history_crumbs"].m(sender_id = ctx.author.id, recipient_id = member.id, commission_fraction = commission, description = comment, amount = count)
|
||||
session.add(history_write)
|
||||
author_data.crumbs -= count
|
||||
member_data.crumbs += count * (1 - commission)
|
||||
|
||||
await ctx.response.send_message(embed=disnake.Embed(
|
||||
description=f'{count} крошек успешно отправлено на счёт {member.mention}, комиссия составила {int(commission * 100)}%({int(count * commission)} крошек)',
|
||||
colour=0x2F3136))
|
||||
14
src/cogs/resetsupcommands.py
Normal file
14
src/cogs/resetsupcommands.py
Normal file
@@ -0,0 +1,14 @@
|
||||
import disnake
|
||||
from disnake.ext import commands
|
||||
|
||||
def setup(bot: commands.Bot):
|
||||
bot.add_cog(ExampleCog(bot))
|
||||
print("ExampleCog загружен!")
|
||||
|
||||
class ExampleCog(commands.Cog):
|
||||
def __init__(self, bot: commands.Bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.slash_command(name="ping", description="Проверка бота на работоспособность")
|
||||
async def ping(self, inter: disnake.ApplicationCommandInteraction):
|
||||
await inter.response.send_message(f"Понг! {round(self.bot.latency * 1000)}мс")
|
||||
1247
src/cogs/rimagochi.py
Normal file
1247
src/cogs/rimagochi.py
Normal file
File diff suppressed because it is too large
Load Diff
927
src/cogs/roles.py
Normal file
927
src/cogs/roles.py
Normal file
@@ -0,0 +1,927 @@
|
||||
|
||||
import disnake
|
||||
from disnake.ext import commands
|
||||
from disnake.ext import tasks
|
||||
import requests
|
||||
import numpy as np
|
||||
import aiohttp
|
||||
|
||||
import asyncio
|
||||
import sqlite3
|
||||
import sys
|
||||
import os
|
||||
import copy
|
||||
import datetime
|
||||
import math
|
||||
import random
|
||||
import json
|
||||
import shutil
|
||||
from constants.global_constants import *
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(MainRolesModule(bot))
|
||||
|
||||
|
||||
class MainRolesModule(commands.Cog):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self.DataBaseManager = client.DataBaseManager
|
||||
self.costrolecreate = client.costrolecreate
|
||||
self.costrolerenewal = client.costrolerenewal
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
print(f'KrekFunBot roles module activated')
|
||||
krekchat = await self.client.fetch_guild(constants["krekchat"])
|
||||
self.me = disnake.utils.get(krekchat.roles, id=constants["me"])
|
||||
|
||||
|
||||
@commands.slash_command(name="создать")
|
||||
async def CreateSlash(self, ctx):
|
||||
pass
|
||||
@CreateSlash.sub_command(
|
||||
description=f"создайте свою роль за {constants['costrolecreate']} крошек (ежемесячная оплата за роль:{constants['costrolerenewal']})",
|
||||
name="роль")
|
||||
async def CreateRoleSub(self, ctx: disnake.AppCmdInter,
|
||||
name: str = commands.Param(description="Название роли", name="название"),
|
||||
cost: int = commands.Param(description="цена роли для других участников в магазине", name="цена"),
|
||||
colour: disnake.Colour = commands.Param(
|
||||
description="цвет роли (указывается в hex формате, например: #ff9900)",
|
||||
name="цвет", default=0xff9900)):
|
||||
await ctx.response.defer()
|
||||
error_helper = self.client.ErrorOutHelper(send_function = ctx.edit_original_message, err_name = "Ошибка создания роли")
|
||||
if ctx.guild is None:
|
||||
await error_helper.out(d="Эта команда не работает в личных сообщениях!")
|
||||
return
|
||||
async with self.DataBaseManager.session() as session:
|
||||
await self.client.UserUpdate(member = ctx.author, session = session)
|
||||
received_roles_custom_model = self.DataBaseManager.model_classes['received_roles_custom']
|
||||
roles_custom_model = self.DataBaseManager.model_classes['roles_custom']
|
||||
users_model = self.DataBaseManager.model_classes['users']
|
||||
transaction_history_crumbs = self.DataBaseManager.model_classes['transaction_history_crumbs']
|
||||
async with session.begin():
|
||||
stmt = self.DataBaseManager.select(roles_custom_model).where(roles_custom_model.creator_id == ctx.author.id)
|
||||
custom_role = (await session.execute(stmt)).scalars().first()
|
||||
|
||||
if not custom_role is None:
|
||||
await error_helper.out(d=f'Вы уже владеете кастомной ролью')
|
||||
return
|
||||
|
||||
krekchat = await self.client.fetch_guild(constants["krekchat"])
|
||||
bordertop = krekchat.get_role(1221400083736690698)
|
||||
borderbottom = krekchat.get_role(1221400071908753480)
|
||||
|
||||
if not (100 < cost < 2147483647):
|
||||
await error_helper.out(d=f"Минимальная стоимость: 100 крошек")
|
||||
return
|
||||
|
||||
if disnake.utils.get(ctx.guild.roles, name=name):
|
||||
await error_helper.out(d=f"Роль с таким названием уже есть на сервере")
|
||||
return
|
||||
|
||||
stmt = self.DataBaseManager.select(self.DataBaseManager.func.count()).select_from(roles_custom_model)
|
||||
count = (await session.execute(stmt)).scalars().first()
|
||||
if count >= 50:
|
||||
await error_helper.out(d=f"На сервере сейчас максимальное количество кастомных ролей ({count}), попробуйте позже")
|
||||
return
|
||||
|
||||
async with session.begin():
|
||||
stmt = self.DataBaseManager.select(users_model).where(users_model.id == ctx.author.id).with_for_update()
|
||||
user = (await session.execute(stmt)).scalars().first()
|
||||
if user.crumbs < self.costrolecreate:
|
||||
await error_helper.out(d=f"Для создания роли необходимо заплатить {self.costrolecreate} крошек, а у вас есть только {round(user.crumbs)}")
|
||||
return
|
||||
|
||||
role = await ctx.guild.create_role(name=name, colour=colour)
|
||||
await role.edit(position=bordertop.position - 1)
|
||||
await ctx.author.add_roles(role)
|
||||
custom_role = roles_custom_model(id = role.id, creator_id = ctx.author.id, cost = cost, renewal_date = 0)
|
||||
await custom_role.renewal_date_update(self.client.TimeFormater)
|
||||
session.add(custom_role)
|
||||
transaction = transaction_history_crumbs(sender_id = ctx.author.id, amount = self.costrolecreate, description = f"Создание кастомной роли {role.mention}")
|
||||
session.add(transaction)
|
||||
user.crumbs -= self.costrolecreate
|
||||
await session.flush()
|
||||
received_role_custom = received_roles_custom_model(role_id = role.id, user_id = ctx.author.id)
|
||||
session.add(received_role_custom)
|
||||
await ctx.edit_original_message(embed=disnake.Embed(
|
||||
title=f"Роль успешно создана! Вы можете проверить её статус или поменять настройки через команду /изменить роль",
|
||||
description=f'', colour=0x2F3136))
|
||||
|
||||
|
||||
@commands.slash_command(name="роль")
|
||||
async def RoleInfoSlash(self, ctx):
|
||||
pass
|
||||
@RoleInfoSlash.sub_command(description="Показывает информацию о ролях", name="инфо")
|
||||
async def RoleInfoSub(self, ctx: disnake.AppCmdInter,
|
||||
role: disnake.Role = commands.Param(description="О какой роли хотите получить информацию?",
|
||||
name="роль", default=None)):
|
||||
await ctx.response.defer()
|
||||
error_helper = self.client.ErrorOutHelper(send_function = ctx.edit_original_message, err_name = "Ошибка")
|
||||
krekchat = await self.client.fetch_guild(constants["krekchat"])
|
||||
roles_custom_model = self.DataBaseManager.model_classes['roles_custom']
|
||||
roles_static_model = self.DataBaseManager.model_classes['roles_static']
|
||||
custom_role = None
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
if role is None:
|
||||
stmt = self.DataBaseManager.select(roles_custom_model).options(self.DataBaseManager.selectinload(roles_custom_model.users)).where(roles_custom_model.creator_id == ctx.author.id)
|
||||
custom_role = (await session.execute(stmt)).scalars().first()
|
||||
if custom_role is None:
|
||||
await error_helper.out(d=f'Вы не создавали кастомных ролей.\nЧтобы получить информацию о статичной роли, введите целевую роль в поле "роль" при вызове команды "/роль инфо"')
|
||||
return
|
||||
role = krekchat.get_role(custom_role.id)
|
||||
if role is None:
|
||||
await error_helper.out(d=f'Вашей роли нет на сервере, вероятно, произошла ошибка, обратитесь к администратору')
|
||||
return
|
||||
|
||||
else:
|
||||
stmt = self.DataBaseManager.select(roles_static_model).where(roles_static_model.id == role.id)
|
||||
static_role = (await session.execute(stmt)).scalars().first()
|
||||
if not static_role is None:
|
||||
embed = disnake.Embed(title=f"", description=f'### Информация о статичной роли {role.mention}', colour=0x2F3136)
|
||||
embed.add_field(name=f"Описание", value=f"> {static_role.description}", inline=True)
|
||||
await ctx.edit_original_message(embed=embed)
|
||||
return
|
||||
else:
|
||||
stmt = self.DataBaseManager.select(roles_custom_model).options(self.DataBaseManager.selectinload(roles_custom_model.users)).where(roles_custom_model.id == role.id)
|
||||
custom_role = (await session.execute(stmt)).scalars().first()
|
||||
if custom_role is None:
|
||||
await error_helper.out(d=f'Описания этой роли пока не существует.')
|
||||
return
|
||||
|
||||
embed = disnake.Embed(title=f"", description=f'### Информация о кастомной роли {role.mention}', colour=0x2F3136)
|
||||
embed.add_field(name=f"Время продления", value=f"> <t:{int(custom_role.renewal_date)}:D>", inline=True)
|
||||
embed.add_field(name=f"Цена продления", value=f"> {constants['costrolerenewal']}", inline=True)
|
||||
embed.add_field(name=f"", value=f"", inline=False)
|
||||
embed.add_field(name=f"Количество покупок", value=f"> {len(custom_role.users)-1}", inline=True)
|
||||
embed.add_field(name=f"Цена роли", value=f"> {custom_role.cost}", inline=True)
|
||||
embed.add_field(name=f"", value=f"", inline=False)
|
||||
try:
|
||||
creator = await krekchat.fetch_member(custom_role.creator_id)
|
||||
except:
|
||||
creator = custom_role.creator_id
|
||||
embed.add_field(name=f"Создатель", value=f"> {creator.mention if creator else custom_role.creator_id}", inline=True)
|
||||
await ctx.edit_original_message(embed=embed)
|
||||
|
||||
|
||||
@commands.slash_command(name="изменить")
|
||||
async def RoleChangeSlash(self, ctx):
|
||||
pass
|
||||
@RoleChangeSlash.sub_command(description="Позволяет изменить вашу роль", name="роль")
|
||||
async def RoleChangeSub(self, ctx: disnake.AppCmdInter):
|
||||
await ctx.response.defer()
|
||||
error_helper = self.client.ErrorOutHelper(send_function = ctx.edit_original_message, err_name = "Ошибка изменения роли")
|
||||
|
||||
if ctx.guild is None:
|
||||
await error_helper.out(d="Эта команда не работает в личных сообщениях!")
|
||||
return
|
||||
|
||||
krekchat = await self.client.fetch_guild(constants["krekchat"])
|
||||
client = self.client
|
||||
HexToRgb = self.client.HexToRgb
|
||||
|
||||
class ButtonsEditRole(disnake.ui.View):
|
||||
client = self.client
|
||||
def __init__(self, ctx, role, embed):
|
||||
super().__init__(timeout=180)
|
||||
self.ctx = ctx
|
||||
self.role = role
|
||||
self.embed = embed
|
||||
|
||||
@disnake.ui.button(label="Стоимость", custom_id="cost", style=disnake.ButtonStyle.blurple)
|
||||
async def cost(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = self.client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете редактировать эту роль", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
modal = ActionModal(self.role, self.ctx, self.embed, "cost")
|
||||
await inter.response.send_modal(modal)
|
||||
|
||||
@disnake.ui.button(label="Цвет", custom_id="color", style=disnake.ButtonStyle.blurple)
|
||||
async def color(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = self.client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете редактировать эту роль", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
modal = ActionModal(self.role, self.ctx, self.embed, "color")
|
||||
await inter.response.send_modal(modal)
|
||||
|
||||
@disnake.ui.button(label="Название", custom_id="name", style=disnake.ButtonStyle.blurple)
|
||||
async def name(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = self.client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете редактировать эту роль", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
modal = ActionModal(self.role, self.ctx, self.embed, "name")
|
||||
await inter.response.send_modal(modal)
|
||||
|
||||
@disnake.ui.button(label="Иконку", custom_id="icon", style=disnake.ButtonStyle.blurple)
|
||||
async def icon(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = self.client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете редактировать эту роль", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
modal = ActionModal(self.role, self.ctx, self.embed, "icon")
|
||||
await inter.response.send_modal(modal)
|
||||
|
||||
async def on_timeout(self):
|
||||
for child in self.children:
|
||||
if isinstance(child, (disnake.ui.Button, disnake.ui.BaseSelect)):
|
||||
child.disabled = True
|
||||
await self.ctx.edit_original_message(view=self)
|
||||
|
||||
class ActionModal(disnake.ui.Modal):
|
||||
DataBaseManager = self.DataBaseManager
|
||||
def __init__(self, role, ctx, embed, operation):
|
||||
self.role = role
|
||||
self.ctx = ctx
|
||||
self.embed = embed
|
||||
self.operation = operation
|
||||
if operation == "name":
|
||||
components = [
|
||||
disnake.ui.TextInput(label="Новое название", placeholder="Например: abc", custom_id="name",
|
||||
style=disnake.TextInputStyle.short, max_length=25)
|
||||
]
|
||||
if operation == "cost":
|
||||
components = [
|
||||
disnake.ui.TextInput(label="Новая стоимость", placeholder="Например: 123", custom_id="cost",
|
||||
style=disnake.TextInputStyle.short, min_length=3)
|
||||
]
|
||||
if operation == "color":
|
||||
components = [
|
||||
disnake.ui.TextInput(label="Новый цвет", placeholder="Например: #ff9900", custom_id="color",
|
||||
style=disnake.TextInputStyle.short)
|
||||
]
|
||||
if operation == "icon":
|
||||
components = [
|
||||
disnake.ui.TextInput(label="Введите ссылку на новую иконку", placeholder="Например: https://i.imgur.com/zWsCJun.png", custom_id="icon",
|
||||
style=disnake.TextInputStyle.short)
|
||||
]
|
||||
|
||||
super().__init__(title=operation, components=components, timeout=300)
|
||||
|
||||
async def callback(self, interaction: disnake.Interaction):
|
||||
ctx = self.ctx
|
||||
error_helper.send_function = interaction.send
|
||||
async with self.DataBaseManager.session() as session:
|
||||
key, value, = list(interaction.text_values.items())[0]
|
||||
await interaction.response.defer(ephemeral=True)
|
||||
if self.operation == "name":
|
||||
await self.role.edit(name=value)
|
||||
if self.operation == "color":
|
||||
rgb = HexToRgb(value)
|
||||
await self.role.edit(color=disnake.Colour.from_rgb(rgb[0], rgb[1], rgb[2]))
|
||||
if self.operation == "cost":
|
||||
cost = abs(int(value))
|
||||
if not 100 < cost < 2147483647:
|
||||
await error_helper.out(d = "Нельзя ставить стоимость роли меньше 100")
|
||||
return
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['roles_custom'] as roles_custom_model:
|
||||
stmt = self.DataBaseManager.update(roles_custom_model).where(roles_custom_model.creator_id == ctx.author.id).values(cost = cost)
|
||||
await session.execute(stmt)
|
||||
if self.operation == "icon":
|
||||
image_url = value
|
||||
TRUSTED_DOMAINS = ["imgur.com", "cdn.discordapp.com", "i.imgur.com"]
|
||||
if ctx.guild.premium_tier < 2:
|
||||
await error_helper.out(d = "Этот сервер должен быть уровня 2 буста для изменения иконок ролей")
|
||||
return
|
||||
if not any(domain in image_url for domain in TRUSTED_DOMAINS):
|
||||
await error_helper.out(d = "Загрузка изображений разрешена только с ресурса https://imgur.com/")
|
||||
return
|
||||
if not image_url.lower().endswith((".png", ".jpg", ".jpeg")):
|
||||
await error_helper.out(d = "Разрешены только файлы с расширением .png, .jpg или .jpeg")
|
||||
return
|
||||
async with aiohttp.ClientSession() as session:
|
||||
try:
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
||||
}
|
||||
async with session.get(image_url, headers=headers) as resp:
|
||||
if resp.status != 200:
|
||||
await error_helper.out(d = "Не удалось загрузить изображение")
|
||||
return
|
||||
|
||||
content_type = resp.headers.get("Content-Type", "").lower()
|
||||
if content_type not in ("image/png", "image/jpeg"):
|
||||
await error_helper.out(d = "Файл должен быть изображением в формате PNG или JPEG")
|
||||
return
|
||||
|
||||
# Проверка размера файла (не более 5 МБ)
|
||||
content_length = int(resp.headers.get("Content-Length", 0))
|
||||
if content_length > 5 * 1024 * 1024: # 5 МБ
|
||||
await error_helper.out(d = "Размер файла не должен превышать 5 МБ")
|
||||
return
|
||||
|
||||
image_data = await resp.read() # Получаем изображение в виде bytes
|
||||
|
||||
except aiohttp.ClientError as e:
|
||||
await error_helper.out(d = f"Ошибка при загрузке изображения: {e}")
|
||||
return
|
||||
try:
|
||||
await self.role.edit(icon=image_data, reason=f"Иконка изменена пользователем {ctx.author}")
|
||||
except disnake.Forbidden:
|
||||
await error_helper.out(d = "У бота недостаточно прав для изменения роли")
|
||||
return
|
||||
except disnake.HTTPException as e:
|
||||
await error_helper.out(d = f"Ошибка при изменении роли: {e}")
|
||||
return
|
||||
|
||||
krekchat = await client.fetch_guild(constants["krekchat"])
|
||||
|
||||
role = krekchat.get_role(self.role.id)
|
||||
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['roles_custom'] as roles_custom_model:
|
||||
stmt = self.DataBaseManager.select(roles_custom_model).where(roles_custom_model.id == self.role.id)
|
||||
custom_role = (await session.execute(stmt)).scalars().first()
|
||||
self.embed.description = f"**Укажите, какой параметр вы хотите изменить у роли {role.mention}:**\n\n**1) Стоимость: **```{custom_role.cost} крошек```\n**2) Цвет: **```{role.color}```\n**3) Название: **```{role.name}```"
|
||||
|
||||
await interaction.edit_original_message(embed=disnake.Embed(title="Роль успешно изменена!", description=f'', colour=0x2F3136))
|
||||
|
||||
await self.ctx.edit_original_message(embed=self.embed)
|
||||
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['users'] as users_model:
|
||||
stmt = self.DataBaseManager.select(users_model).options(self.DataBaseManager.joinedload(users_model.creation_role)).where(users_model.id == ctx.author.id)
|
||||
user = (await session.execute(stmt)).scalars().first()
|
||||
if user.creation_role is None:
|
||||
await error_helper.out(d=f"Вы не создавали кастомных ролей")
|
||||
return
|
||||
|
||||
role = krekchat.get_role(user.creation_role.id)
|
||||
if not role:
|
||||
await error_helper.out(d=f'Вашей роли нет на сервере')
|
||||
return
|
||||
|
||||
embed = disnake.Embed(title=f"Управление ролью",
|
||||
description=f'**Укажите, какой параметр вы хотите изменить у роли {role.mention}:**\n\n**1) Стоимость: **```{user.creation_role.cost} крошек```\n**2) Цвет: **```{role.color}```\n**3) Название: **```{role.name}```',
|
||||
colour=0x2F3136)
|
||||
embed.set_thumbnail(ctx.author.avatar)
|
||||
EditRole = ButtonsEditRole(ctx, role, embed)
|
||||
krekchat = await self.client.fetch_guild(constants["krekchat"])
|
||||
await ctx.edit_original_message(embed=embed, view=EditRole)
|
||||
|
||||
|
||||
|
||||
@commands.slash_command(name="инвентарь")
|
||||
async def RoleInventorySlash(self, ctx):
|
||||
pass
|
||||
@RoleInventorySlash.sub_command(description="Показывает все роли, которыми вы владеете", name="ролей")
|
||||
async def RoleInventorySub(self, ctx: disnake.AppCmdInter):
|
||||
await ctx.response.defer()
|
||||
error_helper = self.client.ErrorOutHelper(send_function = ctx.edit_original_message, err_name = "Ошибка инвентаря")
|
||||
if ctx.guild is None:
|
||||
await error_helper.out(d="Эта команда не работает в личных сообщениях!")
|
||||
return
|
||||
krekchat = await self.client.fetch_guild(constants["krekchat"])
|
||||
|
||||
class RolesInventoryButtons(disnake.ui.View):
|
||||
DataBaseManager = self.DataBaseManager
|
||||
client = self.client
|
||||
def __init__(self, ctx, inventory, embed):
|
||||
super().__init__(timeout=180)
|
||||
self.inventory = inventory
|
||||
self.ctx = ctx
|
||||
self.embed = embed
|
||||
self.page = 1
|
||||
self.maxpage = len(inventory) if len(inventory) > 0 else 1
|
||||
|
||||
self.left.disabled = (self.page == 1)
|
||||
self.right.disabled = (self.page == self.maxpage)
|
||||
|
||||
@disnake.ui.button(label="1", custom_id="1", style=disnake.ButtonStyle.blurple)
|
||||
async def one(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = self.client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете управлять ролями другого участника", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
await self.ToggleRoles(inter, button)
|
||||
|
||||
@disnake.ui.button(label="2", custom_id="2", style=disnake.ButtonStyle.blurple)
|
||||
async def two(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = self.client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете управлять ролями другого участника", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
await self.ToggleRoles(inter, button)
|
||||
|
||||
@disnake.ui.button(label="3", custom_id="3", style=disnake.ButtonStyle.blurple)
|
||||
async def three(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = self.client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете управлять ролями другого участника", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
await self.ToggleRoles(inter, button)
|
||||
|
||||
@disnake.ui.button(label="4", custom_id="4", style=disnake.ButtonStyle.blurple)
|
||||
async def four(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = self.client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете управлять ролями другого участника", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
await self.ToggleRoles(inter, button)
|
||||
|
||||
@disnake.ui.button(label="5", custom_id="5", style=disnake.ButtonStyle.blurple)
|
||||
async def five(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = self.client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете управлять ролями другого участника", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
await self.ToggleRoles(inter, button)
|
||||
|
||||
@disnake.ui.button(label="<", custom_id="left", style=disnake.ButtonStyle.secondary)
|
||||
async def left(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = self.client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете управлять ролями другого участника", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
self.page -= 1
|
||||
self.left.disabled = (self.page == 1)
|
||||
self.right.disabled = (self.page == self.maxpage)
|
||||
self.embed = await EmbedRoleInventoryChanger(self.inventory, self.embed, self.page, self.ctx.author)
|
||||
await inter.response.edit_message(embed=self.embed, view=self)
|
||||
|
||||
@disnake.ui.button(label=">", custom_id="right", style=disnake.ButtonStyle.secondary)
|
||||
async def right(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = self.client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете управлять ролями другого участника", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
self.page += 1
|
||||
self.left.disabled = (self.page == 1)
|
||||
self.right.disabled = (self.page == self.maxpage)
|
||||
self.embed = await EmbedRoleInventoryChanger(self.inventory, self.embed, self.page, self.ctx.author)
|
||||
await inter.response.edit_message(embed=self.embed, view=self)
|
||||
|
||||
async def ToggleRoles(self, inter, button):
|
||||
error_helper = self.client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка инвентаря", ephemeral = True)
|
||||
|
||||
if len(self.inventory) < 1:
|
||||
await error_helper.out(d=f'Роли с таким номером нет на странице')
|
||||
return
|
||||
if len(self.inventory[self.page - 1]) < int(button.custom_id):
|
||||
await error_helper.out(d=f'Роли с таким номером нет на странице')
|
||||
return
|
||||
role = self.inventory[self.page - 1][int(button.custom_id) - 1]
|
||||
role = krekchat.get_role(role)
|
||||
if not role:
|
||||
return
|
||||
if role in self.ctx.author.roles:
|
||||
await self.ctx.author.remove_roles(role)
|
||||
else:
|
||||
roles_inventory_ids = []
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['received_roles_custom'] as received_roles_custom_model:
|
||||
stmt = self.DataBaseManager.select(received_roles_custom_model).where(
|
||||
received_roles_custom_model.user_id == ctx.author.id
|
||||
)
|
||||
custom_roles = (await session.execute(stmt)).scalars().all()
|
||||
roles_inventory_ids += [role.role_id for role in custom_roles if not role.role_id in roles_inventory_ids]
|
||||
|
||||
async with self.DataBaseManager.models['received_roles_prize'] as received_roles_prize_model:
|
||||
stmt = self.DataBaseManager.select(received_roles_prize_model).where(
|
||||
received_roles_prize_model.user_id == ctx.author.id
|
||||
)
|
||||
prize_roles = (await session.execute(stmt)).scalars().all()
|
||||
roles_inventory_ids += [role.role_id for role in prize_roles if not role.role_id in roles_inventory_ids]
|
||||
|
||||
if not role.id in roles_inventory_ids:
|
||||
await error_helper.out(d=f"Эта роль пропала из вашего инвентаря")
|
||||
return
|
||||
await self.ctx.author.add_roles(role)
|
||||
self.embed = await EmbedRoleInventoryChanger(self.inventory, self.embed, self.page, self.ctx.author)
|
||||
await inter.response.edit_message(embed=self.embed, view=self)
|
||||
|
||||
async def on_timeout(self):
|
||||
for child in self.children:
|
||||
if isinstance(child, (disnake.ui.Button, disnake.ui.BaseSelect)):
|
||||
child.disabled = True
|
||||
await self.ctx.edit_original_message(view=self)
|
||||
|
||||
async def EmbedRoleInventoryChanger(inventory, embed, selfpage, member):
|
||||
embed.clear_fields()
|
||||
if len(inventory) == 0:
|
||||
embed.add_field(name=f"В инвентаре пока ничего нет", value=f"", inline=False)
|
||||
return embed
|
||||
page = inventory[selfpage - 1]
|
||||
c = 1
|
||||
for roleid in page:
|
||||
role = krekchat.get_role(roleid)
|
||||
|
||||
wearing = role in member.roles
|
||||
embed.add_field(name=f"",
|
||||
value=f"**{c})** {role.mention if role else roleid} {':green_circle:' if wearing else ':red_circle:'}",
|
||||
inline=False)
|
||||
c += 1
|
||||
return embed
|
||||
|
||||
roles_inventory_sorted_ids = []
|
||||
async with self.DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with self.DataBaseManager.models['roles_custom'] as roles_custom_model:
|
||||
stmt = self.DataBaseManager.select(roles_custom_model).where(
|
||||
roles_custom_model.creator_id == ctx.author.id
|
||||
)
|
||||
creation_role = (await session.execute(stmt)).scalars().first()
|
||||
if not creation_role is None:
|
||||
roles_inventory_sorted_ids.append(creation_role.id)
|
||||
|
||||
async with self.DataBaseManager.models['received_roles_prize'] as received_roles_prize_model:
|
||||
stmt = self.DataBaseManager.select(received_roles_prize_model).where(
|
||||
received_roles_prize_model.user_id == ctx.author.id
|
||||
)
|
||||
prize_roles = (await session.execute(stmt)).scalars().all()
|
||||
roles_inventory_sorted_ids += [role.role_id for role in prize_roles if not role.role_id in roles_inventory_sorted_ids]
|
||||
|
||||
async with self.DataBaseManager.models['received_roles_custom'] as received_roles_custom_model:
|
||||
stmt = self.DataBaseManager.select(received_roles_custom_model).where(
|
||||
received_roles_custom_model.user_id == ctx.author.id
|
||||
)
|
||||
custom_roles = (await session.execute(stmt)).scalars().all()
|
||||
roles_inventory_sorted_ids += [role.role_id for role in custom_roles if not role.role_id in roles_inventory_sorted_ids]
|
||||
|
||||
ready_array = self.client.PartitioningEmbeder(roles_inventory_sorted_ids)
|
||||
embed = disnake.Embed(title=f"Инвентарь ролей", description=f'', colour=0x2F3136)
|
||||
embed.set_thumbnail(ctx.author.avatar)
|
||||
view = RolesInventoryButtons(ctx, ready_array, embed)
|
||||
|
||||
embed = await EmbedRoleInventoryChanger(ready_array, embed, 1, ctx.author)
|
||||
await ctx.edit_original_message(embed=embed, view=view)
|
||||
|
||||
|
||||
@commands.slash_command(name="магазин")
|
||||
async def ShopSlash(self, ctx):
|
||||
pass
|
||||
@ShopSlash.sub_command(description="Тут вы можете купить себе роль", name="ролей")
|
||||
async def RolesShopSub(self, ctx: disnake.AppCmdInter):
|
||||
'''
|
||||
|
||||
|
||||
магазин ролей
|
||||
|
||||
|
||||
'''
|
||||
await ctx.response.defer()
|
||||
error_helper = self.client.ErrorOutHelper(send_function = ctx.edit_original_message, err_name = "Ошибка магазина")
|
||||
DataBaseManager = self.DataBaseManager
|
||||
|
||||
if ctx.guild is None:
|
||||
await error_helper.out(d="Эта команда не работает в личных сообщениях!")
|
||||
return
|
||||
PartitioningEmbeder = self.client.PartitioningEmbeder
|
||||
client = self.client
|
||||
krekchat = await client.fetch_guild(constants["krekchat"])
|
||||
|
||||
class ShopView(disnake.ui.View):
|
||||
def __init__(self, ctx, embed):
|
||||
super().__init__(timeout=180)
|
||||
self.ctx = ctx
|
||||
self.embed = embed
|
||||
|
||||
async def initialize(self):
|
||||
async with DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with DataBaseManager.models['roles_custom'] as roles_custom_model:
|
||||
|
||||
stmt = DataBaseManager.select(roles_custom_model).order_by(DataBaseManager.asc(roles_custom_model.date_of_creation))
|
||||
self.sortbydateofcreation = (await session.execute(stmt)).scalars().all()
|
||||
|
||||
stmt = DataBaseManager.select(roles_custom_model).order_by(DataBaseManager.asc(roles_custom_model.cost))
|
||||
self.sortbycost = (await session.execute(stmt)).scalars().all()
|
||||
|
||||
stmt = DataBaseManager.select(roles_custom_model).order_by(DataBaseManager.desc(roles_custom_model.cost))
|
||||
self.sortbycostreversed = (await session.execute(stmt)).scalars().all()
|
||||
|
||||
stmt = DataBaseManager.select(roles_custom_model).order_by(DataBaseManager.desc(roles_custom_model.date_of_creation))
|
||||
self.sortbydateofcreationreversed = (await session.execute(stmt)).scalars().all()
|
||||
|
||||
stmt = DataBaseManager.select(roles_custom_model).outerjoin(roles_custom_model.users).group_by(roles_custom_model.id).order_by(DataBaseManager.func.count(DataBaseManager.models['users'].model.id).desc())
|
||||
self.sortbypopularityreversed = (await session.execute(stmt)).scalars().all()
|
||||
|
||||
stmt = DataBaseManager.select(roles_custom_model).outerjoin(roles_custom_model.users).group_by(roles_custom_model.id).order_by(DataBaseManager.func.count(DataBaseManager.models['users'].model.id).asc())
|
||||
self.sortbypopularity = (await session.execute(stmt)).scalars().all()
|
||||
|
||||
self.sortvalues = {"sortbydateofcreation": self.sortbydateofcreation, "sortbycost": self.sortbycost,
|
||||
"sortbycostreversed": self.sortbycostreversed,
|
||||
"sortbydateofcreationreversed": self.sortbydateofcreationreversed,
|
||||
"sortbypopularityreversed": self.sortbypopularity,
|
||||
"sortbypopularity": self.sortbypopularityreversed}
|
||||
self.maxpage = len(PartitioningEmbeder([i for i in self.sortbydateofcreation]))
|
||||
|
||||
self.page = 1
|
||||
|
||||
@disnake.ui.button(label="<<", custom_id="leftmax")
|
||||
async def leftmax(self, button, inter):
|
||||
await inter.response.defer()
|
||||
if inter.author != self.ctx.author:
|
||||
error = client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете покупать роли в магазине другого участника. Используйте команду `/магазин ролей`, чтобы купить роли", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
self.page = 1
|
||||
self.leftmax.disabled = (self.page == 1)
|
||||
self.left.disabled = (self.page == 1)
|
||||
self.rightmax.disabled = (self.page == self.maxpage)
|
||||
self.right.disabled = (self.page == self.maxpage)
|
||||
|
||||
self.embed = await EmbedShopChanger(self.sort.values, self.embed, self.page, self.sortvalues)
|
||||
|
||||
await inter.message.edit(view=self, embed=self.embed)
|
||||
|
||||
@disnake.ui.button(label="<", custom_id="left")
|
||||
async def left(self, button, inter):
|
||||
await inter.response.defer()
|
||||
if inter.author != self.ctx.author:
|
||||
error = client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете покупать роли в магазине другого участника. Используйте команду `/магазин ролей`, чтобы купить роли", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
self.page = max(self.page - 1, 1)
|
||||
self.leftmax.disabled = (self.page == 1)
|
||||
self.left.disabled = (self.page == 1)
|
||||
self.rightmax.disabled = (self.page == self.maxpage)
|
||||
self.right.disabled = (self.page == self.maxpage)
|
||||
|
||||
self.embed = await EmbedShopChanger(self.sort.values, self.embed, self.page, self.sortvalues)
|
||||
|
||||
await inter.message.edit(view=self, embed=self.embed)
|
||||
|
||||
@disnake.ui.button(label=">", custom_id="right")
|
||||
async def right(self, button, inter):
|
||||
await inter.response.defer()
|
||||
if inter.author != self.ctx.author:
|
||||
error = client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете покупать роли в магазине другого участника. Используйте команду `/магазин ролей`, чтобы купить роли", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
self.page = min(self.page + 1, self.maxpage)
|
||||
self.leftmax.disabled = (self.page == 1)
|
||||
self.left.disabled = (self.page == 1)
|
||||
self.rightmax.disabled = (self.page == self.maxpage)
|
||||
self.right.disabled = (self.page == self.maxpage)
|
||||
|
||||
self.embed = await EmbedShopChanger(self.sort.values, self.embed, self.page, self.sortvalues)
|
||||
|
||||
await inter.message.edit(view=self, embed=self.embed)
|
||||
|
||||
@disnake.ui.button(label=">>", custom_id="rightmax")
|
||||
async def rightmax(self, button, inter):
|
||||
await inter.response.defer()
|
||||
if inter.author != self.ctx.author:
|
||||
error = client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете покупать роли в магазине другого участника. Используйте команду `/магазин ролей`, чтобы купить роли", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
self.page = self.maxpage
|
||||
self.leftmax.disabled = (self.page == 1)
|
||||
self.left.disabled = (self.page == 1)
|
||||
self.rightmax.disabled = (self.page == self.maxpage)
|
||||
self.right.disabled = (self.page == self.maxpage)
|
||||
|
||||
self.embed = await EmbedShopChanger(self.sort.values, self.embed, self.page, self.sortvalues)
|
||||
|
||||
await inter.message.edit(view=self, embed=self.embed)
|
||||
|
||||
@disnake.ui.string_select(
|
||||
placeholder="Сначала новые",
|
||||
options=[
|
||||
disnake.SelectOption(label="Сначала новые", value="sortbydateofcreationreversed", default=True),
|
||||
disnake.SelectOption(label="Сначала старые", value="sortbydateofcreation", default=False),
|
||||
disnake.SelectOption(label="Сначала дешёвые", value="sortbycost", default=False),
|
||||
disnake.SelectOption(label="Сначала дорогие", value="sortbycostreversed", default=False),
|
||||
disnake.SelectOption(label="Сначала популярные", value="sortbypopularity", default=False),
|
||||
disnake.SelectOption(label="Сначала не популярные", value="sortbypopularityreversed", default=False),
|
||||
],
|
||||
min_values=1,
|
||||
max_values=1,
|
||||
)
|
||||
async def sort(self, select: disnake.ui.StringSelect, inter: disnake.MessageInteraction):
|
||||
await inter.response.defer()
|
||||
if inter.author != self.ctx.author:
|
||||
error = client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете покупать роли в магазине другого участника. Используйте команду `/магазин ролей`, чтобы купить роли", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
self.page = 1
|
||||
self.leftmax.disabled = (self.page == 1)
|
||||
self.left.disabled = (self.page == 1)
|
||||
self.rightmax.disabled = (self.page == self.maxpage)
|
||||
self.right.disabled = (self.page == self.maxpage)
|
||||
|
||||
self.embed = await EmbedShopChanger(self.sort.values, self.embed, self.page, self.sortvalues)
|
||||
|
||||
for opt in range(len(self.sort.options)):
|
||||
if self.sort.options[opt].value == self.sort.values[0]:
|
||||
self.sort.options[opt].default = True
|
||||
else:
|
||||
self.sort.options[opt].default = False
|
||||
|
||||
await inter.message.edit(view=self, embed=self.embed)
|
||||
|
||||
@disnake.ui.button(label="1", custom_id="1", style=disnake.ButtonStyle.blurple, row=3)
|
||||
async def one(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете покупать роли в магазине другого участника. Используйте команду `/магазин ролей`, чтобы купить роли", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
await self.BuyRoles(inter, button)
|
||||
|
||||
@disnake.ui.button(label="2", custom_id="2", style=disnake.ButtonStyle.blurple, row=3)
|
||||
async def two(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете покупать роли в магазине другого участника. Используйте команду `/магазин ролей`, чтобы купить роли", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
await self.BuyRoles(inter, button)
|
||||
|
||||
@disnake.ui.button(label="3", custom_id="3", style=disnake.ButtonStyle.blurple, row=3)
|
||||
async def three(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете покупать роли в магазине другого участника. Используйте команду `/магазин ролей`, чтобы купить роли", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
await self.BuyRoles(inter, button)
|
||||
|
||||
@disnake.ui.button(label="4", custom_id="4", style=disnake.ButtonStyle.blurple, row=3)
|
||||
async def four(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете покупать роли в магазине другого участника. Используйте команду `/магазин ролей`, чтобы купить роли", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
await self.BuyRoles(inter, button)
|
||||
|
||||
@disnake.ui.button(label="5", custom_id="5", style=disnake.ButtonStyle.blurple, row=3)
|
||||
async def five(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете покупать роли в магазине другого участника. Используйте команду `/магазин ролей`, чтобы купить роли", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
await self.BuyRoles(inter, button)
|
||||
|
||||
async def on_timeout(self):
|
||||
for child in self.children:
|
||||
if isinstance(child, (disnake.ui.Button, disnake.ui.BaseSelect)):
|
||||
child.disabled = True
|
||||
await self.ctx.edit_original_message(view=self)
|
||||
|
||||
async def BuyRoles(self, inter, button):
|
||||
error_helper = client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка покупки", ephemeral = True)
|
||||
async with DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with DataBaseManager.models['users'] as users_model:
|
||||
stmt = DataBaseManager.select(users_model).options(DataBaseManager.selectinload(users_model.custom_roles)).where(users_model.id == inter.author.id)
|
||||
user = (await session.execute(stmt)).scalars().first()
|
||||
|
||||
page = (PartitioningEmbeder(
|
||||
self.sortvalues[self.sort.values[0] if len(self.sort.values) == 1 else "sortbydateofcreationreversed"]))[
|
||||
self.page - 1]
|
||||
|
||||
if len(page) < int(button.custom_id):
|
||||
await error_helper.out(d=f'Роли с таким номером нет на странице')
|
||||
return
|
||||
|
||||
custom_role = page[int(button.custom_id) - 1]
|
||||
if user.crumbs < custom_role.cost:
|
||||
await error_helper.out(d=f'У вас недостаточно крошек')
|
||||
return
|
||||
|
||||
if custom_role.id in [role.id for role in user.custom_roles]:
|
||||
await error_helper.out(d=f'У вас уже есть эта роль')
|
||||
return
|
||||
|
||||
role = krekchat.get_role(custom_role.id)
|
||||
confirmembed = disnake.Embed(description=f'Вы уверены, что хотите преобрести роль {role.mention if role else custom_role.id} за {custom_role.cost} крошек?',
|
||||
colour=0x2F3136)
|
||||
ConfMessage = ConfirmView(self.ctx, role, custom_role)
|
||||
await inter.response.edit_message(view=ConfMessage, embed=confirmembed)
|
||||
class ConfirmView(disnake.ui.View):
|
||||
def __init__(self, ctx, role, custom_role):
|
||||
super().__init__(timeout=180)
|
||||
self.ctx = ctx
|
||||
self.role = role
|
||||
self.custom_role = custom_role
|
||||
|
||||
@disnake.ui.button(label="Да", custom_id="yes", style=disnake.ButtonStyle.green)
|
||||
async def yes(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете покупать роли за другого участника. Используйте команду `/магазин ролей`, чтобы купить роли", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
if not self.role:
|
||||
error = client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка покупки", err_description = "Такая роль не найдена на сервере. Возможно, это ошибка базы данных", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
|
||||
ctx = self.ctx
|
||||
async with DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with DataBaseManager.models['users'] as users_model:
|
||||
stmt = DataBaseManager.select(users_model).where(users_model.id == inter.author.id).with_for_update()
|
||||
user = (await session.execute(stmt)).scalars().first()
|
||||
stmt = DataBaseManager.select(users_model).where(users_model.id == self.custom_role.creator_id).with_for_update()
|
||||
creator = (await session.execute(stmt)).scalars().first()
|
||||
user.crumbs -= self.custom_role.cost
|
||||
creator.crumbs += self.custom_role.cost
|
||||
receive = DataBaseManager.models['received_roles_custom'].m(role_id = self.custom_role.id, user_id = user.id)
|
||||
session.add(receive)
|
||||
history = DataBaseManager.models['transaction_history_crumbs'].m(sender_id = user.id, recipient_id = creator.id, amount = self.custom_role.cost, description = f"Покупка роли {self.role.mention}")
|
||||
await self.ctx.author.add_roles(self.role)
|
||||
embed = disnake.Embed(description=f'Роль {self.role.mention} успешно преобретена!', colour=0x2F3136)
|
||||
await self.ctx.edit_original_message(embed=embed, view=None)
|
||||
|
||||
@disnake.ui.button(label="Нет", custom_id="no", style=disnake.ButtonStyle.red)
|
||||
async def no(self, button, inter):
|
||||
if inter.author != self.ctx.author:
|
||||
error = client.ErrorOutHelper(send_function = inter.send, err_name = "Ошибка доступа", err_description = "Вы не можете покупать роли за другого участника. Используйте команду `/магазин ролей`, чтобы купить роли", ephemeral = True)
|
||||
await error.out()
|
||||
return
|
||||
|
||||
ctx = self.ctx
|
||||
|
||||
embed = disnake.Embed(title=f"Магазин ролей", description=f'', colour=0x2F3136)
|
||||
ShopMessage = ShopView(ctx, embed)
|
||||
await ShopMessage.initialize()
|
||||
ShopMessage.leftmax.disabled = (ShopMessage.page == 1)
|
||||
ShopMessage.left.disabled = (ShopMessage.page == 1)
|
||||
ShopMessage.rightmax.disabled = (ShopMessage.page == ShopMessage.maxpage)
|
||||
ShopMessage.right.disabled = (ShopMessage.page == ShopMessage.maxpage)
|
||||
|
||||
embed = await EmbedShopChanger([], embed, 1)
|
||||
|
||||
embed.set_thumbnail(ctx.author.avatar)
|
||||
|
||||
await ctx.edit_original_message(view=ShopMessage, embed=embed)
|
||||
|
||||
async def on_timeout(self):
|
||||
for child in self.children:
|
||||
if isinstance(child, (disnake.ui.Button, disnake.ui.BaseSelect)):
|
||||
child.disabled = True
|
||||
await self.ctx.edit_original_message(view=self)
|
||||
async def EmbedShopChanger(sortvalues, embed, selfpage, constvalues=None):
|
||||
if not constvalues:
|
||||
async with DataBaseManager.session() as session:
|
||||
async with session.begin():
|
||||
async with DataBaseManager.models['roles_custom'] as roles_custom_model:
|
||||
|
||||
stmt = DataBaseManager.select(roles_custom_model).order_by(DataBaseManager.asc(roles_custom_model.date_of_creation))
|
||||
sortbydateofcreation = (await session.execute(stmt)).scalars().all()
|
||||
|
||||
stmt = DataBaseManager.select(roles_custom_model).order_by(DataBaseManager.asc(roles_custom_model.cost))
|
||||
sortbycost = (await session.execute(stmt)).scalars().all()
|
||||
|
||||
stmt = DataBaseManager.select(roles_custom_model).order_by(DataBaseManager.desc(roles_custom_model.cost))
|
||||
sortbycostreversed = (await session.execute(stmt)).scalars().all()
|
||||
|
||||
stmt = DataBaseManager.select(roles_custom_model).order_by(DataBaseManager.desc(roles_custom_model.date_of_creation))
|
||||
sortbydateofcreationreversed = (await session.execute(stmt)).scalars().all()
|
||||
|
||||
stmt = DataBaseManager.select(roles_custom_model).outerjoin(roles_custom_model.users).group_by(roles_custom_model.id).order_by(DataBaseManager.func.count(DataBaseManager.models['users'].model.id).desc())
|
||||
sortbypopularityreversed = (await session.execute(stmt)).scalars().all()
|
||||
|
||||
stmt = DataBaseManager.select(roles_custom_model).outerjoin(roles_custom_model.users).group_by(roles_custom_model.id).order_by(DataBaseManager.func.count(DataBaseManager.models['users'].model.id).asc())
|
||||
sortbypopularity = (await session.execute(stmt)).scalars().all()
|
||||
|
||||
constvalues = {"sortbydateofcreation": sortbydateofcreation, "sortbycost": sortbycost,
|
||||
"sortbycostreversed": sortbycostreversed,
|
||||
"sortbydateofcreationreversed": sortbydateofcreationreversed,
|
||||
"sortbypopularityreversed": sortbypopularity,
|
||||
"sortbypopularity": sortbypopularityreversed}
|
||||
embed.clear_fields()
|
||||
page = \
|
||||
(PartitioningEmbeder(constvalues[sortvalues[0] if len(sortvalues) == 1 else "sortbydateofcreationreversed"]))[
|
||||
selfpage - 1]
|
||||
c = 1
|
||||
for custom_role in page:
|
||||
try:
|
||||
creator = await krekchat.fetch_member(custom_role.creator_id)
|
||||
except:
|
||||
creator = custom_role.creator_id
|
||||
role = krekchat.get_role(custom_role.id)
|
||||
embed.add_field(name=f"",
|
||||
value=f"**{c})** {role.mention if role else custom_role.id}\n**Цена: **{custom_role.cost} крошек\n**Создатель: **{creator.mention if type(creator)!=int else creator}",
|
||||
inline=False)
|
||||
c += 1
|
||||
return embed
|
||||
embed = disnake.Embed(title=f"Магазин ролей", description=f'', colour=0x2F3136)
|
||||
async with DataBaseManager.session() as session:
|
||||
await self.client.UserUpdate(ctx.author, session = session)
|
||||
async with session.begin():
|
||||
async with DataBaseManager.models['roles_custom'] as roles_custom_model:
|
||||
stmt = DataBaseManager.select(roles_custom_model)
|
||||
roles = (await session.execute(stmt)).scalars().all()
|
||||
if len(roles) == 0:
|
||||
embed.description = "В магазине пока нет ни одной роли"
|
||||
embed.set_thumbnail(ctx.author.avatar)
|
||||
await ctx.edit_original_message(embed=embed)
|
||||
return
|
||||
ShopMessage = ShopView(ctx, embed)
|
||||
await ShopMessage.initialize()
|
||||
ShopMessage.leftmax.disabled = (ShopMessage.page == 1)
|
||||
ShopMessage.left.disabled = (ShopMessage.page == 1)
|
||||
ShopMessage.rightmax.disabled = (ShopMessage.page == ShopMessage.maxpage)
|
||||
ShopMessage.right.disabled = (ShopMessage.page == ShopMessage.maxpage)
|
||||
|
||||
|
||||
embed = await EmbedShopChanger([], embed, 1)
|
||||
embed.set_thumbnail(ctx.author.avatar)
|
||||
await ctx.edit_original_message(view=ShopMessage, embed=embed)
|
||||
20
src/constants/global_constants.py
Normal file
20
src/constants/global_constants.py
Normal file
@@ -0,0 +1,20 @@
|
||||
constants = {
|
||||
"costrolecreate": 10000, "costrolerenewal": 5000, "dailycrumbs": 300, "casinospinslimit": 20,
|
||||
"krekchat": 490445877903622144,
|
||||
"sponsors": [648932777755934740, 926021742151999509, 959416622144180265, 995602360325902386, 995602360325902387,
|
||||
995602360325902388, 995602360325902389, 712751688741814343, 1244009309000437780, 1285667409365176372],
|
||||
"mutes": [1219615791750709268, #text
|
||||
1219615979185770597],#voice
|
||||
"ban_role": 1219276355875639407,
|
||||
"me": 887696340920963072,
|
||||
"moder": 490712181927837722,
|
||||
"curator": 490712205445169162,
|
||||
"everyone": 490445877903622144,
|
||||
"staff": 1229765545310818377,
|
||||
"level_roles": [None, 1379836872653799515, 1379837914837549197, 1379838164394577980, 490707634710642700],
|
||||
"moderators": [490712181927837722, 490712205445169162],
|
||||
"hierarchy": [490712205445169162, 490712181927837722, 490445877903622144],
|
||||
"givecrumbscommission": 0.05,
|
||||
"bots_talk_protocol_channel": 1376233239202758827,
|
||||
"databases_backups_channel": 1382363252683706448,
|
||||
}
|
||||
1307
src/constants/rimagochi_constants.py
Normal file
1307
src/constants/rimagochi_constants.py
Normal file
File diff suppressed because it is too large
Load Diff
401
src/database/db_classes.py
Normal file
401
src/database/db_classes.py
Normal file
@@ -0,0 +1,401 @@
|
||||
from sqlalchemy import Column, Integer, BigInteger, Text, Float, ForeignKey, UniqueConstraint, MetaData, Boolean, JSON, String, ARRAY, CheckConstraint, func, Index
|
||||
from sqlalchemy.orm import declarative_base, relationship, Mapped, mapped_column
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
||||
from sqlalchemy.orm import sessionmaker, relationship, DeclarativeBase
|
||||
from sqlalchemy.schema import CreateTable
|
||||
from sqlalchemy.sql import text
|
||||
import datetime
|
||||
from zoneinfo import ZoneInfo
|
||||
from typing import Annotated, List
|
||||
import disnake
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
|
||||
def get_table_name(self):
|
||||
return self.__tablename__
|
||||
|
||||
def to_dict(self, exclude: list[str] = None):
|
||||
"""Конвертирует модель в словарь, исключая указанные поля."""
|
||||
if exclude is None:
|
||||
exclude = []
|
||||
|
||||
return {
|
||||
c.name: getattr(self, c.name)
|
||||
for c in self.__table__.columns
|
||||
if c.name not in exclude
|
||||
}
|
||||
|
||||
|
||||
discord_identificator_pk = Annotated[int, mapped_column(BigInteger, primary_key=True, nullable=False, index = True)]
|
||||
identificator_pk = Annotated[int, mapped_column(Integer, primary_key=True, nullable=False, autoincrement=True, index = True)]
|
||||
discord_identificator = Annotated[int, mapped_column(BigInteger, nullable=False, index=True)]
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = 'users'
|
||||
|
||||
id: Mapped[discord_identificator_pk]
|
||||
crumbs: Mapped[float] = mapped_column(Float, nullable=False, default=0)
|
||||
period_messages: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||
summary_messages: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||
period_voice_activity: Mapped[float] = mapped_column(Float, nullable=False, default=0)
|
||||
summary_voice_activity: Mapped[float] = mapped_column(Float, nullable=False, default=0)
|
||||
carma: Mapped[float] = mapped_column(Float, nullable=False, default=0)
|
||||
staff_salary: Mapped[float] = mapped_column(Float, nullable=False, default=0)
|
||||
last_daily_crumbs_date: Mapped[float] = mapped_column(Float, nullable=False, default=0)
|
||||
last_activity_date: Mapped[float] = mapped_column(Float, nullable=False, onupdate=func.extract('epoch', func.now()), server_onupdate=text("EXTRACT(EPOCH FROM NOW())"), server_default=text("EXTRACT(EPOCH FROM NOW())"))
|
||||
|
||||
custom_roles: Mapped[list["CustomRole"]] = relationship(
|
||||
secondary="received_roles_custom",
|
||||
back_populates="users",
|
||||
#lazy="selectin"
|
||||
)
|
||||
|
||||
creation_role: Mapped["CustomRole"] = relationship(
|
||||
back_populates="creator",
|
||||
uselist=False,
|
||||
#lazy="joined"
|
||||
)
|
||||
|
||||
prize_roles: Mapped[list["PrizeRole"]] = relationship(
|
||||
secondary="received_roles_prize",
|
||||
back_populates="users",
|
||||
#lazy="selectin"
|
||||
)
|
||||
|
||||
sender_in_crumbs_transactions: Mapped[list["CrumbsTransactionHistory"]] = relationship(
|
||||
back_populates="sender",
|
||||
uselist=True,
|
||||
foreign_keys="[CrumbsTransactionHistory.sender_id]",
|
||||
#lazy="selectin"
|
||||
)
|
||||
|
||||
recipient_in_crumbs_transactions: Mapped[list["CrumbsTransactionHistory"]] = relationship(
|
||||
back_populates="recipient",
|
||||
uselist=True,
|
||||
foreign_keys="[CrumbsTransactionHistory.recipient_id]",
|
||||
#lazy="selectin"
|
||||
)
|
||||
|
||||
casino_account: Mapped["CasinoUser"] = relationship(
|
||||
back_populates="user",
|
||||
uselist=False,
|
||||
#lazy="joined"
|
||||
)
|
||||
|
||||
rimagochi_account: Mapped["RimagochiUser"] = relationship(
|
||||
back_populates="user",
|
||||
uselist=False,
|
||||
#lazy="joined"
|
||||
)
|
||||
|
||||
async def get_member(self, client = None, guild = None, guild_id = None):
|
||||
|
||||
if not guild is None:
|
||||
try:
|
||||
member = await guild.fetch_member(self.id)
|
||||
except disnake.NotFound:
|
||||
member = None
|
||||
return member
|
||||
|
||||
if client is None:
|
||||
raise TypeError("get_member() не хватает обязательного аргумента: 'client'")
|
||||
|
||||
if guild_id is None:
|
||||
raise TypeError("get_member() не хватает обязательного аргумента: 'guild_id'")
|
||||
else:
|
||||
guild = await client.fetch_guild(guild_id)
|
||||
return self.get_member(guild = guild, client = None, guild_id = None)
|
||||
|
||||
async def in_role(self, roles = None, member = None, boolean = True, guild = None, guild_id = None, client = None):
|
||||
'''
|
||||
Находит пересечение между ролями пользователя и входным списком ролей
|
||||
'''
|
||||
if member is None:
|
||||
await self.get_member(guild = guild, guild_id = guild_id, client = client)
|
||||
|
||||
if roles is None:
|
||||
raise TypeError("in_role() не хватает обязательного аргумента: 'roles'")
|
||||
|
||||
member_roles = set(member.roles)
|
||||
roles = set(roles)
|
||||
|
||||
if boolean:
|
||||
return bool(member_roles & roles)
|
||||
else:
|
||||
return member_roles & roles
|
||||
|
||||
async def crumbs_adder(self, incoming_crumbs: float, sponsor = None, member = None, sponsor_roles = None):
|
||||
if sponsor is None:
|
||||
sponsor = await self.in_role(roles = sponsor_roles, member = member)
|
||||
|
||||
modifier = (self.carma / 100) + (sponsor+1)
|
||||
|
||||
add_crumbs = (incoming_crumbs * modifier) if incoming_crumbs * modifier >= 0 else 0
|
||||
self.crumbs += add_crumbs
|
||||
return add_crumbs
|
||||
|
||||
async def give_salary(self):
|
||||
self.crumbs += self.staff_salary
|
||||
|
||||
async def claim_daily_crumbs(self, sponsor = None, member = None, sponsor_roles = None, daily_constant = 300):
|
||||
|
||||
moscow_tz = ZoneInfo("Europe/Moscow")
|
||||
last_daily_time = datetime.datetime.fromtimestamp(self.last_daily_crumbs_date, tz=moscow_tz)
|
||||
now_time = datetime.datetime.now(tz=moscow_tz)
|
||||
if now_time.date() > last_daily_time.date():
|
||||
daily = await self.crumbs_adder(incoming_crumbs = daily_constant, sponsor = sponsor, sponsor_roles = sponsor_roles, member = member)
|
||||
self.last_daily_crumbs_date = datetime.datetime.now().timestamp()
|
||||
return {'success': True, 'output': f"Вы получили ежедневную награду в размере {int(daily)} крошек!", 'count': daily, 'date': now_time.date()}
|
||||
else:
|
||||
next_day = (now_time + datetime.timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0).timestamp()
|
||||
return {'success': False, 'output': f"Вы уже получали ежедневную награду сегодня, следующая будет доступна <t:{int(next_day)}:R>"}
|
||||
|
||||
#роли
|
||||
class CustomRole(Base):
|
||||
__tablename__ = 'roles_custom'
|
||||
|
||||
id: Mapped[discord_identificator_pk]
|
||||
creator_id: Mapped[int] = mapped_column(ForeignKey('users.id', ondelete='CASCADE'), nullable=False, unique=True)
|
||||
date_of_creation: Mapped[float] = mapped_column(Float, nullable=False, server_default=text("EXTRACT(EPOCH FROM NOW())"))
|
||||
cost: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
renewal_date: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
renewal_enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
|
||||
|
||||
users: Mapped[list["User"]] = relationship(
|
||||
secondary="received_roles_custom",
|
||||
back_populates="custom_roles",
|
||||
#lazy="selectin"
|
||||
)
|
||||
|
||||
creator: Mapped["User"] = relationship(
|
||||
back_populates="creation_role",
|
||||
foreign_keys = [creator_id],
|
||||
uselist=False,
|
||||
#lazy="joined"
|
||||
)
|
||||
|
||||
async def renewal_date_update(self, time_formater_function, days = 30):
|
||||
self.renewal_date = float(time_formater_function(days = days))
|
||||
|
||||
class PrizeRole(Base):
|
||||
__tablename__ = 'roles_prize'
|
||||
|
||||
id: Mapped[discord_identificator_pk]
|
||||
|
||||
users: Mapped[list["User"]] = relationship(
|
||||
secondary="received_roles_prize",
|
||||
back_populates="prize_roles",
|
||||
#lazy="selectin"
|
||||
)
|
||||
|
||||
class StaticRole(Base):
|
||||
__tablename__ = 'roles_static'
|
||||
|
||||
id: Mapped[discord_identificator_pk]
|
||||
description: Mapped[str | None] = mapped_column(String, nullable=True)
|
||||
|
||||
class ReceivedCustomRoles(Base):
|
||||
__tablename__ = 'received_roles_custom'
|
||||
|
||||
role_id: Mapped[int] = mapped_column(ForeignKey('roles_custom.id', ondelete='CASCADE'), primary_key=True)
|
||||
user_id: Mapped[int] = mapped_column(ForeignKey('users.id', ondelete='CASCADE'), primary_key=True)
|
||||
|
||||
__table_args__ = (
|
||||
UniqueConstraint('user_id', 'role_id', name='received_roles_custom_user_role'),
|
||||
)
|
||||
|
||||
class ReceivedPrizeRoles(Base):
|
||||
__tablename__ = 'received_roles_prize'
|
||||
|
||||
role_id: Mapped[int] = mapped_column(ForeignKey('roles_prize.id', ondelete='CASCADE'), primary_key=True)
|
||||
user_id: Mapped[int] = mapped_column(ForeignKey('users.id', ondelete='CASCADE'), primary_key=True)
|
||||
|
||||
__table_args__ = (
|
||||
UniqueConstraint('user_id', 'role_id', name='received_roles_prize_user_role'),
|
||||
)
|
||||
|
||||
#профили
|
||||
class ProfileDesign(Base):
|
||||
__tablename__ = 'profile_design'
|
||||
|
||||
id: Mapped[identificator_pk]
|
||||
file_name: Mapped[str] = mapped_column(String, nullable=False)
|
||||
type: Mapped[str | None] = mapped_column(String, server_default=text("'PNG'"), nullable=False)
|
||||
border_color: Mapped[list[int]] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[0, 0, 0, 255]"), nullable=False)
|
||||
border_width: Mapped[int | None] = mapped_column(Integer, server_default=text("3"), nullable=False)
|
||||
scale: Mapped[int | None] = mapped_column(Integer, server_default=text("4"), nullable=False)
|
||||
|
||||
blackout_background_size: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[452, 226]"), nullable=False)
|
||||
blackout_background_position: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[30, 15]"), nullable=False)
|
||||
blackout_background_color: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[0, 0, 0, 100]"), nullable=False)
|
||||
blackout_background_radius: Mapped[int | None] = mapped_column(Integer, server_default=text("15"), nullable=False)
|
||||
|
||||
avatar_size: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[100, 100]"), nullable=False)
|
||||
avatar_position: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[50, 25]"), nullable=False)
|
||||
|
||||
nick_size: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[145, 65]"), nullable=False)
|
||||
nick_position: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[30, 110]"), nullable=False)
|
||||
nick_color: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[255, 255, 255, 255]"), nullable=False)
|
||||
nick_stroke_width: Mapped[int | None] = mapped_column(Integer, server_default=text("6"), nullable=False)
|
||||
nick_stroke_color: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[0, 0, 0, 255]"), nullable=False)
|
||||
|
||||
progress_bar_size: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[400, 40]"), nullable=False)
|
||||
progress_bar_position: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[50, 175]"), nullable=False)
|
||||
progress_bar_fill_color: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[0, 255, 0, 255]"), nullable=False)
|
||||
progress_bar_empty_color: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[50, 50, 50, 255]"), nullable=False)
|
||||
progress_bar_radius: Mapped[int | None] = mapped_column(Integer, server_default=text("15"), nullable=False)
|
||||
progress_bar_text_position: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[15, 7]"), nullable=False)
|
||||
progress_bar_text_color: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[255, 255, 255, 255]"), nullable=False)
|
||||
progress_bar_text_stroke_width: Mapped[int | None] = mapped_column(Integer, server_default=text("6"), nullable=False)
|
||||
progress_bar_text_stroke_color: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[0, 0, 0, 255]"), nullable=False)
|
||||
|
||||
info_bar_size: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[275, 135]"), nullable=False)
|
||||
info_bar_position: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[175, 30]"), nullable=False)
|
||||
info_bar_color: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[255, 255, 255, 0]"), nullable=False)
|
||||
info_bar_radius: Mapped[int | None] = mapped_column(Integer, server_default=text("15"), nullable=False)
|
||||
info_bar_text_color: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[0, 0, 0, 255]"), nullable=False)
|
||||
info_bar_text_position: Mapped[list[int] | None] = mapped_column(ARRAY(Integer), server_default=text("ARRAY[15, 10]"), nullable=False)
|
||||
|
||||
render_profile_code: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
|
||||
def render_profile(self, data, namespace):
|
||||
#{user, place_in_top, avatar, nick, level, crumbs_modify} in data
|
||||
exec(self.render_profile_code, namespace)
|
||||
render_func = namespace['render']
|
||||
|
||||
return render_func(data, design = self)
|
||||
|
||||
usage: Mapped[list["ProfileDesignInventory"]] = relationship(
|
||||
back_populates="design",
|
||||
#lazy="selectin"
|
||||
)
|
||||
|
||||
class ProfileDesignInventory(Base):
|
||||
__tablename__ = 'profile_design_inventory'
|
||||
|
||||
user_id: Mapped[int] = mapped_column(ForeignKey('users.id', ondelete='CASCADE'), primary_key=True)
|
||||
design_id: Mapped[int] = mapped_column(ForeignKey('profile_design.id', ondelete='CASCADE'), primary_key=True)
|
||||
is_active: Mapped[bool | None] = mapped_column(Boolean, server_default=text("FALSE"), nullable=False)
|
||||
|
||||
design: Mapped["ProfileDesign"] = relationship(
|
||||
back_populates="usage",
|
||||
foreign_keys=[design_id],
|
||||
lazy="joined"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
UniqueConstraint('user_id', 'design_id', name='received_profile_design_inventory_user_design'),
|
||||
Index(
|
||||
'received_profile_design_inventory_user_only_one_active',
|
||||
'user_id',
|
||||
unique=True,
|
||||
postgresql_where=text('is_active')
|
||||
),
|
||||
)
|
||||
|
||||
#экономика
|
||||
class CrumbsTransactionHistory(Base):
|
||||
__tablename__ = 'transaction_history_crumbs'
|
||||
|
||||
id: Mapped[identificator_pk]
|
||||
sender_id: Mapped[int | None] = mapped_column(ForeignKey('users.id', ondelete='CASCADE'), nullable=True, default=None)
|
||||
recipient_id: Mapped[int | None] = mapped_column(ForeignKey('users.id', ondelete='CASCADE'), nullable=True, default=None)
|
||||
amount: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
commission_fraction: Mapped[float] = mapped_column(Float, default=0, nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(String, default=None, nullable=True)
|
||||
transaction_time: Mapped[float] = mapped_column(Float, nullable=False, server_default=text("EXTRACT(EPOCH FROM NOW())"))
|
||||
|
||||
sender: Mapped["User | None"] = relationship(
|
||||
back_populates="sender_in_crumbs_transactions",
|
||||
foreign_keys=[sender_id],
|
||||
uselist=False,
|
||||
#lazy="joined"
|
||||
)
|
||||
|
||||
recipient: Mapped["User | None"] = relationship(
|
||||
back_populates="recipient_in_crumbs_transactions",
|
||||
foreign_keys=[recipient_id],
|
||||
uselist=False,
|
||||
#lazy="joined"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
CheckConstraint('NOT (sender_id IS NULL AND recipient_id IS NULL)'),
|
||||
)
|
||||
|
||||
class CasinoUser(Base):
|
||||
__tablename__ = 'casino_user_account'
|
||||
|
||||
id: Mapped[int] = mapped_column(ForeignKey('users.id', ondelete='CASCADE'), primary_key=True, nullable=False)
|
||||
spins_today_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
||||
last_reset_time: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
||||
|
||||
user: Mapped["User"] = relationship(
|
||||
back_populates="casino_account",
|
||||
uselist=False,
|
||||
#lazy="joined"
|
||||
)
|
||||
|
||||
#rimagochi
|
||||
class RimagochiUser(Base):
|
||||
__tablename__ = 'rimagochi_users'
|
||||
|
||||
id: Mapped[int] = mapped_column(ForeignKey('users.id', ondelete='CASCADE'), primary_key=True, nullable=False, index = True)
|
||||
items: Mapped[dict] = mapped_column(JSON, default={}, nullable=False)
|
||||
genes: Mapped[dict] = mapped_column(JSON, default={}, nullable=False)
|
||||
wins: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
||||
settings: Mapped[dict] = mapped_column(JSON, nullable=False)
|
||||
|
||||
user: Mapped["User"] = relationship(
|
||||
back_populates="rimagochi_account",
|
||||
uselist=False,
|
||||
#lazy="joined"
|
||||
)
|
||||
|
||||
animals: Mapped[list["RimagochiAnimal"]] = relationship(
|
||||
back_populates="owner",
|
||||
uselist=True,
|
||||
#lazy="selectin"
|
||||
)
|
||||
|
||||
battle_slots_animals: Mapped[list["RimagochiAnimal"]] = relationship(
|
||||
primaryjoin=(
|
||||
"and_("
|
||||
"RimagochiAnimal.owner_id == RimagochiUser.id, "
|
||||
"RimagochiAnimal.in_battle_slots == True"
|
||||
")"
|
||||
),
|
||||
uselist=True,
|
||||
viewonly=True,
|
||||
#lazy="selectin"
|
||||
)
|
||||
|
||||
class RimagochiAnimal(Base):
|
||||
__tablename__ = 'rimagochi_animals'
|
||||
|
||||
id: Mapped[identificator_pk]
|
||||
model_animal_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
genes: Mapped[list[int]] = mapped_column(ARRAY(Integer), default=[], nullable=False)
|
||||
items: Mapped[list[int]] = mapped_column(ARRAY(Integer), default=[], nullable=False)
|
||||
last_feeding_time: Mapped[float] = mapped_column(Float, default=0, nullable=False)
|
||||
first_today_feed_time: Mapped[float] = mapped_column(Float, default=0, nullable=False)
|
||||
feed_today_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
||||
experience: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
||||
level: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
|
||||
wins: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
||||
initial_owner_id: Mapped[discord_identificator]
|
||||
owner_id: Mapped[int] = mapped_column(ForeignKey('rimagochi_users.id', ondelete='CASCADE'), nullable=False, index = True)
|
||||
in_battle_slots: Mapped[bool] = mapped_column(Boolean, default = False, nullable = False)
|
||||
creation_time: Mapped[float] = mapped_column(Float, nullable=False, server_default=text("EXTRACT(EPOCH FROM NOW())"))
|
||||
|
||||
owner: Mapped["RimagochiUser"] = relationship(
|
||||
back_populates="animals",
|
||||
foreign_keys = [owner_id],
|
||||
uselist=False,
|
||||
#lazy="joined"
|
||||
)
|
||||
|
||||
|
||||
all_data = {
|
||||
'base': Base
|
||||
}
|
||||
17
src/database/settings/config.py
Normal file
17
src/database/settings/config.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from database.settings.db_settings import *
|
||||
|
||||
class Settings:
|
||||
|
||||
def __init__(self):
|
||||
self.DB_HOST = DB_HOST
|
||||
self.DB_PORT = DB_PORT
|
||||
self.DB_USER = DB_USER
|
||||
self.DB_PASS = DB_PASS
|
||||
self.DB_NAME = DB_NAME
|
||||
|
||||
@property
|
||||
def DB_URL(self):
|
||||
return f"postgresql+asyncpg://{self.DB_USER}:{self.DB_PASS}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
|
||||
|
||||
settings = Settings()
|
||||
|
||||
5
src/database/settings/db_settings.py
Normal file
5
src/database/settings/db_settings.py
Normal file
@@ -0,0 +1,5 @@
|
||||
DB_HOST='localhost'
|
||||
DB_PORT=5432
|
||||
DB_USER='discord_economy_bot'
|
||||
DB_PASS='economy_bot'
|
||||
DB_NAME='discord_economy_bot_db'
|
||||
128
src/managers/DataBaseManager.py
Normal file
128
src/managers/DataBaseManager.py
Normal file
@@ -0,0 +1,128 @@
|
||||
import disnake
|
||||
from disnake.ext import commands
|
||||
from disnake.ext import tasks
|
||||
from typing import Optional, Union, List, Dict, Any, AsyncIterator, Tuple
|
||||
import datetime
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm import joinedload, selectinload, contains_eager
|
||||
from sqlalchemy import select, delete, insert, update, func, asc, desc
|
||||
from sqlalchemy import and_, or_, not_
|
||||
import subprocess
|
||||
import os
|
||||
from types import SimpleNamespace
|
||||
|
||||
class Model:
|
||||
def __init__(self, model):
|
||||
self.model = model
|
||||
self.m = model
|
||||
|
||||
async def __aenter__(self):
|
||||
return self.model
|
||||
|
||||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||
return None
|
||||
|
||||
class DatabaseManager:
|
||||
|
||||
def __init__(self, engine, tables_data):
|
||||
self.engine = engine
|
||||
self.session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
||||
self.metadata = tables_data['base'].metadata
|
||||
|
||||
self.model_classes = {}
|
||||
tables = self.metadata.tables
|
||||
for table_name, table in tables.items():
|
||||
model_class = next((cls for cls in tables_data['base'].__subclasses__() if cls.__tablename__ == table_name), None)
|
||||
if model_class:
|
||||
#print(f"Table: {table_name}, Model Class: {model_class.__name__}")
|
||||
self.model_classes[table_name] = model_class
|
||||
|
||||
self.models = {table_name: Model(model) for table_name, model in self.model_classes.items()}
|
||||
|
||||
self.tables_data = tables_data
|
||||
|
||||
#функции sqlalchemy
|
||||
self.joinedload = joinedload
|
||||
self.selectinload = selectinload
|
||||
self.contains_eager = contains_eager
|
||||
self.select = select
|
||||
self.delete = delete
|
||||
self.insert = insert
|
||||
self.update = update
|
||||
self.func = func
|
||||
self.desc = desc
|
||||
self.asc = asc
|
||||
self.and_ = and_
|
||||
self.or_ = or_
|
||||
self.not_ = not_
|
||||
|
||||
#ошибки sqlalchemy
|
||||
exceptions = SimpleNamespace(
|
||||
IntegrityError = IntegrityError
|
||||
)
|
||||
|
||||
async def close(self):
|
||||
await self.engine.dispose()
|
||||
|
||||
async def pg_dump(self, echo=False, backup_file='src/backups/discord_economy_bot_backup.sql'):
|
||||
conn = await self.engine.connect()
|
||||
db_name = self.engine.url.database
|
||||
user = self.engine.url.username
|
||||
host = self.engine.url.host
|
||||
port = self.engine.url.port
|
||||
password = self.engine.url.password
|
||||
|
||||
os.environ['PGPASSWORD'] = password
|
||||
command = [
|
||||
'pg_dump',
|
||||
'-h', host,
|
||||
'-p', str(port),
|
||||
'-U', user,
|
||||
'-F', 'p', # <-- plain text SQL
|
||||
] + (['-v'] if echo else []) + [
|
||||
'-f', backup_file,
|
||||
db_name
|
||||
]
|
||||
|
||||
try:
|
||||
subprocess.run(command, check=True)
|
||||
if echo:
|
||||
print(f"{datetime.datetime.now():%H:%M:%S %d-%m-%Y} :: SQL backup of '{db_name}' created.")
|
||||
return backup_file
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"{datetime.datetime.now():%H:%M:%S %d-%m-%Y} :: Backup failed: {e}")
|
||||
return f"{datetime.datetime.now():%H:%M:%S %d-%m-%Y} :: Backup failed: {e}"
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
async def pg_restore(self, echo = False, backup_file = 'src/backups/backup_file.backup'):
|
||||
conn = await self.DataBaseManager.engine.connect()
|
||||
db_name = self.DataBaseManager.engine.url.database
|
||||
user = self.DataBaseManager.engine.url.username
|
||||
host = self.DataBaseManager.engine.url.host
|
||||
port = self.DataBaseManager.engine.url.port
|
||||
password = self.DataBaseManager.engine.url.password
|
||||
|
||||
os.environ['PGPASSWORD'] = password # Установка пароля для подключения
|
||||
command = [
|
||||
'pg_restore',
|
||||
'-h', host,
|
||||
'-p', str(port),
|
||||
'-U', user,
|
||||
'-d', db_name, # Имя базы данных, в которую будет восстановлено
|
||||
] + ([
|
||||
'-v' # Подробный вывод
|
||||
] if echo else []) + [
|
||||
backup_file # Путь к файлу резервной копии
|
||||
]
|
||||
|
||||
try:
|
||||
subprocess.run(command, check=True)
|
||||
if echo:
|
||||
print(f"{datetime.datetime.now().strftime('%H:%M:%S %d-%m-%Y')}:: Database '{db_name}' restored successfully from '{backup_file}'.")
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"{datetime.datetime.now().strftime('%H:%M:%S %d-%m-%Y')}:: Error during restore: {e}")
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
239
src/managers/old_DataBaseManager.py
Normal file
239
src/managers/old_DataBaseManager.py
Normal file
@@ -0,0 +1,239 @@
|
||||
try:
|
||||
import aiosqlite
|
||||
import disnake
|
||||
from disnake.ext import commands
|
||||
from disnake.ext import tasks
|
||||
except:
|
||||
import pip
|
||||
|
||||
pip.main(['install', 'disnake'])
|
||||
pip.main(['install', 'aiosqlite'])
|
||||
import disnake
|
||||
from disnake.ext import commands
|
||||
from disnake.ext import tasks
|
||||
import aiosqlite
|
||||
from typing import Optional, Union, List, Dict, Any, AsyncIterator, Tuple
|
||||
from asyncio import Lock
|
||||
import datetime
|
||||
|
||||
class DatabaseManager:
|
||||
"""Расширенное соединение с БД с поддержкой транзакций и удобных методов"""
|
||||
|
||||
def __init__(self, connection: aiosqlite.Connection):
|
||||
self._connection = connection
|
||||
self._transaction_depth = 0
|
||||
self._closed = False
|
||||
self._transaction_lock = Lock()
|
||||
self.last_error = None
|
||||
|
||||
@classmethod
|
||||
async def connect(cls, database: str, **kwargs) -> 'DatabaseManager':
|
||||
"""Альтернатива конструктору для подключения"""
|
||||
connection = await aiosqlite.connect(database, **kwargs)
|
||||
return cls(connection)
|
||||
|
||||
async def UpdateBD(self, table: str, *, change: dict, where: dict, whereandor = "AND"):
|
||||
request = ()
|
||||
|
||||
change_request = []
|
||||
for i in change.keys():
|
||||
change_request.append(f"{i} = ?")
|
||||
request = request + (change[i],)
|
||||
|
||||
where_request = []
|
||||
for i in where.keys():
|
||||
where_request.append(f"{i} = ?")
|
||||
request = request + (where[i],)
|
||||
|
||||
await self.execute('UPDATE {table} SET {change} WHERE {where}'
|
||||
.format(table = table, change = ", ".join(change_request), where = f" {whereandor} ".join(where_request)), request)
|
||||
return 0
|
||||
|
||||
async def SelectBD(self, table: str, *, select: list = ["*"], where: dict = None, where_ops: dict = None, whereandor = "AND", order_by: str = None, limit: int = None):
|
||||
|
||||
where_combined = {}
|
||||
if where:
|
||||
where_combined.update({f"{k} =": v for k, v in where.items()})
|
||||
if where_ops:
|
||||
where_combined.update(where_ops)
|
||||
|
||||
request = ()
|
||||
where_clauses = ""
|
||||
|
||||
for condition, value in where_combined.items():
|
||||
field_op = condition.split()
|
||||
field = field_op[0]
|
||||
op = "=" if len(field_op) == 1 else field_op[1]
|
||||
if where_clauses == "":
|
||||
where_clauses= where_clauses + f"{field} {op} ? "
|
||||
else:
|
||||
where_clauses= where_clauses + f"{whereandor} {field} {op} ? "
|
||||
request += (value,)
|
||||
|
||||
query = "SELECT {select} FROM {table}".format(
|
||||
select=", ".join(select),
|
||||
table=table
|
||||
)
|
||||
|
||||
if where_clauses:
|
||||
query += f" WHERE {where_clauses}"
|
||||
|
||||
if order_by:
|
||||
query += f" ORDER BY {order_by}"
|
||||
|
||||
if limit:
|
||||
query += f" LIMIT {limit}"
|
||||
|
||||
async with await self.execute(query, request) as cursor:
|
||||
return [i for i in await cursor.fetchall()]
|
||||
|
||||
async def GetStaffJoins():
|
||||
query = \
|
||||
"""
|
||||
SELECT sur.userid, sur.roleid, sur.description, sur.starttime, sr.staffsalary, sbr.layer as rolelayer, sbr.branchid, sb.layer as branchlayer, sb.purpose
|
||||
FROM staff_users_roles AS sur
|
||||
JOIN staff_roles as sr ON sr.roleid = sur.roleid
|
||||
JOIN staff_branches_roles as sbr ON sbr.roleid = sur.roleid
|
||||
JOIN staff_branches as sb ON sb.branchid = sbr.branchid
|
||||
ORDER BY branchlayer ASC, rolelayer ASC;
|
||||
"""
|
||||
async with await self.execute(query, request) as cursor:
|
||||
answer = [i for i in await cursor.fetchall()]
|
||||
result = [{'userid': userid, 'roleid': roleid, 'description': description, 'starttime': starttime, 'staffsalary': staffsalary, 'rolelayer': rolelayer, 'branchid': branchid, 'branchlayer': branchlayer, 'purpose': purpose} for userid, roleid, description, starttime, staffsalary, rolelayer, branchid, branchlayer, purpose in answer]
|
||||
return result
|
||||
|
||||
async def DeleteBD(self, table: str, *, where: dict, whereandor = "AND"):
|
||||
request = ()
|
||||
where_request = []
|
||||
for i in where.keys():
|
||||
where_request.append(f"{i} = ?")
|
||||
request = request + (where[i],)
|
||||
await self.execute("DELETE FROM {table} where {where}"
|
||||
.format(table = table, where = f" {whereandor} ".join(where_request)), request)
|
||||
return 0
|
||||
|
||||
async def InsertBD(self, table: str, *, data: dict):
|
||||
request = ()
|
||||
keys = list(data.keys())
|
||||
qstring = []
|
||||
for i in keys:
|
||||
request = request + (data[i],)
|
||||
qstring.append("?")
|
||||
await self.execute("INSERT INTO {table}({keys}) VALUES({values})"
|
||||
.format(table = table, keys = ", ".join(keys), values = ", ".join(qstring)), request)
|
||||
return 0
|
||||
|
||||
async def execute(self, sql: str, parameters: Optional[Union[Tuple, Dict]] = None, **kwargs) -> aiosqlite.Cursor:
|
||||
"""
|
||||
Универсальный execute, который автоматически определяет:
|
||||
- Нужно ли начинать транзакцию (для INSERT/UPDATE/DELETE вне транзакции)
|
||||
- Работает ли уже внутри транзакции (не создаёт вложенные транзакции)
|
||||
"""
|
||||
is_modifying = sql.strip().upper().startswith(("INSERT", "UPDATE", "DELETE"))
|
||||
|
||||
# Если это модифицирующий запрос И мы НЕ внутри транзакции
|
||||
if is_modifying and self._transaction_depth == 0:
|
||||
async with self: # Автоматические begin/commit
|
||||
cursor = await self._connection.execute(sql, parameters or (), **kwargs)
|
||||
await cursor.close() # Важно: закрываем курсор для COMMIT
|
||||
return cursor
|
||||
else:
|
||||
# Для SELECT или работы внутри существующей транзакции
|
||||
return await self._connection.execute(sql, parameters or (), **kwargs)
|
||||
|
||||
async def fetch_all(self, sql: str, parameters: Optional[Union[Tuple, Dict]] = None) -> List[Tuple]:
|
||||
"""Выполняет запрос и возвращает все строки"""
|
||||
async with await self.execute(sql, parameters) as cursor:
|
||||
return await cursor.fetchall()
|
||||
|
||||
async def fetch_one(self, sql: str, parameters: Optional[Union[Tuple, Dict]] = None) -> Optional[Tuple]:
|
||||
"""Выполняет запрос и возвращает первую строку"""
|
||||
async with await self.execute(sql, parameters) as cursor:
|
||||
return await cursor.fetchone()
|
||||
|
||||
async def fetch_val(self, sql: str, parameters: Optional[Union[Tuple, Dict]] = None, column: int = 0) -> Any:
|
||||
"""Возвращает значение из первого столбца"""
|
||||
row = await self.fetch_one(sql, parameters)
|
||||
return row[column] if row else None
|
||||
|
||||
async def insert(self, table: str, data: Dict[str, Any], on_conflict: str = None) -> int:
|
||||
"""Упрощенный INSERT с поддержкой ON CONFLICT"""
|
||||
keys = data.keys()
|
||||
values = list(data.values())
|
||||
|
||||
sql = f"""
|
||||
INSERT INTO {table} ({', '.join(keys)})
|
||||
VALUES ({', '.join(['?']*len(keys))})
|
||||
"""
|
||||
|
||||
if on_conflict:
|
||||
sql += f" ON CONFLICT {on_conflict}"
|
||||
|
||||
await self.execute(sql, values)
|
||||
return self.lastrowid
|
||||
|
||||
async def update(self, table: str, where: Dict[str, Any], changes: Dict[str, Any], where_operator: str = "AND") -> int:
|
||||
"""Упрощенный UPDATE с автоматическим построением WHERE"""
|
||||
set_clause = ", ".join([f"{k} = ?" for k in changes.keys()])
|
||||
where_clause = f" {where_operator} ".join([f"{k} = ?" for k in where.keys()])
|
||||
|
||||
sql = f"""
|
||||
UPDATE {table}
|
||||
SET {set_clause}
|
||||
WHERE {where_clause}
|
||||
"""
|
||||
|
||||
result = await self.execute(sql, [*changes.values(), *where.values()])
|
||||
return result.rowcount
|
||||
|
||||
async def begin(self):
|
||||
"""Начать транзакцию (с поддержкой вложенности)"""
|
||||
async with self._transaction_lock:
|
||||
if self._transaction_depth == 0:
|
||||
await self._connection.execute("BEGIN IMMEDIATE")
|
||||
self._transaction_depth += 1
|
||||
|
||||
async def commit(self):
|
||||
"""Зафиксировать транзакцию"""
|
||||
async with self._transaction_lock:
|
||||
if self._transaction_depth == 1:
|
||||
await self._connection.commit()
|
||||
self._transaction_depth = max(0, self._transaction_depth - 1)
|
||||
|
||||
async def rollback(self):
|
||||
"""Откатить транзакцию"""
|
||||
async with self._transaction_lock:
|
||||
if self._transaction_depth > 0:
|
||||
await self._connection.rollback()
|
||||
self._transaction_depth = 0
|
||||
|
||||
async def close(self) -> None:
|
||||
"""Безопасное закрытие соединения с учётом транзакций"""
|
||||
async with self._transaction_lock:
|
||||
try:
|
||||
# Откатываем активную транзакцию, если есть
|
||||
if self._transaction_depth > 0:
|
||||
await self._connection.rollback()
|
||||
self._transaction_depth = 0
|
||||
|
||||
# Закрываем соединение
|
||||
if hasattr(self._connection, '_connection'): # Проверка внутреннего состояния
|
||||
await self._connection.close()
|
||||
except Exception as e:
|
||||
self.last_error = f"{datetime.datetime.now().strftime('%H:%M:%S %d-%m-%Y')}:: Ошибка при закрытии соединения: {e}"
|
||||
print(f"{datetime.datetime.now().strftime('%H:%M:%S %d-%m-%Y')}:: Ошибка при закрытии соединения: {e}")
|
||||
finally:
|
||||
# Помечаем соединение как закрытое
|
||||
self._closed = True
|
||||
|
||||
async def __aenter__(self):
|
||||
await self.begin() # Используем собственный метод begin
|
||||
return self # Возвращаем сам менеджер, а не соединение
|
||||
|
||||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||
if exc_type is None:
|
||||
await self.commit()
|
||||
else:
|
||||
self.last_error = f"{datetime.datetime.now().strftime('%H:%M:%S %d-%m-%Y')}:: Во время записи в бд произошла ошибка: {exc_type}({exc_val}): {exc_tb.tb_frame.f_code.co_filename}(строка {exc_tb.tb_lineno})!"
|
||||
print(f"{datetime.datetime.now().strftime('%H:%M:%S %d-%m-%Y')}:: Во время записи в бд произошла ошибка: {exc_type}({exc_val}): {exc_tb.tb_frame.f_code.co_filename}(строка {exc_tb.tb_lineno})!")
|
||||
await self.rollback()
|
||||
84
src/test.py
Normal file
84
src/test.py
Normal file
@@ -0,0 +1,84 @@
|
||||
|
||||
import disnake
|
||||
from disnake.ext import commands
|
||||
from disnake.ext import tasks
|
||||
import requests
|
||||
import numpy as np
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
import copy
|
||||
import datetime
|
||||
import math
|
||||
import random
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
from constants.global_constants import *
|
||||
from data.TOKENS import TOKENS
|
||||
|
||||
import CoreFun
|
||||
|
||||
|
||||
async def main():
|
||||
stop_event = asyncio.Event()
|
||||
sup_bot = None
|
||||
DataBase = None
|
||||
all_bots = []
|
||||
|
||||
try:
|
||||
DataBase = await CoreFun.init_db()
|
||||
#await CoreFun.db_migration(DataBase)
|
||||
|
||||
'''
|
||||
async with DataBase.session() as session:
|
||||
async with session.begin():
|
||||
stmt = DataBase.select(DataBase.model_classes['users']).where(
|
||||
DataBase.model_classes['users'].id == 479210801891115009
|
||||
).options(
|
||||
DataBase.selectinload(DataBase.model_classes['users'].custom_roles)
|
||||
.selectinload(DataBase.model_classes['roles_custom'].creator)
|
||||
)
|
||||
user = (await session.execute(stmt)).scalars().first()
|
||||
for role in user.custom_roles:
|
||||
print("role: " + str(role.to_dict()), "creator: " + str(role.creator.to_dict()), sep = "\n")
|
||||
|
||||
'''
|
||||
|
||||
|
||||
sup_bot = CoreFun.AdminBot(DataBase, stop_event, task_start = False)
|
||||
all_bots = [sup_bot]
|
||||
|
||||
# Загрузка когов
|
||||
sup_bot.load_extension("cogs.resetsupcommands")
|
||||
#sup_bot.load_extension("cogs.economy")
|
||||
sup_bot.load_extension("cogs.designer")
|
||||
#sup_bot.load_extension("cogs.roles")
|
||||
#sup_bot.load_extension("cogs.admin")
|
||||
#sup_bot.load_extension("cogs.rimagochi")
|
||||
|
||||
# Запуск монитора остановки и ботов
|
||||
monitor_task = asyncio.create_task(CoreFun.monitor_stop(stop_event, all_bots))
|
||||
bot_tasks = [
|
||||
asyncio.create_task(CoreFun.run_bot(sup_bot, TOKENS["KrekSupBot"], stop_event)),
|
||||
]
|
||||
|
||||
await asyncio.gather(*bot_tasks, monitor_task)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(f"{datetime.datetime.now().strftime('%H:%M:%S %d-%m-%Y')}:: Боты остановлены по запросу пользователя")
|
||||
except Exception as e:
|
||||
print(f"{datetime.datetime.now().strftime('%H:%M:%S %d-%m-%Y')}:: Произошла критическая ошибка: {e}")
|
||||
finally:
|
||||
|
||||
# Остановка всех ботов
|
||||
stop_event.set()
|
||||
for bot in all_bots:
|
||||
if not bot.is_closed():
|
||||
await bot.close()
|
||||
|
||||
await DataBase.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
74
src/testbase.py
Normal file
74
src/testbase.py
Normal file
@@ -0,0 +1,74 @@
|
||||
from constants.rimagochi_constants import *
|
||||
import itertools
|
||||
# Данные из файла constants.py
|
||||
animals = rimagochi_animals
|
||||
|
||||
# Рассчитываем эффективность для каждого животного
|
||||
animal_stats = []
|
||||
for animal_id, animal_data in animals.items():
|
||||
#if not animal_data["params"]["rarity"] == rimagochi_rarity[31]:
|
||||
health = animal_data["params"]["health"]
|
||||
damage = animal_data["params"]["damage"]
|
||||
slots = animal_data["params"]["required_slots"]
|
||||
efficiency = (health * damage) / slots
|
||||
animal_stats.append({
|
||||
"id": animal_id,
|
||||
"name": animal_data["name"],
|
||||
"health": health,
|
||||
"damage": damage,
|
||||
"slots": slots,
|
||||
"efficiency": efficiency
|
||||
})
|
||||
|
||||
# Сортируем по убыванию эффективности
|
||||
animal_stats.sort(key=lambda x: -x["efficiency"])
|
||||
|
||||
for animal_data in animal_stats:
|
||||
print(f"{animal_data['name'].replace(' ', '_')} {animal_data['damage']} {animal_data['health']} {animal_data['slots']} {animal_data['efficiency']}".replace(".", ","))
|
||||
|
||||
best_animals = {}
|
||||
|
||||
for animal in animal_stats:
|
||||
best_animals.setdefault(animal["slots"], animal)
|
||||
|
||||
|
||||
the_best = [[1]]
|
||||
|
||||
for i in range(8):
|
||||
for animals in itertools.product(best_animals.keys(), repeat = i):
|
||||
if sum(animals) == 8:
|
||||
sum_damage = 0
|
||||
for i in animals:
|
||||
sum_damage += best_animals[i]['efficiency']*best_animals[i]['slots']
|
||||
sum_best_damage = 0
|
||||
for i in the_best[0]:
|
||||
sum_best_damage += best_animals[i]['efficiency']*best_animals[i]['slots']
|
||||
if sum_damage > sum_best_damage:
|
||||
the_best = [sorted(animals, reverse = True)]
|
||||
elif sum_damage == sum_best_damage:
|
||||
if not sorted(animals, reverse = True) in the_best:
|
||||
the_best.append(sorted(animals, reverse = True))
|
||||
|
||||
|
||||
# Вывод результатов
|
||||
print("\n\nНайденные комбинации:")
|
||||
i=0
|
||||
for animals in the_best:
|
||||
total_slots = 0
|
||||
total_health = 0
|
||||
total_damage = 0
|
||||
total_efficiency = 0
|
||||
i+=1
|
||||
print(f"Комбинация №{i}")
|
||||
for animal in animals:
|
||||
total_slots += best_animals[animal]['slots']
|
||||
total_health += best_animals[animal]['health']
|
||||
total_damage += best_animals[animal]['damage']
|
||||
total_efficiency += best_animals[animal]['efficiency']*best_animals[animal]['slots']
|
||||
print(f"{best_animals[animal]['name']} (Здоровье: {best_animals[animal]['health']}, Урон: {best_animals[animal]['damage']}, Слоты: {best_animals[animal]['slots']})")
|
||||
print(f"\nСуммарное здоровье: {total_health}")
|
||||
print(f"Суммарный урон: {total_damage}")
|
||||
print(f"Суммарная эффективность: {total_efficiency}")
|
||||
print(f"Занято слотов: {total_slots}\n"+"--"*40)
|
||||
|
||||
print(max(rimagochi_animals.keys()))
|
||||
Reference in New Issue
Block a user