@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.
#discord.py edit message
77 messages · Page 1 of 1 (latest)
Nope. Just the way Discord embeds work.
any idea how popular bots do this then? for example carl bot only takes the message id arguement followed by the message - do they really store each embed in a database or something?
you want the colour of new_embed to be the same as the one in the message you fetched?
Correct
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 :)
They likely cache the necessary information or store it in the interaction state.
worked like a charm!!!! thank you!
any advice on the best way to replicate that by any chance?
Just use a dictionary. The key is the message id, the value is whatever you need to reference with the command.
Can you provide an example on how to implement that please
np, i'm pretty sure if the message is cached there is a .get_message method, that isn't async you could use
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
I wouldn't rely on the discord.py cache. I'd create my own cache that stores just the values that are needed. So you'd use the message id as the key in a dictionary and store the necessary data for future reference as the value.
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 🤷♂️
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.
can you guys show me a code example of how you would implement that by any chance
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
that would be great, im having a hard time understanding how to implement your solution
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
They want it so they don't have to give a channel id to their command. They just want to provide a message id
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
That's why you need a cache to track the channel ID for each message you might need to change
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
ok thank you so much ill check it out
no problem, a more permanent solution would be to use a database, because this will refresh every time your bot restarts
took me a little while but ended up just coding the database solution which im very happy and proud about!
thank you for you help, I didint even know you could do it with the way you showed me - learned something new
how i have it setup in postgre
no problem, make sure to use an async driver such as asyncpg for postgres to avoid blocking code
im sorry what do you mean by that
im just using psycopg2 atm
for simple data wouldnt psycopg2 be faster/better than asyncpg tho?
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
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
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
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()
But if I were to use psycopg instead, would it be the same way I have it now?
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.
That’s kind weird lol
Very lol
So it would be a little different from using .execute and .fetchone etc?
I'm not sure what all the differences are. I haven't needed to use either directly in a very long time
Ah gotcha
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?
You shouldn't really need to reset the auto incrementing id counter...
Well for test inputs which I don’t want to be in there anymore or mess up the number increment
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.
Yea you’re right but just a bit of ocd that I’ll prob have to look over 