Discord.PY цикл блокирует код
Не могу понять, почему цикл в background_task_cenniky вешает весь код и ничего не выполняется. Прошу помощи. Я новичок в python, поэтому прошу развернутый ответ.
# Библиотеки
import time # для работы со временем
import datetime # для работы со временем
import discord # DiscordAPI
import asyncio # библиотека асинхронного кода
from discord.ext import commands, tasks # импортируем работу с командами
from discord.ext.tasks import loop # функция цикла
from config import settings # забираем данные настроек из config.py
# ПЕРЕМЕННЫЕ
# Для работы с DiscordAPI
bot = commands.Bot(command_prefix = settings['prefix'])
# Чтобы получить текущее время
now = datetime.datetime.now()
# СОБЫТИЯ
# Эвент о запуске бота и подключению в DiscordAPI
@bot.event
async def on_ready():
print('Time',datetime.datetime.now(), '\nScript connected to {}'.format(bot.user))
# Указываем ID канала, куда отправить сообщение
channel = bot.get_channel(*********)
# Отправляем сообщение
await channel.send('бот зашел в сеть')
# Устанавливаем статус бота
await bot.change_presence(status=discord.Status.do_not_disturb, activity=discord.Game("GarwinVRN"))
# Кто-то присоединился к серверу
@bot.event
async def on_member_join(member):
await member.send('Привет, если ты наш сотрудник, мы подтвердим твою учетку')
for ch in bot.get_guild(member.guild.id).channels:
if ch.name == 'для-входа':
await bot.get_channel(ch.id).send(f'{member}, у нас новый запрос')
# Кто-то вышел с сервера
@bot.event
async def on_member_remove(member):
for ch in bot.get_guild(member.guild.id).channels:
if ch.name == 'болталка':
await bot.get_channel(ch.id).send(f'{member}, вышел из группы')
# Уведомление печать ценников
async def background_task_cenniky():
while True:
z = time.localtime()
await asyncio.sleep(1)
if z.tm_hour == 8 and z.tm_min == 25 and z.tm_sec == 0:
print("TEST TIMER MESSAGE")
channel = bot.get_channel(************)
channel.send('background_task')
async def main():
task1 = asyncio.create_task(background_task_cenniky())
await asyncio.gather(task1)
if __name__ == '__main__':
asyncio.run(main())
# КОМАНДЫ
# Команда пинг
@bot.command() # Говорим что будем обрабатывать команду
async def пинг(ctx): # Создаём функцию и ставим перехватчик ctx.
author = ctx.message.author # Объявляем переменную author и записываем туда информацию об авторе.
await ctx.reply(f'**Да тут я, тут, {author.mention}! Че пристал?**') # Выводим сообщение с упоминанием автора, обращаясь к переменной author.
# Команда инфо
@bot.command()
async def инфо(ctx, arg=None):
author = ctx.message.author
if arg == None:
await ctx.send(f'{author.mention} \n**Введи:**\n!инфо создатель\n!инфо команды')
elif arg == 'создатель':
await ctx.send(f'{author.mention} \n**Мой создатель Алексей Шнайдер aka SNR93**')
elif arg == 'команды':
await ctx.send(f'{author.mention} \n**Список моих команд**:\n!пинг - проверить, в сети и работает ли бот')
else:
await ctx.send(f'{author.mention} **Нет такой команды. Список команд: !инфо команды**')
# СТАРТ
bot.run(settings['token']) # Обращаемся к словарю settings с ключом token, для получения токена
Ответы (1 шт):
Цикл блокирует весь код, который выполняется в данном потоке. Для подобных случаев в модуле asyncio предусмотрена функция asyncio.run_coroutine_threadsafe(function(*args), loop), принимающая на вход первым аргументом вызов функции с передачей аргументов (если нужно), а вторым - поток, в котором нужно выполнить эту функцию. При этом работоспособность потока сохранится для остальных действий.
Простой пример. Вам нужно отправлять какой-либо текст в определенный канал раз в 10 секунд.
Для начала создадим функцию отправки сообщений. Назовем ее send_info(channel) - она будет принимать один аргумент - канал для отправки сообщений.
import discord
import asyncio
async def send_info(channel: discord.TextChannel):
while True:
await channel.send('Информация')
await asyncio.sleep(10)
Теперь создадим обработчик события on_ready(), таким образом убедившись, что бот запущен и запустим функцию отправки сообщений:
@bot.event
async def on_ready():
info_channel = bot.get_channel(760471276072390836)
asyncio.run_coroutine_threadsafe(send_info(info_channel), bot.loop)
Готово! Теперь бот будет параллельно выполнять еще и свои основные функции по обработке других событий.
Но, стоит также обратить внимание на еще один способ, который уже предоставляет библиотека discord-py.
discord.ext.tasks позволяет пометить декоратором функцию, которая должна повторяться несколько раз. В этом случае реализация будет немного другой:
Опять же импортируем модуль, и создаем функцию. Выставлять задержку в 10 сек. уже не надо, т.к. она теперь указана в декораторе:
import discord
from discord.ext import tasks
@tasks.loop(seconds=10)
async def send_info(channel: discord.TextChannel):
await channel.send('Информация')
Ну и точно также запускаем функцию, когда бот будет готов:
@bot.event
async def on_ready():
info_channel = bot.get_channel(760470076070690836)
send_info.start(info_channel)