Files
krekbot-moderation/src/database/db_classes.py

270 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import io
import disnake
from sqlalchemy import Column, Integer, BigInteger, Text, Float, ForeignKey, UniqueConstraint, MetaData, Boolean
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 typing import Annotated
class Base(DeclarativeBase):
def get_table_name(self):
return self.__tablename__
def to_dict(self, exclude: list[str] | None = 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 PunishmentTextMute(Base):
__tablename__ = 'punishment_mutes_text'
id: Mapped[identificator_pk]
user_id: Mapped[discord_identificator]
reason: Mapped[str | None] = mapped_column(Text)
time_end: Mapped[float | None] = mapped_column(Float)
time_warn: Mapped[float | None] = mapped_column(Float)
time_begin: Mapped[float] = mapped_column(Float, nullable=False, server_default=text("EXTRACT(EPOCH FROM NOW())"))
moderator_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True, index=True, default = None)
class PunishmentVoiceMute(Base):
__tablename__ = 'punishment_mutes_voice'
id: Mapped[identificator_pk]
user_id: Mapped[discord_identificator]
reason: Mapped[str | None] = mapped_column(Text)
time_end: Mapped[float | None] = mapped_column(Float)
time_warn: Mapped[float | None] = mapped_column(Float)
time_begin: Mapped[float] = mapped_column(Float, nullable=False, server_default=text("EXTRACT(EPOCH FROM NOW())"))
moderator_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True, index=True, default = None)
class PunishmentWarn(Base):
__tablename__ = 'punishment_warns'
id: Mapped[identificator_pk]
user_id: Mapped[discord_identificator]
reason: Mapped[str | None] = mapped_column(Text)
time_warn: Mapped[float] = mapped_column(Float)
time_begin: Mapped[float] = mapped_column(Float, nullable=False, server_default=text("EXTRACT(EPOCH FROM NOW())"))
moderator_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True, index=True, default = None)
class PunishmentBan(Base):
__tablename__ = 'punishment_bans'
id: Mapped[identificator_pk]
user_id: Mapped[discord_identificator]
reason: Mapped[str | None] = mapped_column(Text)
time_end: Mapped[float | None] = mapped_column(Float)
time_begin: Mapped[float] = mapped_column(Float, nullable=False, server_default=text("EXTRACT(EPOCH FROM NOW())"))
moderator_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True, index=True, default = None)
class PunishmentPerm(Base):
__tablename__ = 'punishment_perms'
id: Mapped[identificator_pk]
user_id: Mapped[discord_identificator]
reason: Mapped[str | None] = mapped_column(Text)
time_begin: Mapped[float] = mapped_column(Float, nullable=False, server_default=text("EXTRACT(EPOCH FROM NOW())"))
moderator_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True, index=True, default = None)
class PunishmentReprimand(Base):
__tablename__ = 'punishment_reprimands'
id: Mapped[identificator_pk]
user_id: Mapped[discord_identificator]
reason: Mapped[str | None] = mapped_column(Text)
time_warn: Mapped[float] = mapped_column(Float)
branch_id: Mapped[int] = mapped_column(ForeignKey('staff_branches.id', ondelete='CASCADE'))
time_begin: Mapped[float] = mapped_column(Float, nullable=False, server_default=text("EXTRACT(EPOCH FROM NOW())"))
designated_user_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True, index=True, default = None)
# Модели для системы персонала
class StaffBranch(Base):
__tablename__ = 'staff_branches'
id: Mapped[identificator_pk]
layer: Mapped[int] = mapped_column(Integer, nullable=False)
purpose: Mapped[str] = mapped_column(Text)
is_admin: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
is_moder: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
roles: Mapped[list["StaffRole"]] = relationship(
back_populates="branch",
primaryjoin="StaffBranch.id==StaffRole.branch_id"
)
users: Mapped[list["StaffUserRole"]] = relationship(
back_populates="branch",
primaryjoin="StaffBranch.id==StaffUserRole.branch_id"
)
curations: Mapped[list["StaffCuration"]] = relationship(
back_populates="branch",
primaryjoin="StaffBranch.id==StaffCuration.branch_id"
)
class StaffRole(Base):
__tablename__ = 'staff_roles'
id: Mapped[discord_identificator_pk]
layer: Mapped[int] = mapped_column(Integer, nullable=False)
staff_salary: Mapped[int] = mapped_column(Integer, nullable=False)
branch_id: Mapped[int] = mapped_column(ForeignKey('staff_branches.id', ondelete='CASCADE'))
branch: Mapped["StaffBranch"] = relationship(
back_populates="roles",
primaryjoin="StaffRole.branch_id==StaffBranch.id"
)
users: Mapped[list["StaffUserRole"]] = relationship(
back_populates="role",
primaryjoin="StaffRole.id==StaffUserRole.role_id"
)
class StaffUser(Base):
__tablename__ = 'staff_users'
id: Mapped[discord_identificator_pk]
roles: Mapped[list["StaffUserRole"]] = relationship(back_populates="user")
curators: Mapped[list["StaffCuration"]] = relationship(
back_populates="apprentice",
primaryjoin="StaffUser.id==StaffCuration.apprentice_id"
)
apprentices: Mapped[list["StaffCuration"]] = relationship(
back_populates="curator",
primaryjoin="StaffUser.id==StaffCuration.curator_id"
)
async def is_admin_or_moder(self, DBManager, session):
return await self.__class__.is_admin_or_moder_by_id(self.id, DBManager, session)
@staticmethod
async def is_admin_or_moder_by_id(member_id, DBManager, session, *, is_admin=True, is_moder=True):
staff_branches_model = DBManager.model_classes['staff_branches']
staff_users_roles_model = DBManager.model_classes['staff_users_roles']
stmt = (
DBManager.select(staff_users_roles_model)
.join(staff_branches_model, staff_branches_model.id == staff_users_roles_model.branch_id)
.where(
DBManager.and_(
DBManager.or_(
(staff_branches_model.is_admin == True) if is_admin else False,
(staff_branches_model.is_moder == True) if is_moder else False,
),
staff_users_roles_model.user_id == member_id
)
)
)
result = await session.execute(stmt)
return result.scalars().first() is not None
class StaffUserRole(Base):
__tablename__ = 'staff_users_roles'
id: Mapped[identificator_pk]
user_id: Mapped[int] = mapped_column(ForeignKey('staff_users.id', ondelete='CASCADE'))
role_id: Mapped[int] = mapped_column(ForeignKey('staff_roles.id', ondelete='CASCADE'))
branch_id: Mapped[int] = mapped_column(ForeignKey('staff_branches.id', ondelete='CASCADE'))
description: Mapped[str | None] = mapped_column(Text)
update_time: Mapped[float] = mapped_column(Float,
server_default=text("EXTRACT(EPOCH FROM NOW())"),
server_onupdate=text("EXTRACT(EPOCH FROM NOW())")
)
user: Mapped["StaffUser"] = relationship(back_populates="roles")
branch: Mapped["StaffBranch"] = relationship(back_populates="users")
role: Mapped["StaffRole"] = relationship(back_populates="users")
__table_args__ = (
UniqueConstraint('user_id', 'branch_id', name='uq_user_branch'),
)
@classmethod
async def create_with_auto_branch(cls, session, user_id: int, role_id: int, **kwargs):
# Получаем роль
role = await session.get(StaffRole, role_id)
if not role:
raise ValueError("Роль не найдена")
return cls(user_id=user_id, role_id=role_id, branch_id=role.branch_id, **kwargs)
class StaffCuration(Base):
__tablename__ = 'staff_curation'
id: Mapped[identificator_pk]
apprentice_id: Mapped[int] = mapped_column(ForeignKey('staff_users.id', ondelete='CASCADE'))
curator_id: Mapped[int] = mapped_column(ForeignKey('staff_users.id', ondelete='CASCADE'))
branch_id: Mapped[int] = mapped_column(ForeignKey('staff_branches.id', ondelete='CASCADE'))
apprentice: Mapped["StaffUser"] = relationship(
back_populates="curators",
foreign_keys=[apprentice_id]
)
curator: Mapped["StaffUser"] = relationship(
back_populates="apprentices",
foreign_keys=[curator_id]
)
branch: Mapped["StaffBranch"] = relationship(back_populates="curations")
__table_args__ = (
UniqueConstraint('apprentice_id', 'curator_id', 'branch_id', name='uq_apprentice_curator_branch'),
)
class AllowedDomain(Base):
__tablename__ = "аllowed_domains"
id: Mapped[identificator_pk]
domain: Mapped[str] = mapped_column(Text, index=True, nullable=False, unique=True)
initiator_id: Mapped[discord_identificator]
class ScheduledMessage(Base):
__tablename__ = "scheduled_messages"
source_message_id: Mapped[discord_identificator_pk]
source_channel_id: Mapped[discord_identificator]
webhook_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True, index=True)
timestamp: Mapped[float | None] = mapped_column(Float, nullable=True, index=True)
async def parse_message(self, client, *, preview=False):
krekchat = await client.fetch_guild(client.krekchat.id)
source_channel = await krekchat.fetch_channel(self.source_channel_id)
source_message = await source_channel.fetch_message(self.source_message_id)
files = []
for attachment in source_message.attachments:
file_bytes = await attachment.read()
files.append(disnake.File(io.BytesIO(file_bytes), filename=attachment.filename))
content = source_message.content if not preview else source_message.clean_content
def make_dict(**kwargs):
return kwargs
args = make_dict(content=content,
embeds=source_message.embeds,
components=source_message.components,
files=files if files else None
)
return args
all_data = {
'base': Base
}