Ker
Ker

Reputation: 1

Discord bot can't use threads, stuck error: required positional argument: 'ctx'

I try to make this bot have a thread so that every 24 hours it executes the almanax coroutine without any command, but when I try to call it like this in another thread, I get an error (TypeError: almanax() missing 1 required positional argument: 'ctx') that it needs ctx and I have tried but I just get stuck. I'm new to this completely.

import discord
from discord.ext import commands
import urllib.request
import re
import datetime
from bs4 import BeautifulSoup
import requests
import asyncio

bot = commands.Bot(command_prefix='-', description= "Este es un DuckBot")

sourceLinkAlmanax = 'http://www.krosmoz.com/es/almanax'
fechaexacta = '{0:%d-%m-%Y}'.format(datetime.datetime.now())

async def dailyAlmanax():
    while 1:
        await asyncio.sleep(5) #86400
        await almanax()

@bot.command()
async def almanax(ctx):
    print("Procesando almanax")

    source = requests.get(sourceLinkAlmanax).text

    soup = BeautifulSoup(source, 'lxml')

    mision = soup.find('div', class_='mid').p.text
    bonus = soup.find('div', class_='more').getText()
    ofrenda = soup.find('div', class_='more-infos-content').p.text
    bonus = bonus.replace(mision, "")
    bonus = bonus.replace(ofrenda, "")
    linkImagen = soup.find('div', {"class": "more-infos"}).img['src']

    fechaexacta = '{0:%d-%m-%Y}'.format(datetime.datetime.now())

    mensaje = discord.Embed(title = "`Duckmanax del " + fechaexacta + "`", url=sourceLinkAlmanax, color=0xe5be01)
    mensaje.add_field(name="Mision: ", value=f"{mision}", inline=False)
    mensaje.add_field(name="Bonus: ", value=f"{bonus.strip()}", inline=False)
    mensaje.add_field(name="Ofrenda: ", value=f"{ofrenda.strip()}", inline=False)
    mensaje.set_image(url=linkImagen)
    await ctx.send(embed = mensaje)

    print("Almanax enviado")

@bot.command()
async def salmanax(ctx, busqueda: str):
    print("Procesando busqueda de almanax")
    fecha = datetime.datetime.now()
    año = fecha.year
    smes = fecha.month
    sdia = fecha.day

    for mes in range (smes,13):

        if mes > smes:
            sdia = 1

        for dia in range (sdia,32):

            print("Procesando Año:", año, "Mes:", mes, "Dia:", dia, "Buscando:", busqueda)

            if mes < 10:
                mes2 = "0" + str(mes)
            else:
                mes2 = mes
            if dia < 10:
                dia2 = "0" + str (dia)
            else:
                dia2 = dia

            link = "http://www.krosmoz.com/es/almanax/" + str(año) + "-" + str(mes2) + "-" + str(dia2)

            try:
                data = urllib.request.urlopen(link).read().decode('utf-8')
            except Exception as error:
                pass

            for linea in data.split("/n"):
                try:
                    if re.findall(busqueda, linea, re.IGNORECASE):
                        await ctx.send("Encontre esta coincidencia de " + busqueda + " " + link)
                except Exception as error2:
                    pass
    print("Busqueda de almanax finalizada")

@bot.event
async def on_message(ctx):
    if ctx.channel.name == 'almanax':
        await bot.process_commands(ctx)

@bot.event
async def on_ready():
    print("Bot listo")
    await bot.change_presence(activity=discord.Streaming(name="-almanax",url="https://www.twitch.tv/kerdrai"))

bot.loop.create_task(dailyAlmanax())
bot.run(token)

Upvotes: 0

Views: 358

Answers (1)

LukeAtmi
LukeAtmi

Reputation: 169

Commands require a Context object to be passed, so await mycommand() isn't valid unless you pass a valid context. And a valid context is necessary, because in your almanax() command, it is used in await ctx.send(embed = mesaje), so you can't just make up a new Context object and pass it in and expect it to work.

You could instance a new Context object that is just perfect to make the call valid, but that would be too workaround-ish, and there is a better way to do this. (If you wish to go that way, use the Bot.get_context() method on a message that has a valid almanax command, and pass that as argument in await almanax(). This would cause the daily message to be sent from the channel the message you picked was in.)

The entire part of the almanax command that goes from

source = requests.get(sourceLinkAlmanax).text

to

mensaje.set_image(url=linkImagen)

can be outsourced into another function that you can call in almanax(), I'll call that function get_almanax() (you should make that function async as it relies on waiting for a response) but, you can name it whatever you want. This function returns the embed that is sent in almanax(). Then, you could change both dailyAlmanax() and almanax() to something like this:

async def dailyAlmanax():
    while 1:
        await asyncio.sleep(5) #86400
        mesaje = await get_almanax()
        channel = bot.get_channel(ID) #ID for the daily almanax channel
        await channel.send(embed = mesaje)
...

@bot.command()
async def almanax(ctx):
    print("Procesando almanax")

    mesaje = await get_almanax()
    await ctx.send(embed = mesaje)

The get_almanax() function should be defined as the lines I mentioned before, along with whatever input you need (doesn't look like any is necessary, but just in case), and it should return the desired embed.

Upvotes: 2

Related Questions