Возможно ли реализовать очередь из отложенных новостей в Discord?
Вопрос: есть ли возможность сделать несколько параллельно работающих отложенных сообщений, которые не будут блокировать остальной функционал и будут отправляться в заданные дату и время?
Условия таковы, что необходимо задавать сообщение и время не через слэшовую def функцию, а через лс.
Например, сначала присылается текст, записывается в переменную, потом картинки, кнопка и т.д.
Реально ли при таких условиях сделать их несколько? Или из-за наличия переменных, которые каждый раз перезаписываются, это нужно переделывать?
Подобной возможности нет в самом Discord. Например, как в том же VK или Telegram.
Но есть tasks и библиотека shedule, которая позволяет задавать время. Однако tasks делает не в задаваемое время, а в каждое указанное время (каждые 5-10-15 секунд-минут-часов).
shedule хоть и может отправлять в определенное время
every().day.at("время").do(job)
,
но не может указать дату и блокирует весь остальной функционал, то есть можно запустить только одну задачу, без параллельного выполнения.
Да, можно запустить проверку в отдельном треде через thread.start()
, но опять же только на одно сообщение. Не очень удобно.
Также пробовал вариант со сравнением текущего времени и нужного, но там опять блокируется функционал на одной задаче.
Получилось прийти к вот такому варианту, но только для одного сообщения:
@tasks.loop(seconds=5)
async def late():
global need_date
if datetime.datetime.now().replace(microsecond=0)==need_date:
await msg.publish()
if need_date!=None:
late.start()
Ответы (2 шт):
Возможно. Можно использовать asyncio.run_coroutine_threadsafe, если я правильно понял ваш вопрос.
@self.tree.command(name="send_after", description="Отправляет запланированное сообщение", guild=self.guild_object)
@discord.app_commands.describe(message = "Планируемое сообщение", minutes = "Через сколько минут отправить")
async def send_after(interaction: discord.Interaction, message: str, minutes: int):
async def send_mes(channel: discord.TextChannel, message: str, minutes: int):
start: datetime = datetime.datetime.now()
while True:
if datetime.datetime.now()-start >= datetime.timedelta(minutes=minutes):
await channel.send(message)
break
await asyncio.sleep(0.5)
await interaction.response.send_message(f"Ваше сообщение отправится через {minutes} мин.", ephemeral=True)
asyncio.run_coroutine_threadsafe(send_mes(channel=interaction.channel, message=message, minutes=minutes), bot.loop)
from asyncio import run_coroutine_threadsafe, sleep
from datetime import datetime, timedelta
from discord.app_commands import CommandTree, describe
from discord import Interaction, Object, Client, Intents, TextChannel, Embed, Colour, Message
class Bot(Client):
def __init__(self) -> None:
Client.__init__(self, intents=Intents.all())
GUILD_ID: int = 969610478139629568
self.tree: CommandTree = CommandTree(self)
self.guild_object: Object = Object(id=969610478139629568)
self.posts: dict = {}
@self.tree.command(name="create_post", description="Создаёт образец поста", guild=self.guild_object)
@describe(title="Заголовок поста", colour="Цвет боковой линии (hex значение или 'rgb(r, g, b)')")
async def create_post(interaction: Interaction, title: str, colour: str):
embed: Embed = Embed(title=title, colour=Colour.from_str(colour))
embed.set_author(name=interaction.user.display_name, icon_url=interaction.user.avatar.url)
self.posts[interaction.user.id] = {"embed": embed}
self.posts[interaction.user.id]["sample_post"] = interaction
self.posts[interaction.user.id]["sample_post_id"] = interaction
await interaction.response.send_message(embed=self.posts[interaction.user.id]["embed"], ephemeral=True)
# Ничего умного не придумал, что можно в посты запихать
@self.tree.command(name="add_text_to_post", description="Добавляет текст посту с отступом", guild=self.guild_object)
@describe(text="Текст")
async def add_text_to_post(interaction: Interaction, text: str):
try:
embed: Embed = self.posts[interaction.user.id]["embed"]
if embed.description:
embed.description += f"\n{text}"
else:
embed.description = f"{text}"
interact: Interaction = self.posts[interaction.user.id]["sample_post"]
await interact.edit_original_response(embed=embed)
await interaction.response.send_message("Готово", ephemeral=True, delete_after=1)
except:
await interaction.response.send_message("Для начала вам необходимо создать пост. Для этого используйте ```/create_post```", ephemeral=True)
@self.tree.command(name="send_post_after", description="Отправляет пост через указанное время", guild=self.guild_object)
@describe(minutes="Через сколько минут отправить")
async def send_post_after(interaction: Interaction, minutes: int):
async def send_mes(channel: TextChannel, embed: Embed, minutes: int, user_id: int):
start: datetime = datetime.now()
while True:
if datetime.now()-start >= timedelta(minutes=minutes):
await channel.send(embed=embed)
self.posts.pop(user_id)
break
await sleep(0.5)
await interaction.response.send_message(f"Ваш пост отправится через {minutes} мин.", ephemeral=True)
run_coroutine_threadsafe(send_mes(channel=interaction.channel, embed=self.posts[interaction.user.id]["embed"], minutes=minutes, user_id=interaction.user.id), bot.loop)
async def on_ready(self) -> None:
await self.tree.sync(guild=self.guild_object)
bot = Bot()
bot.run(token=token, reconnect=True)