Search code examples
pythondiscord.py

discord.py, task function has no attribute start()


I'm trying to implement a task in a loop for one discord bot using the discord.py library.

In synthesis, this is what my code looks like:

class RoblinBot(commands.Bot):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.path = ".roblin"
        self.fsettings = f"{self.path}/settings.json"

        self.listen_urls = False
        self.links = []
        self.high_activity = False
        
        self.setup()

    def setup(self):

        if not os.path.exists(self.path):
            os.makedirs(self.path)

        self.settings = Settings(self.fsettings)
    
    def reset(self):

        for f in os.listdir(self.path):
            os.remove(os.path.join(self.path, f))

        self.setup()
    
    async def on_ready(self):
        
        print("BOT READY")

        try:
            sync = await bot.tree.sync()
            print(f"SYNCHED {len(sync)} COMMAND(S)")
            await self.add_cog(ListenWebsite(self))
        except Exception as e:
            logger.exception(e)

and the cog (containing the task), is defined on the same file as:

class ListenWebsite(commands.Cog):
    
    def __init__(self, bot):
        
        self.bot = bot
        self.check_for_articles.start()

    @tasks.loop(seconds=CHECK_EVERY)
    async def check_for_articles(self):
        print("this is a looped message")

        if not self.listen_urls:
            return

        for url in self.settings.urls:
            source = requests.get(url)
            soup = BeautifulSoup(source.content, 'lxml')
            raw = []
            links = []

            for link in soup.find_all('a', href=True):
                raw.append(str(link.get('href')))

            for item in raw:
                match = re.search("(?P<url>https?://[^\s]+)", item)
                if match is not None:
                    links.append((match.group("url")))

            links = list(set(links)) # remove duplicates
            for link in links:
                if link.count("-")<=1: # should not be an article
                    links.remove(link)

            print(url)
            print(links)

            # compare self.links and links to see if new articles are found
            
            if not self.links:
                new_links = list(set(links)-set(self.links))
                for link in new_links:
                    await self.settings.channel.send(
                        f"Baby wake up! New article just dropped: {link}"
                    )

                    self.links.append(link)
            else:
                self.links = links

    @check_for_articles.before_loop
    async def before_check_for_articles(self):
        
        print('waiting for bot to be ready...')
        await self.wait_until_ready()

    @check_for_articles.error
    async def check_for_articles(self, error: Exception):

        logger.exception(error)

however I cannot seem to make it work, I keep getting 'function' object has no attribute 'start' when the cog's init is called. One thing to be noted is that before this I tried putting the task also inside the bot's class and outside, however I also got the same error. It's as if the tasks.loop decorator isn't there, and I'm baffled since according to every example I can find, my code should just work.

Maybe there's something I'm missing, but I don't know what it could be.

What I tried:

  • Putting the loop function inside the bot class
  • Putting it outside
  • Putting it into a cog

What I expected to happened: In all listed cases, I would've expected the check_for_articles function to inherit all the methods bestowed by the tasks.loop decorator.

What actually resulted: None of the methods of tasks.loop are given to the function.


Solution

  • You're overwriting your check_for_articles task in the error handler:

    class ListenWebsite(commands.Cog):
        def __init__(self, bot):
            self.bot = bot
            self.check_for_articles.start()
    
        @tasks.loop(seconds=CHECK_EVERY)
        async def check_for_articles(self):
            ...
    
        @check_for_articles.before_loop
        async def before_check_for_articles(self):
            ...
    
        @check_for_articles.error
        async def check_for_articles_error(self, error: Exception): # error was here
            ...