From 618b2b7fbc40fe1a0e40cba55d3abc8994571ac4 Mon Sep 17 00:00:00 2001 From: HypoxiE Date: Thu, 7 Aug 2025 17:24:48 +0300 Subject: [PATCH] =?UTF-8?q?MVP=20=D1=81=D0=B8=D1=81=D1=82=D0=B5=D0=BC?= =?UTF-8?q?=D1=8B=20=D0=BE=D1=82=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D1=85=20=D1=81=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CoreMod.py | 21 ++++++++++++++++++++ src/cogs/administrators.py | 39 +++++++++++++++++++++++++++++++++++++- src/database/db_classes.py | 20 +++++++++++++------ 3 files changed, 73 insertions(+), 7 deletions(-) diff --git a/src/CoreMod.py b/src/CoreMod.py index 6dea025..b7dd291 100644 --- a/src/CoreMod.py +++ b/src/CoreMod.py @@ -288,6 +288,8 @@ class MainBot(AnyBots): self.MakeBackups.start() self.CheckDataBases.start() + else: + self.SendingDeferredMessages.start() async def BotOff(self): if self.task_start: @@ -303,6 +305,25 @@ class MainBot(AnyBots): print(f"{datetime.datetime.now().strftime('%H:%M:%S %d-%m-%Y')}:: Соединение с дискордом разорвано") await self.BotOff() + @tasks.loop(seconds=60) + async def SendingDeferredMessages(self): + try: + async with self.DataBaseManager.session() as session: + async with session.begin(): + stmt = self.DataBaseManager.select(self.DataBaseManager.model_classes['scheduled_messages']).where( + self.DataBaseManager.model_classes['scheduled_messages'].timestamp - datetime.datetime.now().timestamp() <= 0 + ).with_for_update() + messages = (await session.execute(stmt)).scalars().all() + + for message in messages: + webhook = await self.fetch_webhook(message.webhook_id) + await webhook.send(await message.parse_message(self)) + + await session.delete(message) + + except Exception as error: + print(f"{datetime.datetime.now().strftime('%H:%M:%S %d-%m-%Y')}:: err SendingDeferredMessages: {error}") + @tasks.loop(seconds=60) async def CheckDataBases(self): try: diff --git a/src/cogs/administrators.py b/src/cogs/administrators.py index 8ff1b2b..44e1486 100644 --- a/src/cogs/administrators.py +++ b/src/cogs/administrators.py @@ -1,3 +1,4 @@ +import re import disnake from disnake.ext import commands from disnake.ext import tasks @@ -207,4 +208,40 @@ class AdminModule(commands.Cog): user_role = await self.DataBaseManager.model_classes['staff_users_roles'].create_with_auto_branch(session, user_id = userid, role_id = roleid, description = description) session.add(user_role) await ctx.send(embed = self.client.SuccessEmbed(description = f'Пользователю <@{userid}> успешно назначена роль <@&{roleid}>. \n```diff\n- Эта функция не выдаёт роли автоматически, поэтому требуется выдача вручную.```', colour = 0xff9900)) - return 0 \ No newline at end of file + return 0 + + @commands.slash_command(name="запланировать_сообщение", description="Позволяет запланировать отправку анонсов и других сообщений") + async def schedule_message(self, ctx: disnake.AppCmdInter, + message_id: str = commands.Param(description="Укажите id сообщения, которое будет отложено", name="сообщение"), + webhook_link: str = commands.Param(description="Укажите ссылку на вебхук, от которого будет отправлено сообщение(по умолчанию от лица бота)", name="вебхук", default=None), + timestamp: int = commands.Param(description="Временная метка для отправки сообщения", name="таймстамп", default=None)): + + await ctx.response.defer() + + def extract_webhook_id(webhook_url: str) -> int | None: + pattern = r"^https:\/\/(?:canary\.|ptb\.)?discord(?:app)?\.com\/api\/webhooks\/(\d+)\/[\w\-]+$" + match = re.match(pattern, webhook_url) + if match: + return int(match.group(1)) + return None + + if webhook_link is None: + webhook_id = None + else: + webhook_id = extract_webhook_id(webhook_link) + if webhook_id is None: + await ctx.edit_original_response(embed = self.client.ErrEmbed(description = f'Некорректная ссылка')) + return 1 + + async with self.DataBaseManager.session() as session: + async with session.begin(): + if not (await self.DataBaseManager.model_classes['staff_users'].is_admin_or_moder_by_id(ctx.author.id, self.DataBaseManager, session, is_admin=True, is_moder=False)): + await ctx.edit_original_response(embed = self.client.ErrEmbed(description = f'У вас недостаточно полномочий, чтобы оставлять отложенные сообщения.')) + return 1 + else: + scheduled_message_model = self.DataBaseManager.model_classes['scheduled_messages'] + message = scheduled_message_model(source_channel_id=ctx.channel.id, source_message_id=int(message_id), webhook_id=webhook_id, timestamp=timestamp) + session.add(message) + + await ctx.edit_original_response(embed = self.client.SuccessEmbed(description = f'Успешно! Текст сообщения:\n {await message.parse_message(self.client)}', colour = 0xff9900)) + return 0 diff --git a/src/database/db_classes.py b/src/database/db_classes.py index 17b8cef..93f4f27 100644 --- a/src/database/db_classes.py +++ b/src/database/db_classes.py @@ -152,7 +152,7 @@ class StaffUser(Base): 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): + 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'] @@ -162,8 +162,8 @@ class StaffUser(Base): .where( DBManager.and_( DBManager.or_( - staff_branches_model.is_admin == True, - staff_branches_model.is_moder == True, + (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 ) @@ -233,10 +233,18 @@ class AllowedDomain(Base): class ScheduledMessage(Base): __tablename__ = "scheduled_messages" - message_id: Mapped[discord_identificator_pk] - webhook_id: Mapped[discord_identificator] - timestamp: Mapped[float | None] = mapped_column(Float) + 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): + 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) + + content = source_message.content + return content all_data = { 'base': Base