Defxult / discordLevelingSystem

A library to implement a leveling system into a discord bot. Contains features such as XP, level, ranks, and role awards.
MIT License
91 stars 17 forks source link

How can I fix this event loop? #20

Closed jsanchoa closed 1 year ago

jsanchoa commented 1 year ago

Describe the bug I'm trying to create a rank command in commands.Cog but when I try to start the discord bot it says me about "the event loop is already running" how can I fix it

To Reproduce Minimal code to reproduce the bug/error import discord, random from discord.ext import commands from discord import app_commands, File from easy_pil import Editor, load_image_async, Font from discordLevelingSystem import DiscordLevelingSystem, LevelUpAnnouncement

announcement = LevelUpAnnouncement(f"¡GG {LevelUpAnnouncement.Member.mention}, you just advanced to level {LevelUpAnnouncement.LEVEL} !") lvl = DiscordLevelingSystem(rate=6,per=20.0,level_up_announcement=announcement) lvl.connect_to_database_file(r'C:\Users\Personal\Desktop\Mimi-Bot\DiscordLevelingSystem.db')

DiscordLevelingSystem.create_database_file(r'C:\Users\Personal\Desktop\Mimi-Bot\')

class ranks(commands.Cog): def init(self, client): self.client = client

@commands.Cog.listener()
async def on_ready(self):
    print("Loaded ranks")

@commands.Cog.listener()
async def on_message(self, message):
    await lvl.award_xp(amount=25, message=message)

@app_commands.command(name="rank", description="¡Get your actual rank!")
@app_commands.checks.cooldown(1, 5.0, key=lambda i: (i.guild_id, i.user.id))
async def rank(self, interaction: discord.Interaction, member: discord.Member=None):
    if member == None:
        member = interaction.user
        data = await lvl.get_data_for(member)
        xp_total_lvl_up = DiscordLevelingSystem.get_xp_for_level(data.level+1)
        xp_actual = await lvl.get_xp_for(member)
    data = await lvl.get_data_for(member)
    xp_total_lvl_up = DiscordLevelingSystem.get_xp_for_level(data.level+1)
    xp_actual = await lvl.get_xp_for(member)
    percentage = int(((xp_actual * 100)/ xp_total_lvl_up))
    if percentage < 1:
        percentage = 0
    ## Rank card
    background = Editor(f"{0}.png")
    profile = await load_image_async(str(member.avatar.url))
    profile = Editor(profile).resize((150, 150)).circle_image()
    poppins = Font.poppins(size=40)
    poppins_small = Font.poppins(size=30)
    background.paste(profile.image, (30, 30))
    background.rectangle((30, 220), width=650, height=40, fill="#fff", radius=20)
    background.bar(
        (30, 220),
        max_width=650,
        height=40,
        percentage=percentage,
        fill="#ff9933",
        radius=20,
    )
    background.text((200, 40), str(member.name), font=poppins, color="#ff9933")
    background.rectangle((200, 100), width=350, height=2, fill="#ff9933")
    background.text(
        (200, 130),
        f"Level : {data.level}   "
        + f" XP : {xp_actual} / {xp_total_lvl_up}   " + f"Rank: {data.rank}",
        font=poppins_small,
        color="#ff9933",
    )
    card = File(fp=background.image_bytes, filename="zCARD.png")
    await interaction.response.send_message(file=card)

@rank.error
async def on_rank_error(self, interaction: discord.Interaction, error: app_commands.AppCommandError):
    messages = ["¡Bro, calm down!", "¡Bro, slow!", "¡Just be patient!", "¡You have to wait!"]
    if isinstance(error, app_commands.CommandOnCooldown):
        await interaction.response.send_message(content=random.choice(messages), ephemeral=True)
    elif isinstance(error, app_commands.AppCommandError):
        await interaction.response.send_message(f"Error: {error}", ephemeral=True)

async def setup(client): await client.add_cog(ranks(client))

Traceback if any 2022-11-27 19:07:55 ERROR discord.client Ignoring exception in on_ready Traceback (most recent call last): File "C:\Users\Personal\AppData\Local\Programs\Python\Python310\lib\site-packages\discordLevelingSystem\leveling_system.py", line 359, in connect_to_database_file self._connection = self._loop.run_until_complete(aiosqlite.connect(path)) File "C:\Users\Personal\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 622, in run_until_complete self._check_running() File "C:\Users\Personal\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 582, in _check_running raise RuntimeError('This event loop is already running') RuntimeError: This event loop is already running

During handling of the above exception, another exception occurred:

Traceback (most recent call last): File "C:\Users\Personal\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\ext\commands\bot.py", line 934, in _load_from_module_spec spec.loader.exec_module(lib) # type: ignore File "", line 883, in exec_module File "", line 241, in _call_with_frames_removed File "c:\Users\Personal\Desktop\Mimi-Bot\cogs\rank.py", line 9, in lvl.connect_to_database_file(r'C:\Users\Personal\Desktop\Mimi-Bot\DiscordLevelingSystem.db') File "C:\Users\Personal\AppData\Local\Programs\Python\Python310\lib\site-packages\discordLevelingSystem\leveling_system.py", line 363, in connect_to_database_file raise ConnectionFailure discordLevelingSystem.errors.ConnectionFailure: Cannot connect to database file because the event loop is already running

The above exception was the direct cause of the following exception:

Traceback (most recent call last): File "C:\Users\Personal\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\client.py", line 409, in _run_event await coro(*args, **kwargs) File "c:\Users\Personal\Desktop\Mimi-Bot\main.py", line 15, in on_ready await client.load_extension(f"cogs.{file[:-3]}") File "C:\Users\Personal\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\ext\commands\bot.py", line 1012, in load_extension await self._load_from_module_spec(spec, name) File "C:\Users\Personal\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\ext\commands\bot.py", line 937, in _load_from_module_spec raise errors.ExtensionFailed(key, e) from e discord.ext.commands.errors.ExtensionFailed: Extension 'cogs.rank' raised an error: ConnectionFailure: Cannot connect to database file because the event loop is already running

Required Checklist

Version Info What's the exact version of discordLevelingSystem/discord.py are you using?

Defxult commented 1 year ago

According to this error:

Traceback (most recent call last):
File "C:\Users\Personal\AppData\Local\Programs\Python\Python310\lib\site-packages\discord\ext\commands\bot.py", line 934, in _load_from_module_spec
spec.loader.exec_module(lib) # type: ignore
File "", line 883, in exec_module
File "", line 241, in _call_with_frames_removed
File "c:\Users\Personal\Desktop\Mimi-Bot\cogs\rank.py", line 9, in
lvl.connect_to_database_file(r'C:\Users\Personal\Desktop\Mimi-Bot\DiscordLevelingSystem.db')
File "C:\Users\Personal\AppData\Local\Programs\Python\Python310\lib\site-packages\discordLevelingSystem\leveling_system.py", line 363, in connect_to_database_file
raise ConnectionFailure
discordLevelingSystem.errors.ConnectionFailure: Cannot connect to database file because the event loop is already running

For the cog/line 9, you're trying to connect to the database file again. Is that correct? The code you've shown is...'messy'. And displays a lot of extra information that is not needed to represent the "Minimal code to reproduce the bug/error"

jsanchoa commented 1 year ago

No, that's the only line who is trying to connect to the database, and my bad, sorry. if you want I can send you the hastebin with my main class that is trying to load the extension from cog and the cog rank.

Defxult commented 1 year ago

Sure, send it.

jsanchoa commented 1 year ago

Here's the main class and rank cog https://hastebin.com/etuxiwidag.py and this is the full error: https://hastebin.com/agiliyocas.py PD: I tried only with rank cog deleting my other cogs but still not working. This is like I have it image

Defxult commented 1 year ago

Ahh ok. I think I see the issue. You're setting up the leveling system (loading in your cog) in on_ready. When on_ready is dispatched, the bots event loop would have already started. It says in the documentation that you cannot set this up when the event loop has already started.

You should move the entire leveling system setup to your main class. The setup being:

This should happen before bot.run() is executed. And to access the leveling system from your cogs, attach a variable to your client instance. So your main class will look like this:

main.py

import discord, random, datetime, os
# other imports ...
from discordLevelingSystem import DiscordLevelingSystem, LevelUpAnnouncement

announcement = LevelUpAnnouncement(...)
lvl = DiscordLevelingSystem(...)
lvl.connect_to_database_file(...)

config = default.get(...)

client = commands.Bot(...)

# ---------- Add the leveling system as a variable so you can access it in a cog ----------
client.leveling_system = lvl

@client.event
async def on_ready():
    # ...

client.run(config.token)

rank.py

import discord, random
# other imports...

class ranks(commands.Cog):
    def __init__(self, client):
        self.client = client
        self.lvl = client.leveling_system

    @commands.Cog.listener()
    async def on_message(self, message):
        ❌ await lvl.award_xp(amount=25, message=message)

        # You need to access the leveling system via your bot instance from now on
        ✅ await self.client.leveling_system.award_xp(amount=25, message=message)

        # IMO, that's too long to type out, so set a shorter variable in the class (as shown above)
        ✅ await self.lvl.award_xp(amount=25, message=message)
jsanchoa commented 1 year ago

Thank you so much, is like you said it.

jsanchoa commented 1 year ago

Excuse me man, idk if you can add me to Discord, I'm trying but it says me that you are the only who can send friend requests, and Idk If you can help me with something of my Discord bot. Josu#4939