#discord.py edit message

77 messages · Page 1 of 1 (latest)

analog parrot
#
@bot.command()
async def editembed(ctx, channel: discord.TextChannel, message_id: int, color: str, *, new_message: str):
    if ctx.author.id == 963188382119379024 or ctx.author.id == 460512261413404673:
        message = await channel.fetch_message(message_id)
        new_embed = discord.Embed(description=new_message, color=int(color, 16))
        await message.edit(embed=new_embed)
        await ctx.send("Message edited.")
    else:
        await ctx.send("You don't have permission to use this command.")
editembed.help = "EX. editembed #general message_id hex_color new_message"```

This is what I have made atm to edit an embed sent by my bot, the only thing is - its a bit annoying having to enter the hex every time, and when removing the color arguement and editing an embed, it results into changing the embed color to a default color. Is there anyway I can shorten this edit command so I dont have to enter the color arguement every time or maybe even the channel arguement? Thanks guys.
pine surge
#

Nope. Just the way Discord embeds work.

analog parrot
whole eagle
whole eagle
#

you can index message.embeds to get the first embed, and then get the colour of that

#

let me give you an example quickly

#
message_id = xxx

message = await channel.fetch_message(message_id)
first_embed = message.embeds[0]

new_embed = discord.Embed(
    title='Test',
    description=f'Colour of this embed is {first_embed.colour}',
    colour=first_embed.colour,
)

await message.edit(embed=new_embed)```
#

this should work in theory

#

i will test it give me 2

#

works for me :)

pine surge
analog parrot
analog parrot
pine surge
#

Just use a dictionary. The key is the message id, the value is whatever you need to reference with the command.

analog parrot
#

Can you provide an example on how to implement that please

whole eagle
#

but occasionally it would fail because it's not in the cache, but you could try get it, and if that returns none then fetch

pine surge
whole eagle
#

yeah but really isnt it the same thing?
i did think it capped message cache at 1k but i cannot find anything ab it in the docs now 🤷‍♂️

pine surge
#

You never know if or when the discord.py cache might reset because how it behaves is undocumented. Creating your own cache is inexpensive and guaranteed to work for what you're doing.

analog parrot
#

can you guys show me a code example of how you would implement that by any chance

whole eagle
#

maybe you would create a custom context and overwrite the send function to store the message id in your dict with the value being the colour, and then index your dict for the value

#

that's how i would do it

#

i can try do an example in the morning, would be hard for me to do now i'm on my phone

analog parrot
#

that would be great, im having a hard time understanding how to implement your solution

whole eagle
#

oh shit my bad i actually forgot i said that

#

lemme finish this val game

#

shouldnt take long

#

this is just how i would do it, you can probs use an on_message listener or event and do the same

#

bro ive spent ages typing it and i cant send the whole thing

#

i will send the first part then just wait for the second part

#

wait no, i just realised something

#

you would need to use fetch_message anyway, because if the message you want to edit isnt in the cache then there will be issues

#

and you can easily use the .embeds attribute so idk

#

if you want the code just lmk i still have it written, imo theres no point doing it, 2 api calls really will do nothing to your bot unless your running them very frequently

pine surge
#

They want it so they don't have to give a channel id to their command. They just want to provide a message id

whole eagle
#

did they say that?

#

holdup i need to read again

#

oh god, im so used to having discord on a vertical monitor its sm easier to read everthing

#

they will need it anyway im pretty sure

#

there are no methods to my knowledge that can get a discord.Message without a TextChannel

#

well there are, but require other things, discord.Member/ discord.User / discord.Thread

#

i tried using bot.user as a Member and i forgot its a ClientUser so that dosent work

pine surge
#

That's why you need a cache to track the channel ID for each message you might need to change

whole eagle
#

gonna try change the one i made and then hopefull it should work

#

you would subclass your context and then overwrite the send method, using a bot var to store the channel ids, message ids and colour(s)

