简体   繁体   中英

Edit part of a Message Embed (Discord.JS)

I have a channel that contains 10 embedded messages (1 embed per message). Each embed is a leaderboard for people's best lap times, by Track.

The layout of each embed is

const trackName = new MessageEmbed
.setTitle(trackName) 
.addField(user1, lapTime)
.addField(user2, lapTime) 
.addField(user3, lapTime)

Assume for example the 3rd embed looks something like this:

| Track 3 Name

| John 37 seconds

| Chris 39 seconds

| Jeff 40 seconds

Beyond simply editing the embed and sending all the information updated manually, how could I update just one particular slot? For example, say Clark comes in with a 38 second lap, how would I move Chris to 3rd, remove Jeff, and add Clark to 2nd so the embed looks as such

| Track 3 Name

| John 37 seconds

| Clark 38 seconds

| Chris 39 seconds

Without changing any other embeds in the channel

You don't necessarily need to create an entirely new embed and input all of the information into it. You could get the current embed from the message, and edit the specific fields you need to edit. Here is an example that you could adapt to work with your system:

function editTrack(msg, newUser, newTime) {
    //Assume `msg` is the message in the channel containing the embed you want to edit
    //currentEmbed is now the embed you want to edit
    let currentEmbed = msg.embeds[0];

    //Add `newUser` and its `newTime` to the embed as a new field, in the last position
    currentEmbed.addField(newUser, newTime);

    //Sorts the embed's fields by the lap times of each user, from lowest to highest
    //This example numerically sorts the fields by the number in their value
    //This does most of the work for you; the laps are now in the correct order
    currentEmbed.fields.sort((a, b) => Number(a.value.split(" second")[0]) - Number(b.value.split(" second")[0]));

    //If you want to display only the top 3, remove the 4th field of the embed (if any)
    if (currentEmbed.fields.length == 4) currentEmbed.fields.splice(3, 1);

    //Now, we need to edit the message with our updated embed (djs 13.x syntax)
    return msg.edit({embeds: [currentEmbed]});
}

I tested this editTrack method using one of my bots' eval commands:

BEFORE -
首先嵌入

AFTER -
编辑嵌入

The original embed is successfully edited with the new information. And it only required the Message object containing the embed, the new lap's user, and the new lap's time.


Edit based on OP's comment:

For the answer to work when editing the track embed with an existing user, who has a new lap time, a slight modification needs to be made. Before removing the 4th field of the embed, you must do something like this:

const names = new Set();
currentEmbed.fields = currentEmbed.fields.filter(field =>
    !names.has(field.name) && names.add(field.name)
);

Here's what that code is doing. First, we create a Set . Sets are iterable structures similar to arrays, but they can only contain unique values. We can therefore use sets to ensure there are no repeats of a user's name. Next, we filter the fields; only the fields whose user is not already contained in names will remain. If a user's name is not already in names , it gets added to the set via .add() . And because the fields are already sorted with the quickest lap time coming first, only the quickest lap of a given user will remain; any longer lap times by the same user will be filtered out.

Note that I only tested this edit briefly, let me know if there are any logical errors or further issues caused by it (or if it fails to work entirely).

Thanks to Cannicide's helpful answer, it led me down the right track.

To achieve the end result of sorting times and overwriting existing times, I ended up with the following function. I require all times be submitted in the format: MINUTES:SECONDS (0:35 = 35s | 4:24 = 4m24s | 62:08 = 1h2m8s).

function editLb(theMessage, newUser, newTime) {
    //get the embed you want to edit
    let currentEmbed = theMessage.embeds[0];

    //Check all existing fields for the same newUser, if the same newUser
    //is found, replace that entire field with the name: "Placeholder"
    //and the value: "999:99". This will also remove any existing duplicates.
    currentEmbed.fields.forEach(field => {
        if (field.name == newUser) {
            field.name = `Placeholder`;
            field.value = `999:99`;
        }
    })

    //add the newUser and the newTime to a new field
    currentEmbed.addField(`${newUser}`, `${newTime}`);

    //sort all available fields effectively by seconds, by taking 
    // (minutes*60) + (seconds)
    currentEmbed.fields.sort((a, b) => Number((a.value.split(":")[0])*60 + (a.value.split(":")[1])) - Number((b.value.split(":")[0])*60 + (b.value.split(":")[1])));
    
    //If there are now 4 fields, remove the slowest(last) one.
    if (currentEmbed.fields.length == 4) currentEmbed.fields.splice(3, 1);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM