Search code examples
pythonpython-3.xdiscorddiscord.py

How to make your Discord bot add a string of text to it's previous message?


I need my Discord bot to edit their own previous message, adding a string of text. But I don't understand how to accomplish this. I am using discord.py.

My Discord bot is for hosting reservations for a grand-strategy video game. The bot sends the contents of a .txt file into a channel via message, which contains a list of playable nations. Then a user can do '!reserve (x nation)', and the bot proceeds to update it's list to add that user's name to the list, next to their chosen (x nation).

However, I don't understand how to make the bot edit, and then add strings of text to a message it has previously sent. I have looked through discord.py documentation and browsed stackoverflow, but haven't found anything that has addressed this issue. Additionally, I would need to program the bot to put the user's name after the user's chosen 'nation', inside the list.

For example:

bot message:

Playable Nations List
USA
Italy
...

upon command '!reserve USA'

bot message (edited):

Playable Nations List
USA @user
Italy
...

Regarding code; I have set up a function to grab the message ID of the bot's original message. It is simply the process after that which is incomprehensible to me.

Here is the code:

import discord
from discord import app_commands
from discord.ext import commands
from discord.utils import get

intents = discord.Intents.all()
client = commands.Bot(command_prefix='!', intents = intents)
tree = app_commands.CommandTree

@client.event
async def on_ready():
    print("The bot is now ready")
    try:
        synced = await tree.sync(guild=discord.Object(id=GUILD_ID))
        print(f"Synced {len(synced)} command(s)")
    except Exception as e:
        print(e)

@client.command()
async def host(ctx):
    with open('host-template.txt', 'r') as fp:
        message = await ctx.send(fp.read())
    with open('message.txt', 'w') as f:
        f.write(str(message.id))

@client.command()
async def reserve_USA(ctx):
    channel = client.get_channel(CHANNEL_ID)
    with open("message.txt", "r") as f:
        message_id = int(f.read())
    message = await channel.fetch_message(message_id)
    await message.edit(content="CONTENT") #This basic message.edit function is not feasible.



client.run(BOTTOKEN)

Here is the contents of 'host-template.txt':

Available Nations

USA
Japan
UK
USSR
Germany
France
Italy

Solution

  • I think you are better off using embeds for this. Embeds have fields, which can be easily manipulated. For every country, you can add a field and then add a person under that field. Besides, it will also look way more appealing to the eye.

    For starters, you'll have to remove the Available Nations from host-template.txt and make it only the countries. Then, you can .readlines() on it, which will return a list with every line.

    with open("host-template.txt") as f:
        countries = f.readlines() # countries will now be ['USA', 'Japan', ...] 
    

    You need to keep the countries in separate lines for this to work.

    And then, you can use discord.Embed and construct an embed and then add a new field for every country. For example:

    country_embed = discord.Embed(title="Available Nations")
    for country in countries:
       country_embed.add_field(name=country, value="N/A") # we can edit the N/A later with our list of members
    

    .send() has an embed kwarg and to send the embed, we will pass our embed instance there.

    await ctx.send(embed=country_embed)
    

    Then, when you edit it, you can fetch the message and access message.embeds which is a list. Since there's only one embed in the message, we get that embed by indexing 0.

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

    Now, we get the field we need to edit. Message.embeds is a list so we'll use indexing to get the field we want. The fields will be created in the order the countries are in host-template.txt, so if we want to access Japan which is second, we'll subtract 1 from it since indexing starts at 0 and not 1. So, 2 - 1 = 1.

    japan_field = country_embed.fields[1]
    # Now, we will remove the field and add it again using `Embed.insert_field_at`.
    country_embed.remove_field(1)
    # We need to check if the value is "N/A" (no users) or if there are users
    new_value = ""
    if japan_field.value == "N/A": # There is no user so we can replace it
        new_value = "<your_new_value>"
    else: # There are users logged, so we add to it
        new_value = f"{japan_field.value}\n" + "<your_new_value>"
    # Now, we insert the field at the same index
    country_embed.insert_field_at(1, name=japan_field.name, value=new_value)
    # And now, edit the message with the new embed
    await message.edit(embed=country_embed)
    

    This can be hard to follow, if you have any doubts you can comment below.

    References: