简体   繁体   中英

Python: Send message to specific channel in Discord via cog and background task

I've got a Discord Python bot where I'm attempting to run a background task that will send a message to a channel every X seconds constantly - no command required. Currently have an arbitrary 5 seconds for testing purposes.

Here's the cog file in question (imports and whatnot removed for efficiency)

class TestCog(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self.mytask.start()

    @tasks.loop(seconds=5.0)
    async def mytask(self):
        channel = client.get_channel(my channel id here)
        await channel.send("Test")

def setup(bot):
    bot.add_cog(TestCog(bot))

I've got a feeling this is due to the self parameter being the only passed argument, but I'm a little confused reading the API documentation on what exactly to do here.

I've tried client instead of bot , I've tried defining discord.Client() (but as far as what I've read, I shouldn't use that which I've been trying to avoid.

In other cogs which use actual commands, I have it setup like this which works:

    @commands.command(name='test')
    async def check(self, ctx):
        if ctx.channel.name == 'channel name':
            await ctx.send("Response message")

This is what led me to believe the parameter(s) I'm passing are wrong. I understand because I'm passing ctx I can grab the channel name, but I'm not too sure how to do this with just self. When attempting to pass the ctx parameter I don't get any errors, but the messages do not send for obvious reasons.

What exactly am I missing here? I appreciate any help!

A discord.Client object has no get_channel() method. You must use a discord.Guild object instead:

await client.get_guild(guild_id_here).get_channel(channel_id_here).send("Test")

Read the docs .

You can add a task to your asyncio loop with .loop.create_task(mytask(arguments)) which you'll call before starting your bot. Read all about asyncio here

You define the task like a normal command with async def mytask(argument) , however leave out ctx as ctx is basically all the context you would normally get about the command used to invoke the function. Instead you'll want to use the channel id to manually get the channel object with channel = bot.get_channel(id) , then you can do await channel.send("Your message") to send a message to said channel.

To make it loop just use a while True loop with asyncio.sleep(delay) to time it. This could lead to some inaccuracy in timing due to the time you have to wait for the message send, so I recommend starting the timing task with clock = asyncio.create_task(asyncio.sleep(delay)) in front of your function and catching that after your function with await clock

Now if you want it to run at a certain time every interval and not just at a set interval from when you start the function, you're gonna want to delay the start of the function to match the time you set. You can do that with divmod(time.time(), interval) which returns the quotient and the remainder of the time and your interval, the remainder is the time since the last interval started. If you want to start your function at the start of the interval you can use await asyncio.sleep(interval-remainder) to make the function sleep until the start of the next interval. If you want to set a time in that interval you're gonna want to split it up in two parts, one for if your set time has passed and one for if it has yet to come.

if remainder < set_time: 
    await asyncio.sleep(set_time - remainder)
else:
    await asyncio.sleep(interval + set_time - remainder)

Now if you add all of that together into one function you get something like this(which is a piece of code I use in a bot of mine:

async def reminder(channel, interval, set_time, message):
    await bot.wait_until_ready()
    channel = bot.get_channel(channel)
    quotient, remainder = divmod(time.time(), interval)
    if remainder < set_time:
        await asyncio.sleep(set_time-remainder)
    else:
        await asyncio.sleep(set_time + interval - remainder)
    while True:
        clock = asyncio.create_task(asyncio.sleep(interval))
        await channel.send(message)
        quotient, remainder = divmod(time.time(), interval)
        if remainder-set_time > 1:
            clock.cancel()
            await asyncio.sleep(interval-1)
        await clock

bot.loop.create_task(reminder(774262090801479740, 3600, 3300, "This is the 55th minute of the hour"))
bot.run(TOKEN)

(I know this doesn't 100% answer the question but as you said you tried both bot and client this solution should work for you)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM