Search code examples
pythonjupyter-notebookdiscorddiscord.pypycord

How to run discord.py interactively in jupyter notebook?


In discord.py, you can initialize a Client, and run it with client.run(). But the function never returns.

What if I just want to just retrieve some message history then use it in jupyter notebook?

@bot.command()  
async def history(ctx):
    counter = 0
    messageList=[]
    async for message in ctx.channel.history(limit = 100):
        print(message.author, message.content)
        counter += 1
        messageList.append(message)

    await ctx.send(f'total **{counter}** messages in this channel.')

    # How to return the messageList to my jupyter notebook cell and I start playing with the messageList?

How to return the messageList?


Solution

  • There isn't a good way to do this. discord.py is designed to be started once and run until your program terminates since all of the internal objects are destroyed upon closure of the bot, which makes starting the bot back up again nearly impossible. And it is not possible to "pause" discord.py and run your code then resumes discord.py afterwards, because Discord API communicates via web sockets, which relies on a constant stream of heartbeats and acks.

    A possible workaround would be to:

    • Use a single event loop through out the code.
    • Use loop.run_until_complete() to start the bot's internal loop.
    • Create a new Client every time you need to run the bot.
    • Create an on_ready event handle that fetches the information.
    • Downside is that you won't be able to interact with the Message objects (ie. delete, edit, etc), they will be view only.

    Example:

    import asyncio
    import discord
    
    TOKEN = "YOURBOTTOKEN"
    loop = asyncio.get_event_loop()
    
    
    def get_message_list(token, channel_id):
        client = discord.Client(loop=loop)
        message_list = []
    
        @client.event
        async def on_ready():
            nonlocal message_list
            channel = client.get_channel(channel_id)
            if not channel:  # incase the channel cache isn't fully populated yet
                channel = await client.fetch_channel(channel_id)
    
            async for message in channel.history(limit=100):
                message_list.append(message)
            await client.close()
    
        async def runner():
            try:
                await client.start(token)
            finally:
                if not client.is_closed():
                    # Incase the bot was terminated abruptly (ie. KeyboardInterrupt)
                    await client.close()
    
        loop.run_until_complete(runner())
        return message_list
    
    
    message_list = get_message_list(TOKEN, channel_id=747699344911712345)
    print("Message list for channel #1:", message_list)
    
    # ... do stuff with message_list
    
    message_list = get_message_list(TOKEN, channel_id=747699344911754321)
    print("Message list for channel #2:", message_list)
    
    # ... do more stuff with message_list
    
    # Finally closes the event loop when everything's done
    loop.close()
    

    Instead of doing this, I'd recommend you to find another solution to the task you're trying to accomplish.