#updating embeds per server

1 messages · Page 1 of 1 (latest)

soft parrot
#

I am trying to create a loop to update an embed per discord of stored information, but I am running into an issue where other servers information makes it way into the wrong server. Is there a better method than using for loops?

The picture attached shows 3 things that should be showing up but instead it is showing one (wrong) thing

#

I will grab the code soon as I’m back home and at my pc

atomic swallow
#

update the embed info for each server

soft parrot
#
@tasks.loop(minutes = 1)
async def update_pop_embed(self):
    await bot.wait_until_ready()
    for guild in bot.guild:
        now1 = datetime.now()
        start_time1= now1.strftime('%I:%M %p')
        embed = disnake.Embed(title=f'**__Pop Tracking__**', color=color)
        embed.set_thumbnail(url=bot.user.display_avatar.url)
        embed.set_footer(text=f'{bot.user.name} v{ver} | Updated {start_time1}', icon_url=bot.user.display_avatar.url)
        servers = await bot.db.fetch(f"SELECT * FROM pop_embed")
            for i in servers:
                if i[0] == guild.id and i[7] != None:
                    channel = bot.get_channel(i[7])
                    message = await channel.fetch_message(i[8])
                if i[0] == guild.id: 
                    if i[2] == 'Xbox':
                        emoji = xbox
                    if i[2] == 'PlayStation':
                        emoji = ps
                    if i[1] != None and i[0] == guild.id:
                        embed.add_field(name=f"{emoji} **{i[1].upper()}**", value=f"Players Online: {i[4]}/{i[5]}\nMaps Online: {i[6]}\nVersion: {i[3]}", inline=False)
                    if message.author == bot.user:
                        await message.edit(embed=embed)```
atomic swallow
#

The picture attached shows 3 things that should be showing up but instead it is showing one (wrong) thing
what wrong thing?

soft parrot
#

the edited embed shows royalty of ark instead of the three servers listed in the ephemeral message

#

the command setup view (the ephemeral one) is there for me to see what is supposed to show up

atomic swallow
#

well, you're editing the message in a for loop

#

move the message.edit to the guild loop rather than the server loop

soft parrot
#

if memory serves me well, i tried that earlier and it told me message wasnt defined

atomic swallow
#

So, there's a few issues.

soft parrot
#

But I can try it again

atomic swallow
#
  1. you're querying the db for ALL servers, for each guild. Seems unnecessary to do it more than once. (ie, if you have 10 guilds, you're querying all data 10x)
  2. you're potentially fetching a message multiple times if the guild_id and message ID exist more than once. (ie, if a guild and message combo exists 3 times, you're fetching 3 times, ratelimits could become an issue)
  3. You're editing a message each time the guild.id appears in the query results. (same as 2, still ratelimits could be an issue)
soft parrot
#

Ok so

  1. move db request outside of for guilds loop
  2. guild id + message id combo only exists 1x
  3. what is the rate limit id be hitting?
atomic swallow
#

also it should be bot.guilds

atomic swallow
soft parrot
soft parrot
#

Also time can be adjusted it’s at a minute rn to make testing easier

#

so i updated it to this:

#
    @tasks.loop(minutes = 1)
    async def update_pop_embed(self):
        await bot.wait_until_ready()
        servers = await bot.db.fetch(f"SELECT * FROM pop_embed")
        for guild in bot.guilds:
            now1 = datetime.now()
            start_time1= now1.strftime('%I:%M %p')
            embed = disnake.Embed(title=f'**__Pop Tracking__**', color=color)
            embed.set_thumbnail(url=bot.user.display_avatar.url)
            embed.set_footer(text=f'{bot.user.name} v{ver} | Updated {start_time1}', icon_url=bot.user.display_avatar.url)

            for i in servers:
                if i[0] == guild.id and i[7] != None:
                    channel = bot.get_channel(i[7])
                    message = await channel.fetch_message(i[8])
                if i[0] == guild.id: 
                    if i[2] == 'Xbox':
                        emoji = xbox
                    if i[2] == 'PlayStation':
                        emoji = ps
                    if i[1] != None and i[0] == guild.id:
                        embed.add_field(name=f"{emoji} **{i[1].upper()}**", value=f"Players Online: {i[4]}/{i[5]}\nMaps Online: {i[6]}\nVersion: {i[3]}", inline=False)
            if message.author == bot.user:
                await message.edit(embed=embed)```
