I have a bot that posts a message with an embed and some buttons. After some time (up to multiple days) I want to change some text in the embed and remove the buttons. This should happen without reacting to any event but purely based on the timestamp, meaning I don't get an interaction object that references the original message.

I guess that in principle I could use asyncio.sleep() or bot.wait_for(), but I have experienced that these are easy to fail if the connection is lost at some point, even if it's only for a very short time. Most often, when the time passes a few hours, the scheduled event would not trigger. Is there some way to make this more reliable?

Another possibility that comes to my mind would be to record the channel and message id in my DB and then, once the time has come, fetch the message with bot.get_channel(channel_id) and channel.fetch_message(message_id) and update it. This seems like a bit of a workaround to me, so I was wondering if there is a more direct/elegant way of achieving this?

You could try creating a task that checks if the message every 5 seconds for example. See here for an example of a background task.Benjin
If you restart the bot, the data (messages) will need to be somewhere for you to access. So yes, do store the message and channel ids somewhere. You'll need to use a background task to figure out when to next update the message (you can find the min and asyncio sleep until then)Eric Jin