Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making a discord bot change playing status every 10 seconds

I'm trying to make the status for a test discord bot change between two messages every ten seconds. I need the rest of the script to execute while the status message changes, but an error keeps popping up whenever I try to make it work. There's threading in my script, but I'm not entirely sure how to use it in this circumstance.

@test_bot.event
async def on_ready():
    print('Logged in as')
    print(test_bot.user.name)
    print(test_bot.user.id)
    print('------')
    await change_playing()


@test_bot.event
async def change_playing():
    threading.Timer(10, change_playing).start()
    await test_bot.change_presence(game=discord.Game(name='Currently on ' + str(len(test_bot.servers)) +
                                                          ' servers'))
    threading.Timer(10, change_playing).start()
    await test_bot.change_presence(game=discord.Game(name='Say test.help'))

The error message reads:

C:\Python\Python36-32\lib\threading.py:1182: RuntimeWarning: coroutine 'change_playing' was never awaited
  self.function(*self.args, **self.kwargs)
like image 680
luigiisgreeny Avatar asked Sep 17 '17 18:09

luigiisgreeny


3 Answers

Threading and asyncio don't play nice together unfortunately. You need to jump through extra hoops to await coroutines inside threads. The simplest solution is to just not use threading.

What you are trying to do is wait a duration and then run a coroutine. This can be done with a background task (example)

async def status_task():
    while True:
        await test_bot.change_presence(...)
        await asyncio.sleep(10)
        await test_bot.change_presence(...)
        await asyncio.sleep(10)

@test_bot.event
async def on_ready():
    ...
    bot.loop.create_task(status_task())

You cannot use time.sleep() as this will block the execution of the bot. asyncio.sleep() though is a coroutine like everything else and as such is non-blocking.

Lastly, the @client.event decorator should only be used on functions the bot recognises as events. Such as on_ready and on_message.

like image 58
Sam Rockett Avatar answered Oct 16 '22 22:10

Sam Rockett


discord.py version 1.1.0 introduced discord.ext.tasks, which is designed to make background tasks like the one you describe easier, as well as handling the potentially complicated logic of reconnecting to discord if there is a connection issue.

Here's an example of your task using tasks:

from discord.ext import commands, tasks
from commands import Bot
from tasks import loop
from asyncio import sleep

bot = Bot("!")

@loop(seconds=10)
async def name_change():
    await bot.change_presence(...)
    await sleep(10)
    await bot.change_presence(...)

name_change.before_loop(bot.wait_until_ready())    
name_change.start()
bot.run("TOKEN")
like image 28
Patrick Haugh Avatar answered Oct 16 '22 23:10

Patrick Haugh


See this:

https://github.com/Rapptz/discord.py/blob/master/examples/background_task.py

import discord
import asyncio
client = discord.Client()
async def my_background_task():
    await client.wait_until_ready()
    counter = 0
    channel = discord.Object(id='channel_id_here')
    while not client.is_closed:
        counter += 1
        await client.send_message(channel, counter)
        await asyncio.sleep(60) # task runs every 60 seconds
@client.event
async def on_ready():
    print('Logged in as')
    print(client.user.name)
    print(client.user.id)
    print('------')
client.loop.create_task(my_background_task())
client.run('token')

Discord.py comes with a built in background task feature,

like image 1
ADug Avatar answered Oct 16 '22 22:10

ADug