#

and get this error: line 129, in update_pop_embed if message.author == bot.user: UnboundLocalError: local variable 'message' referenced before assignment

atomic swallow
#

right, because message will not exist until it's created

soft parrot
#

Message is created in the for i in servers loop though

atomic swallow
#

Let's say, current guild is Disnake.

While on the current iteration of guilds, (disnake)
it's trying to compare all the db data as long as the ID matches the guild (disnake) id.
But each time it doesn't match, it's skipping that loop, so message isn't created.

#

So if you have 10 servers in the db, only one matches the disnake guild id, right?
So, 9 other times, it doesn't and message isn't created.

#
@tasks.loop(minutes=1)
async def update_pop_embed(self) -> None:

    emojis = {"Playstation": ps, "Xbox": xbox}
    servers = await bot.db.fetch(f"SELECT * FROM pop_embed")

    for guild in bot.guilds:
        now = datetime.now().strftime("%I:%M %p")
        embed = disnake.Embed(title="**__Pop Tracking__**", color=color)
        embed.set_thumbnail(url=bot.user.display_avatar.url)
        embed.set_footer(
            text=f"{bot.user.name} v{ver} | Updated {now}", icon_url=bot.user.display_avatar.url
        )
        message = None  # set the default to None in case it's not found later
        for server in servers:
            if guild.id != server[0]:
                continue

            channel = self.bot.get_partial_messageable(server[7], type=disnake.TextChannel)
            if channel is None:
                continue

            message = self.bot.get_message(server[8]) or await channel.fetch_message(server[8])
            if message is None:
                continue

            emoji = emojis.get(server[2], "")
            if server[1] is not None:
                embed.add_field(
                    name=f"{emoji} **{i[1].upper()}**",
                    value=f"Players Online: {server[4]}/{server[5]}\nMaps Online: {server[6]}\nVersion: {server[3]}",
                    inline=False,
                )

        if message:
            # tries to edit the message, if it fails for any reason it will continue 
            # to the next guild
            try:
                await message.edit(embed=embed)
            except disnake.HTTPException:
                pass

used a PartialMessage rather than fetching a message since you're just editing.

soft parrot
#

so that gave this error: line 129, in update_pop_embed message = channel.get_message(server[8]) or channel.get_partial_message(server[8]) AttributeError: 'TextChannel' object has no attribute 'get_message'

atomic swallow
#

wait.

#

oh duh

soft parrot
#

isnt it channel.fetch_message

#

not get_message

atomic swallow
#

Removing the need to fetch.

#

updated

#

so now the only API call should be the message.edit

soft parrot
#

you also need this await bot.wait_until_ready() before servers = db connection

atomic swallow
#

Oh.

soft parrot
#

since iits in a cog

#

adding that line fixes the bot has no attribute db error

#
    @tasks.loop(minutes=1)
    async def update_pop_embed(self) -> None:
        await bot.wait_until_ready()
        servers = await bot.db.fetch(f"SELECT * FROM pop_embed")
        emojis = {"playstation": ps, "xbox": xbox}```
atomic swallow
#

added that part, too.. Forgot

#

did it as a before_loop

soft parrot
#

ah fair

atomic swallow
#

so it only happens once, rather than every time the loop runs

#

since you only need to wait the first time

soft parrot
#

makes sense

#
TypeError: Expected TextChannel, DMChannel, VoiceChannel, Thread, or PartialMessageable with a valid type, not <class 'disnake.channel.PartialMessageable'> (type: None)```
atomic swallow
#

umm

soft parrot
#

message = channel.get_partial_message(server[8])

#

thats the line its referring to

atomic swallow
#

oh

#

I got the number messed up

#

channel is [7]

#

no

#

oh.

soft parrot
#

message id is 8

#

channel id is 7

atomic swallow
#

channel = self.bot.get_partial_messageable(server[7], type=disnake.TextChannel)

#

Just need to define the type of channel.

#

I've used partial_message before. First time playing with partial_messageable (channels)

soft parrot
#

Wouldn’t it still be 8 not 7

atomic swallow
#

The issue is getting the channel correctly.

#

