I made a sample code snippet to try and see if I can automate my discord bot so that it automatically sends a message at a set interval (seconds):
import os
import discord
import discord.ext
from discord import app_commands
import asyncio
TOKEN = 'BOT TOKEN HERE'
GUILD = 'GUILD NAME HERE'
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
@client.event
async def on_ready():
for guild in client.guilds:
if guild.name == GUILD:
break
print(
f'{client.user} has successfully connected to the following guild(s):\n'
f'{guild.name}(id: {guild.id})'
)
await client.change_presence(activity=discord.Activity(name='anything', type=discord.ActivityType.playing))
@client.event
async def auto_send():
await send_message('MESSAGE HERE')
await asyncio.sleep(i) #replace 'i' with any number(seconds)
async def send_message(message):
channel = await client.fetch_channel('CHANNEL ID HERE') #must be of type 'int'
await channel.send(message)
client.loop.create_task(auto_send())
client.run(TOKEN)
After running the code I'm getting this error:
Traceback (most recent call last):
File "", line 92, in <module>
client.loop.create_task(auto_send())
^^^^^^^^^^^^^^^^^^^^^^^
File "", line 140, in __getattr__
raise AttributeError(msg)
AttributeError: loop attribute cannot be accessed in non-async contexts. Consider using either an asynchronous main function and passing it to asyncio.run or using asynchronous initialisation hooks such as Client.setup_hook
I tried moving the client.loop.create_task() command into the async function definition, and I got no output at all, without any error messages.
My goal was to try and see if the bot automatically sends a message to a specific channel after a set interval (say 10 seconds).
I don't understand this error, so please let me know if there is a fix. Thank you in advance.
You can make your bot sends a message each X seconds by creating an asyncio task.
discord.py
provides the discord.ext.tasks
module to easily do that. Here is how:
Step 1: Create a Loop
using the tasks.loop
decorator:
@tasks.loop(seconds=10)
async def auto_send(channel : discord.TextChannel):
await channel.send("Test message")
I'm creating a Loop
called auto_send
that recieves a TextChannel
as parameter telling the bot where to send the message. The bot will send the "Test message" in the channel provided each 10 seconds as configured in the decorator.
Step 2: Launch the Loop
using tasks.Loop.start
, you can do it on the global scope. However, I'm going to do it in the on_ready
event to be able to fetch the channel before launching the task:
@client.event
async def on_ready():
if not auto_send.is_running():
channel = await client.fetch_channel('channel id (as int)')
auto_send.start(channel)
print('Ready')
The on_ready
event can be called more than once:
This function is not guaranteed to be the first event called. Likewise, this function is not guaranteed to only be called once. This library implements reconnection logic and thus will end up calling this event whenever a RESUME request fails.
We can check if the task is already running using tasks.Loop.is_running
to avoid launching it more than once.
Your Full code:
import discord
from discord.ext import tasks
TOKEN = 'BOT TOKEN HERE'
GUILD = 'GUILD NAME HERE'
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
@tasks.loop(seconds=10)
async def auto_send(channel : discord.TextChannel):
await channel.send('Test message')
@client.event
async def on_ready():
if not auto_send.is_running():
channel = await client.fetch_channel('channel id (as int)')
auto_send.start(channel)
for guild in client.guilds:
if guild.name == GUILD:
break
print(
f'{client.user} has successfully connected to the following guild(s):\n'
f'{guild.name}(id: {guild.id})'
)
await client.change_presence(
activity=discord.Activity(name='anything', type=discord.ActivityType.playing)
)
client.run(TOKEN)