Parsing line by line in discord bot (node.js) - node.js

I'm making a discord bot with node.js and I have a .txt file with 3 lines:
User1|421
User2|5543
User3|12
And I want to parse only numbers after '|', I've used
fs.readFile('balance.txt', 'utf8', function(err, contents) {
message.channel.sendMessage(contents);
})
to get the text from it, what I want to do is check if the user that sends the request to check balance exists in the list, and if it exists print out the balance.
I'd like for the input to be
!balance
and output to be
$43
while checking:
User exists?
Get line in which the user is in
Get number after '|' and store it in a temporary variable to print
Thanks!

I'd try this approach, read this file into memory and create an accessor function for it:
const fs = require("fs");
const os = require('os');
fs.readFile('balance.txt', 'utf8', function(err, contents) {
if (err) {
console.error("Error occurred: ", err);
} else {
let balancesDetails = getUserBalances(contents);
let user3BalanceDetails = balancesDetails.find(result => result.user === "User3");
console.log("User 3 balance: ", user3BalanceDetails.balance);
// Send the balance if required
//message.channel.sendMessage(user3BalanceDetails.balance);
}
});
function getUserBalances(fileContents) {
return fileContents.split(os.EOL).map(line => line.split("|")).map(([user, balance]) => { return { user, balance: parseFloat(balance)} });
}
An example of this working in plain JavaScript would be like below (the only real difference is we can't use the OS object since this is Node.js only!):
let contents = `User1|421\nUser2|5543\nUser3|12`;
function getUserBalanceArray(fileContents) {
return fileContents.split('\n').map(line => line.split("|")).map(([user, balance]) => { return { user, balance: parseFloat(balance)} });
}
function getUserBalanceMap(fileContents) {
return fileContents.split('\n').map(line => line.split("|")).reduce((map, [user, balance]) => { map[user] = parseFloat(balance);
return map }, {});
}
console.log("File contents:\n" + contents);
console.log("Balance array: ", getUserBalanceArray(contents));
console.log("Balance map: ", getUserBalanceMap(contents));

Related

discord.js guildMemberAdd() not adding correct value to Object

I have been asked to create a Discord bot that displays a list of guild member usernames and the date they last posted in Discord, it should also display a seperate list of members who have not posted anything at all. In order to achieve this the information is pulled from the Discord API and saved in a JSON file. If a new member joins then the message date is set to 00/00/0000 so I can then use an if statement to pull these members into a seperate list.
bot.on("ready", async () => {
console.log(`${bot.user.username} is online!`);
const guild = bot.guilds.cache.get('SERVER ID NUMBER');
guild.members.fetch().then(member => {
member.forEach(member => {
let memberArray = JSON.parse(fs.readFileSync("./messages.json", "utf8"));
if(!member.user.bot) {
if(!memberArray[member.user.id]) {
memberArray[member.user.id] = {name: member.user.username, date: "00/00/0000"}
}
}
fs.writeFileSync("./messages.json", JSON.stringify(memberArray), (err) => {
if (err) console.log(err)
})
})
})
});
bot.on("guildMemberAdd", (member) => {
let memberArray = JSON.parse(fs.readFileSync("./messages.json", "utf8"));
if(!member.user.bot) {
if(!memberArray[member.user.id]) {
memberArray[member.user.id] = {name: member.user.username, date: "00/00/0000"}
}
}
fs.writeFileSync("./messages.json", JSON.stringify(memberArray), (err) => {
if (err) console.log(err)
})
});
bot.on("guildMemberRemove", (member) => {
let memberArray = JSON.parse(fs.readFileSync("./messages.json", "utf8"));
if(member.user.bot) {
return;
} else {
delete memberArray[member.user.id]
}
fs.writeFileSync("./messages.json", JSON.stringify(memberArray), (err) => {
if (err) console.log(err)
})
});
bot.on("message", async message => {
if (message.author.bot) return;
const guild = bot.guilds.cache.get('SERVER ID NUMBER')
guild.members.fetch().then(member => {
let memberArray = JSON.parse(fs.readFileSync("./messages.json", "utf8"));
member.forEach(member => {
if(member.user.bot) {
return;
}
var d = new Date(message.author.lastMessage.createdTimestamp);
day = d.getDate();
month = d.getMonth() + 1;
date = ("0" + d.getDate()).slice(-2) + "/"
+ ("0" + (d.getMonth() + 1)).slice(-2) + "/"
+ d.getFullYear();
if (message.author.username == memberArray[member.user.id].name) {
memberArray[member.user.id].date = date;
}
})
fs.writeFile("./messages.json", JSON.stringify(memberArray),(err) => {
if (err) console.log(err)
})
})
let prefix = botconfig.prefix;
let messageArray = message.content.split(" ");
let cmd = messageArray[0].toLowerCase();
let args = messageArray.slice(1);
let commandfile = bot.commands.get(cmd.slice(prefix.length));
if (commandfile) commandfile.run(bot, message, args, tools);
});
bot.login(botconfig.token)
Everything works fine in my test server but when I add the bot to the live server the date for any new members is set as the date they join instead of 00/00/0000.
I have ran various tests, added the same bots to my test server incase they were causing some kind of issue and I can't figure out why it works on one server but not the other.
When someone join a server, there is a system message (if enabled) that will pop:
The problem is that, on discord.js side, the system message's author is the user who joined.
So when the user join the server and you assign the time to '00/00/0000' it is instantly overwritten by the date of the system message.
You can actually ignore those system message by checking the type of the message. You have the list of the different type here: discord.js.org. A normal message will have a type equal to DEFAULT while a system message when a user join will be GUILD_MEMBER_JOIN.
Small improvement remark:
when you get a message from an user, you can request the guild directly from message.guild instead of fetching it with the id every time. If your bot accept dm, you can use the message.channel.type (the different values are listed on discord.js.org)
You can format a date using toLocaleDateString. See https://stackoverflow.com/a/34015511/8040287 for details.

is it possible to edit a field in JSON from node js? [duplicate]

How do I update a value in a json file and save it through node.js?
I have the file content:
var file_content = fs.readFileSync(filename);
var content = JSON.parse(file_content);
var val1 = content.val1;
Now I want to change the value of val1 and save it to the file.
Doing this asynchronously is quite easy. It's particularly useful if you're concerned with blocking the thread (likely). Otherwise, I'd suggest Peter Lyon's answer
const fs = require('fs');
const fileName = './file.json';
const file = require(fileName);
file.key = "new value";
fs.writeFile(fileName, JSON.stringify(file), function writeJSON(err) {
if (err) return console.log(err);
console.log(JSON.stringify(file));
console.log('writing to ' + fileName);
});
The caveat is that json is written to the file on one line and not prettified. ex:
{
"key": "value"
}
will be...
{"key": "value"}
To avoid this, simply add these two extra arguments to JSON.stringify
JSON.stringify(file, null, 2)
null - represents the replacer function. (in this case we don't want to alter the process)
2 - represents the spaces to indent.
//change the value in the in-memory object
content.val1 = 42;
//Serialize as JSON and Write it to a file
fs.writeFileSync(filename, JSON.stringify(content));
// read file and make object
let content = JSON.parse(fs.readFileSync('file.json', 'utf8'));
// edit or add property
content.expiry_date = 999999999999;
//write file
fs.writeFileSync('file.json', JSON.stringify(content));
addition to the previous answer add file path directory for the write operation
fs.writeFile(path.join(__dirname,jsonPath), JSON.stringify(newFileData), function (err) {})
I would strongly recommend not to use synchronous (blocking) functions, as they hold other concurrent operations. Instead, use asynchronous fs.promises:
const fs = require('fs').promises
const setValue = (fn, value) =>
fs.readFile(fn)
.then(body => JSON.parse(body))
.then(json => {
// manipulate your data here
json.value = value
return json
})
.then(json => JSON.stringify(json))
.then(body => fs.writeFile(fn, body))
.catch(error => console.warn(error))
Remeber that setValue returns a pending promise, you'll need to use .then function or, within async functions, the await operator.
// await operator
await setValue('temp.json', 1) // save "value": 1
await setValue('temp.json', 2) // then "value": 2
await setValue('temp.json', 3) // then "value": 3
// then-sequence
setValue('temp.json', 1) // save "value": 1
.then(() => setValue('temp.json', 2)) // then save "value": 2
.then(() => setValue('temp.json', 3)) // then save "value": 3
For those looking to add an item to a json collection
function save(item, path = './collection.json'){
if (!fs.existsSync(path)) {
fs.writeFile(path, JSON.stringify([item]));
} else {
var data = fs.readFileSync(path, 'utf8');
var list = (data.length) ? JSON.parse(data): [];
if (list instanceof Array) list.push(item)
else list = [item]
fs.writeFileSync(path, JSON.stringify(list));
}
}
Save data after task completion
fs.readFile("./sample.json", 'utf8', function readFileCallback(err, data) {
if (err) {
console.log(err);
} else {
fs.writeFile("./sample.json", JSON.stringify(result), 'utf8', err => {
if (err) throw err;
console.log('File has been saved!');
});
}
});
Promise based solution [Javascript (ES6) + Node.js (V10 or above)]
const fsPromises = require('fs').promises;
fsPromises.readFile('myFile.json', 'utf8')
.then(data => {
let json = JSON.parse(data);
//// Here - update your json as per your requirement ////
fsPromises.writeFile('myFile.json', JSON.stringify(json))
.then( () => { console.log('Update Success'); })
.catch(err => { console.log("Update Failed: " + err);});
})
.catch(err => { console.log("Read Error: " +err);});
If your project supports Javascript ES8 then you could use asyn/await instead of native promise.

How can i store firebase data to json in local

In my case I got the data from firestore now how can I save it to
serversettings.json:
var temp = {}
let query = db.collection('guilds')
let data = query.get().then(snapshot => {
snapshot.forEach(doc => {
console.log(doc.id, '=>',doc.data());
})
so I get the output as:
637301291068030997 => { welcomeChannelID: '<#648968505160630285>',
guildMemberCount: 4,
guildOwnerID: '348832732647784460',
guildOwner: 'Ethical Hacker',
prefix: '.',
guildID: '637301291068030997',
guildName: 'test server 3' }
and this:
GUqGqFanJuN7cRJx4S2w => {}
I need to save that data to serversettings.json
await fs.writeFile ("../serversettings.json", JSON.stringify(temp), function(err) {
if (err) throw err;
console.log('done');
})
here temp is variable where multiple data is stored like a:{},b:{}....
i tried var temp = {} temp.table = [] and then temp.table.push(doc.id, ':',doc.data())
but i get empty output so what can i do to get that expected output ?
also, adding to that how can I update the values if that object is already present in JSON will the above function work the same will it override the same value or delete all other values example update prefix from. to, then await fs.writeFile ("../serversettings.json", JSON.stringify(temp),..... so the temp field has a value of only guild id and that field prefix will it update the only prefix and not delete anything else in that array?
HERE is the code that added stuff to temp variable
var temp = {}
temp.guilds = [] // after some lines
snapshot.forEach(doc => {
console.log(doc.id, '=>',doc.data()); // output is above this code
temp.guilds.push(doc.id = doc.data()) // output is below this code
})
Above codes output
{ guilds:
[ { guildID: '637301291068030997', // here missing doc.id field
guildName: 'test server 3',
welcomeChannelID: '-',
guildMemberCount: 4,
guildOwnerID: '348832732647784460',
guildOwner: 'Ethical Hacker',
prefix: '.' },
{} // this missing thing before {} is (some number) also bracket is empty by the way so no worries
]
}
A fast solution for your issue would be to replace
let data = query.get().then(snapshot => {
with
await query.get().then(snapshot => {
so that your temp object can be filled before the program proceeds to save the file.
I haven't used writeFile yet, but here's what its documentation says:
When file is a filename, asynchronously writes data to the file, replacing the file if it already exists.
I don't think your object will be so large that a complete overwrite would be a problem, unless it's changing very often. In that case, I guess you'd have to use a different method that can support an offset, so that you can write only what has changed, but that seems like a real overkill.
Regarding the format of your JSON file, I think what you're trying to do is this:
var temp = {};
temp.guilds = {};
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
temp.guilds[doc.id] = doc.data();
});
i`m not sure , but firebase has method to convert data from object to JSON, so i think this solution should work
let query = db.collection('guilds')
let data = query.get().then(snapshot => {
let temp = snapshot.toJSON()
await fs.writeFile ("../serversettings.json", temp, function(err) {
if (err) throw err;
console.log('done');
})
})

Node.js function not running in order. Error: Unhandled stream error in pipe

I updated the function to create the CSV file but now I'm getting an error:
In upload function
internal/streams/legacy.js:57
throw er; // Unhandled stream error in pipe.
^
Error: ENOENT: no such file or directory, open 'C:\Users\shiv\WebstormProjects\slackAPIProject\billingData\CSV\1548963844106output.csv'
var csvFilePath = '';
var JSONFilePath = '';
function sendBillingData(){
var message = '';
axios.get(url, {
params: {
token: myToken
}
}).then(function (response) {
message = response.data;
fields = billingDataFields;
// saveFiles(message, fields, 'billingData/');
saveFilesNew(message, fields, 'billingData/');
var file = fs.createReadStream(__dirname + '/' + csvFilePath); // <--make sure this path is correct
console.log(__dirname + '/' + csvFilePath);
uploadFile(file);
})
.catch(function (error) {
console.log(error);
});
}
The saveFilesNew function is:
function saveFilesNew(message, options, folder){
try {
const passedData = message;
var relevantData='';
if (folder == 'accessLogs/'){
const loginsJSON = message.logins;
relevantData = loginsJSON;
console.log(loginsJSON);
}
if(folder == 'billingData/'){
relevantData = passedData.members;
const profile = passedData.members[0].profile;
}
//Save JSON to the output folder
var date = Date.now();
var directoryPath = folder + 'JSON/' + date + "output";
JSONFilePath = directoryPath + '.json';
fs.writeFileSync(JSONFilePath, JSON.stringify(message, null, 4), function(err) {
if (err) {
console.log(err);
}
});
//parse JSON onto the CSV
const json2csvParser = new Json2csvParser({ fields });
const csv = json2csvParser.parse(relevantData);
// console.log(csv);
//function to process the CSV onto the file
var directoryPath = folder + 'CSV/' + date + "output";
csvFilePath = directoryPath + '.csv';
let data = [];
let columns = {
real_name: 'real_name',
display_name: 'display_name',
email: 'email',
account_type: 'account_type'
};
var id = passedData.members[0].real_name;
console.log(id);
console.log("messageLength is" +Object.keys(message.members).length);
for (var i = 0; i < Object.keys(message.members).length; i++) {
console.log("value of i is" + i);
var display_name = passedData.members[i].profile.display_name;
var real_name = passedData.members[i].profile.real_name_normalized;
var email = passedData.members[i].profile.email;
var account_type = 'undefined';
console.log("name: " + real_name);
if(passedData.members[i].is_owner){
account_type = 'Org Owner';
}
else if(passedData.members[i].is_admin){
account_type = 'Org Admin';
}
else if(passedData.members[i].is_bot){
account_type = 'Bot'
}
else account_type = 'User';
data.push([real_name, display_name, email, account_type]);
}
console.log(data);
stringify(data, { header: true, columns: columns }, (err, output) => {
if (err) throw err;
fs.writeFileSync(csvFilePath, output, function(err) {
console.log(output);
if (err) {
console.log(err);
}
console.log('my.csv saved.');
});
});
} catch (err) {
console.error(err);
}
}
The upload file function is:
function uploadFile(file){
console.log("In upload function");
const form = new FormData();
form.append('token', botToken);
form.append('channels', 'testing');
form.append('file', file);
axios.post('https://slack.com/api/files.upload', form, {
headers: form.getHeaders()
}).then(function (response) {
var serverMessage = response.data;
console.log(serverMessage);
});
}
So I think the error is getting caused because node is trying to upload the file before its being created. I feel like this has something to do with the asynchronous nature of Node.js but I fail to comprehend how to rectify the code. Please let me know how to correct this and mention any improvements to the code structure/design too.
Thanks!
You don't wait for the callback provided to stringify to be executed, and it's where you create the file. (Assuming this stringify function really does acccept a callback.)
Using callbacks (you can make this cleaner with promises and these neat async/await controls, but let's just stick to callbacks here), it should be more like:
function sendBillingData() {
...
// this callback we'll use to know when the file writing is done, and to get the file path
saveFilesNew(message, fields, 'billingData/', function(err, csvFilePathArgument) {
// this we will execute when saveFilesNew calls it, not when saveFilesNew returns, see below
uploadFile(fs.createReadStream(__dirname + '/' + csvFilePathArgument))
});
}
// let's name this callback... "callback".
function saveFilesNew(message, options, folder, callback) {
...
var csvFilePath = ...; // local variable only instead of your global
...
stringify(data, { header: true, columns: columns }, (err, output) => {
if (err) throw err; // or return callbcack(err);
fs.writeFile(csvFilePath , output, function(err) { // NOT writeFileSync, or no callback needed
console.log(output);
if (err) {
console.log(err);
// callback(err); may be a useful approach for error-handling at a higher level
}
console.log('my.csv saved.'); // yes, NOW the CSV is saved, not before this executes! Hence:
callback(null, csvFilePath); // no error, clean process, pass the file path
});
});
console.log("This line is executed before stringify's callback is called!");
return; // implicitly, yes, yet still synchronous and that's why your version crashes
}
Using callbacks that are called only when the expected events happen (a file is done writing, a buffer/string is done transforming...) allows JS to keep executing code in the meantime. And it does keep executing code, so when you need data from an async code, you need to tell JS you need it done before executing your piece.
Also, since you can pass data when calling back (it's just a function), here I could avoid relying on a global csvFilePath. Using higher level variables makes things monolithic, like you could not transfer saveFilesNew to a dedicated file where you keep your toolkit of file-related functions.
Finally, if your global process is like:
function aDayAtTheOffice() {
sendBillingData();
getCoffee();
}
then you don't need to wait for the billing data to be processed before starting making coffee. However, if your boss told you that you could NOT get a coffee until the billing data was settled, then your process would look like:
function aDayAtTheOffice() {
sendBillingData(function (err) {
// if (err) let's do nothing here: you wanted a coffee anyway, right?
getCoffee();
});
}
(Note that callbacks having potential error as first arg and data as second arg is a convention, nothing mandatory.)
IMHO you should read about scope (the argument callback could be accessed at a time where the call to saveFilesNew was already done and forgotten!), and about the asynchronous nature of No... JavaScript. ;) (Sorry, probably not the best links but they contain the meaningful keywords, and then Google is your buddy, your friend, your Big Brother.)

Store imap.fetch result in a variable and iterate anytime

I am trying to store the result from imap.fetch() into a variable, so that i can iterate it whenever i want to check all mail.
But it is not working when i call my getAllMail() from another class, Thanks for any help.
let allSearch;
imap.search(['All', ['ON', date]], (err,res) => {
if (err) console.log(err)
else {
allSearch = imap.fetch(res, {bodies:'HEADER.FIELDS (FROM SUBJECT)', struct:true})
}
})
function getAllMail() {
allSerch.on('message', (msg, seq) => {
console.log()
})
}
module.exports.getAllMail = getAllMail

Resources