So, update that line

#

message should work afterwards

soft parrot
#
    emoji = emojis.get(server[2].lower(), "")
AttributeError: 'NoneType' object has no attribute 'lower'```
#

progress lol

atomic swallow
#

so server[2] can be None?

soft parrot
#

Rarely but yes

atomic swallow
#

OK.

#

updated above

soft parrot
#

so i did this

atomic swallow
#

Just need to make it match the case then

soft parrot
#
      emoji = emojis.get(server[2].lower(), "")
else:
    emoji = ''```
atomic swallow
#

and remove the lower()

#

or that

#

but you need emoji to have a default of ''

#

that way if it is None, it will just give emoji a value of ''

#

You could do that, too

#

I'd just probably give it a default under message = None

soft parrot
#

ah ok

atomic swallow
#
...
message = None
emoji = ''
#

then update as needed

#

but doesn't matter

soft parrot
#

gotcha

#

now this

#
    await message.edit(embed=embed)
AttributeError: 'PartialMessageable' object has no attribute 'edit'```
atomic swallow
#

well that's not true..

#

it is.

#

Shit

#

message = self.bot.get_message(server[8]) or await channel.fetch_message(server[8])

#

should work.

#

get the message from cache, or fetch if it's not available.

#

Assuming you have message cache enabled (via intents)

soft parrot
#

intents are defined as disnake.default()

#

and i think it has member as well

atomic swallow
#

default is all but presences, members, and message_content

#
intents = disnake.Intents.default()
intents.members = True

bot = commands.Bot(intents=intents, ...)

Is that what you have?

soft parrot
#

yes

atomic swallow
#

Then yes.

#

That should work. If it can't get the message from cache, it will fetch it

soft parrot
#

ok i will try running it again'

atomic swallow
#

better than fetching 100% of the time.

soft parrot
#

true and the bot rarely goes down unless im messing with it lol

#

how often would you suggest the loop run, to update as often as possible but avoid rate limits?

atomic swallow
#

Depends on how often you need it to.

#

And how many messages you'll be updating.

soft parrot
#

bot is in 1000 servers

#

assuming 100 people use the feature

#

and i know some people want every min

#

but thats unrealistic

#

i was thinking 2-3 mins

atomic swallow
#

you COULD, store that config info and run the task every minute, but only update the ones that want to be updated every minute

#

But, that sounds like a nightmare.

soft parrot
#

yea...

#

no one gets special treatment lol

atomic swallow
#

Disnake is good about handling rate limits, but the less often it happens the better

soft parrot
#

ill stick with 2 mins and if it becomes an issue ill slow it down

#

it is working

#

so much appreciated, thank you

atomic swallow
#

working AND giving you the correct info in the right places?

soft parrot
#

yes

atomic swallow
#

Sweet.

soft parrot
#

and i learned something

#

never knew about get_partial_messageable

atomic swallow
#

We eventually got there. I haven't written any code in over a week and my memory of disnake is a tad rusty 😄

soft parrot
#

lmao i am still impressed and thankful

atomic swallow
#

Yeah. It definitely comes in handy

soft parrot
#

well

#

things were good

atomic swallow
#

Hmm

#

Oh..

#

Easy fix.

soft parrot
#

Those are my favorite kind of fixes

atomic swallow
#
for server in servers:
  if guild.id != server[0]:
    continue
  if server[7] is None or server[8] is None:
    continue
  channel = self.bot.get_partial_messageable(server[7], type=disnake.TextChannel)
  message = self.bot.get_message(server[8]) or await channel.fetch_message(server[8])
#

because they are partial_messageable, they won't return None.

#

so we need to check if the value is None before creating the objects

soft parrot
#

Ah

#

Added and restarting now

#

Looks like that fixed it

atomic swallow
#

Nice

soft parrot
#

One more question

#

Someone is having theirs update, but nothing is in the embed

#

It’s just an empty embed

atomic swallow
#

if server[1] is not None:

#

if the 2nd index is None, embed field isn't added

soft parrot
#

Hm he said he got 4 updates in a row with nothing

#

Despite having 6 servers set to be shown

atomic swallow
#

check your data in the db

#

That's the only thing that would prevent the add_field from executing

soft parrot
#

There is currently 4 servers in the db with the second index not none