Добавил проект с учетом .gitignore

This commit is contained in:
2025-07-06 19:24:54 +03:00
parent 41ad5a5b92
commit e796892942
20 changed files with 6162 additions and 3 deletions

5
.gitignore vendored
View File

@@ -1,13 +1,12 @@
# Игнорируем папку виртуального окружения
venv/ venv/
nb_venv/ nb_venv/
pc_venv/ pc_venv/
# Кэш Python docs/
__pycache__/ __pycache__/
*.pyc *.pyc
# Файлы с секретами (токены, пароли)
.env .env
.secrets .secrets
src/data/ src/data/

0
README.md Normal file
View File

9
exclude.txt Normal file
View File

@@ -0,0 +1,9 @@
venv/
nb_venv/
pc_venv/
docs/
__pycache__/
*.pyc
.env
.secrets
backups/

42
requirements.txt Normal file
View File

@@ -0,0 +1,42 @@
aiohappyeyeballs==2.6.1
aiohttp==3.12.12
aiosignal==1.3.2
alabaster==1.0.0
async-timeout==5.0.1
asyncpg==0.30.0
attrs==25.3.0
babel==2.17.0
certifi==2025.4.26
charset-normalizer==3.4.2
colorama==0.4.6
disnake==2.10.1
docutils==0.21.2
frozenlist==1.7.0
greenlet==3.2.3
idna==3.10
imageio==2.37.0
imagesize==1.4.1
Jinja2==3.1.6
MarkupSafe==3.0.2
multidict==6.4.4
numpy==2.2.6
packaging==25.0
pillow==11.3.0
propcache==0.3.2
Pygments==2.19.2
requests==2.32.4
snowballstemmer==3.0.1
Sphinx==8.1.3
sphinx-rtd-theme==3.0.2
sphinxcontrib-applehelp==2.0.0
sphinxcontrib-devhelp==2.0.0
sphinxcontrib-htmlhelp==2.1.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==2.0.0
sphinxcontrib-serializinghtml==2.0.0
SQLAlchemy==2.0.41
tomli==2.2.1
typing_extensions==4.14.0
urllib3==2.4.0
yarl==1.20.1

810
src/CoreFun.py Normal file
View 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
View 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
View 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
View 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))

View 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

File diff suppressed because it is too large Load Diff

927
src/cogs/roles.py Normal file
View 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)

View 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,
}

File diff suppressed because it is too large Load Diff

401
src/database/db_classes.py Normal file
View 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
}

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

View 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'

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

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