class MyContext(commands.Context):

    async def send(self, content: str, embed: discord.Embed, embeds: List[discord.Embed], **kwargs):

        msg = await super().send(content=content, embed=embed, **kwargs) # this may not actually store as msg, i need to test what it does return

        self.bot.embed_cache[msg.channel.id]
        if embed: # for a single embed passed
            self.bot.embed_cache[msg.channel.id][msg.id] = str(embed.colour)
        if embeds: # for multiple embeds passed, stores them in a list
            self.bot.embed_cache[msg.channel.id][msg.id] = [str(emd.colour) for emd in embeds]

        # self.bot.embed_cache = {message_id_goes_here: {'channel_id': 1067000133281140767, 'colours': ['#7363ff']} # example of dict layout

then you would subclass commands.Bot and then overwrite the get_context method to return your context, instead of the normal commands.Context
also you will see i have added a self.embed_cache dict, you can use this by doing BotObject.embed_cache

class MyBot(commands.Bot):

    async def get_context(self, message, *, cls = None):
        return await super().get_context(message, cls=cls or CustomContext)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.embed_cache = {}

you need to make sure to change bot = commands.Bot(...) to bot = MyBot(...), typically in your main.py file, to use the MyBot class which has get_context overwritten, they are the exact same class so it makes no difference apart from that

#

wait no

#

thats how i would implement the cache, im just having trouble on laying out the dict

#

then you can edit your command to something like this

@bot.command()
@commands.is_owner() # keeps it simpler, only works for the application owner, remove this and reimplement your other check if you wish, or even better, make a custom check
async def editembed(ctx, message_id: int *, new_message: str): # removed the channel argument as not needed anymore

    try:
        cached = ctx.bot.embed_cache[message_id]
    except IndexError:
        return await ctx.send('Message has not been cached')        

    channel = ctx.bot.get_channel(cached['channel_id']) # may return None
    message = await channel.fetch_message(message_id) # also may return none if message has been deleted
    new_embed = discord.Embed(description=new_message, color=cached['colours'][0])
    await message.edit(embed=new_embed)
    await ctx.send('Message has been edited')
#

in theory this should all work

analog parrot
#

ok thank you so much ill check it out

whole eagle
#

no problem, a more permanent solution would be to use a database, because this will refresh every time your bot restarts

analog parrot
#

thank you for you help, I didint even know you could do it with the way you showed me - learned something new

analog parrot
#

how i have it setup in postgre

whole eagle
#

no problem, make sure to use an async driver such as asyncpg for postgres to avoid blocking code

analog parrot
#

im just using psycopg2 atm

#

for simple data wouldnt psycopg2 be faster/better than asyncpg tho?

pine surge
#

The issue is psycopg2 blocks. Async code can only do 1 thing at a time unless you use libraries that support async. So while you're talking to your database your bot can't respond to commands or events from discord

analog parrot
#

would there be any differences between the libraries in terms of changing my code not only with this bot but my other one that uses psycopg2

pine surge
#

They all talk to PostgreSQL. So they can all use the same database. You might have to rewrite the models if you use different packages. If you use the psycopg package instead of psycopg2 you'll have both sync and async support

whole eagle
#

if you decided to use asyncpg the instead of using .execute() then .fetchone() on the cursor, you would do await Pool.fetchrow()
and if you wanted to get everything that returns from your query, you would use await Pool.fetch()

analog parrot
pine surge
#

It's supposed to be kinda similar but they moved it to a new package because it's not exactly the same. It's a bit confusing but psycopg is psycopg v3, so it's the upgraded version of psycopg2.

analog parrot
#

That’s kind weird lol

pine surge
#

Very lol

analog parrot
#

So it would be a little different from using .execute and .fetchone etc?

pine surge
#

I'm not sure what all the differences are. I haven't needed to use either directly in a very long time

analog parrot
#

Ah gotcha

analog parrot
# analog parrot how i have it setup in postgre

Also quick question about this if you maybe can help but is the only way to have the id go back a number or two (since it will go by increments of one on every new added row) without truncating the whole table identity?

pine surge
#

You shouldn't really need to reset the auto incrementing id counter...

analog parrot
pine surge
#

You shouldn't worry about the ID's. They're only used to identify rows, so they could be random and serve the same purpose.

analog parrot
#

Yea you’re right but just a bit of ocd that I’ll prob have to look over kek