1

For a Discord bot, I currently have a command that displays info for our DnD sessions:

pictured.

The data is stored in a dndinfo.json file thats looks like this:

{"time":"**18:00 UK time**",
"date":"**14/05/20**",
"dm":"**Mannion**",
"prime":"Playing",
"smurphy":"Playing",
"calle":"Playing",
"smardon":"Playing",
"james":"Playing",
"mannion":"DMing",
"dex":"Playing",
"module":"Hoard of the Dragon Queen"}

I want users to be able to do a command, such as '!te time 17:00', which will update the time accordingly.

I currently am working with this code:

const Discord = module.require('discord.js');
const fs = require('fs');
const dndinfo = require ('../../dndinfo.json');

module.exports = {
    name: 'test',
    aliases: ['te'],
    category: 'dnd',
    description: 'Updates DnD info',
    usage: '!te',
    run: async (client, message, args) => {

        const time = dndinfo.time;
        let editMessage = message.content.slice(9);

        if (message.content.toLowerCase().includes('time')) {

            fs.readFile('dndinfo.json', function(err, data) {
                console.log(time);
                fs.writeFile('dndinfo.json', JSON.stringify(editMessage, null, 2), (err) => {
                    if (err) console.error;
                    message.channel.send ('message written');

                });
            });
        }
    },
};

When I run the command '!te time 17:00', the entire dndinfo.json file is replaced with:

"17:00"

I understand that it's because i'm using fs.writeFile but i'm not sure of how to specify only 'time' and have that updated?

JHPrime
  • 45
  • 5
  • Why not rewriting the entire file? It's very small and faster than modifying a part of it. Take the data from readfile, parse the Json, replace the time key's value and write it back. Modifying just a part of it will require you to read in streaming, parse it, find the position row/column and how many characters to remove/replace. That can be done using `regex` but that defeats the purpose of the json nodejs power. – Alexis May 01 '20 at 23:09

3 Answers3

1

There is no practical way to update just a piece of JSON in a file. It's just not the type of format or the layout on disk that makes that practical.

The usual mechanism for updating what's in the file is to just write out the whole JSON structure with your modifications in place, replacing the original contents of the file with the new content. If you don't already have all the content you need to do that, then you would first read in the contents of the file, making your changes, then write it out.

Here's a function that reads it in, parses the JSON to a Javascript object, modifies that object, converts back to JSON and writes it out.

const fsp = require('fs').promises;

async function udpateTimeInFile(time) {
    try {
        let data = await fsp.readFile('dndinfo.json');
        let obj = JSON.parse(data);

        // set whatever property or properties in the object that you are trying to change
        obj.time = time;

        await fsp.writeFile('dndinfo.json', JSON.stringify(obj));
        message.channel.send('message written');
     } catch(e) {
        // error handling here
        console.log(e); 
        message.channel.send('error writing new data');
        throw e;      // make sure caller can see the message
     }
}
jfriend00
  • 683,504
  • 96
  • 985
  • 979
0

Instead of writing JSON.stringify(editMessage, null, 2) into your JSON you might want to edit the content of it first.

You can replace the content of your file with data.replace() method.

You can refer to this answer for full coverage: https://stackoverflow.com/a/14181136/4865314

CeamKrier
  • 629
  • 7
  • 15
0

Due to the ways files work, the only way you can change part of a file is to replace its contents with stuff that is exactly the same size. Additionally, your file system probably writes to the disk controller in 8K blocks, so there is no /point/ to doing this, except in very exceptional cases.

Besides re-writing the entire file, you also need to think about what happens if the file gets shorter, or there are other processes on the machine reading (or writing!) to the file at the same time.

A very common general solution for that group of problems is to write the new file to a new filename in the same directory, and rename it on top of the old one. This means that other processes reading your file will either get the new file or the old file, which is almost always preferred to a "half and half" solution.

It's funny that this question came up today, as I happened to write exactly this routine earlier today, to store the REPL history for my debugger. If you'd like to read through a complete [but lightly tested] implementation, have a look at

https://github.com/wesgarland/niim/blob/cfb64356c4e5a9394ba2e2b0e82b2f3bf2ba0305/lib/niim.js#L373 and https://github.com/wesgarland/niim/blob/cfb64356c4e5a9394ba2e2b0e82b2f3bf2ba0305/lib/niim.js#L459

These are too big for a stack overflow comment, but I've provided a static link to a specific version of the file and it should last for several years.

Wes
  • 950
  • 5
  • 12