Node.js ''Skips'' a line of code?? [screenshot] - node.js

i am making a script that will take a picture of my desktop (for server disk stuff) but it doesn't take the picture. But it will when i create another script and run that.
const screenshot = require('screenshot-desktop')
const Discord = require('discord.js');
const client = new Discord.Client();
client.on('message', msg => {
if (msg.content === '!Photo') {
screenshot({ filename: 'C:/Users/MyName/Desktop/TestPictureFile.png' })
Bot.sendFile(`C:/Users/MyName/Desktop/TestPictureFile.png`);
}
});
If i only put screenshot({ filename: 'C:/Users/MyName/Desktop/TestPictureFile.png' }) In another script, it works fine. But it doesn't when i try the code above this one.

Whenever a function writes/reads a file (or query database) it is most likely asynchronous which means you need to wait for it to write/read the file then continue.
As you can see in the docs https://www.npmjs.com/package/screenshot-desktop#usage
screenshot() returns a Promise so you have 2 options.
First, call Bot.sendFile in the then function like so
client.on('message', msg => {
if (msg.content === '!Photo') {
screenshot({ filename: 'C:/Users/MyName/Desktop/TestPictureFile.png' }).then(function(){
Bot.sendFile(`C:/Users/MyName/Desktop/TestPictureFile.png`);
})
}
});
or use await like so
client.on('message', msg => {
if (msg.content === '!Photo') {
await screenshot({ filename: 'C:/Users/MyName/Desktop/TestPictureFile.png' })
Bot.sendFile(`C:/Users/MyName/Desktop/TestPictureFile.png`);
}
});

Related

How to delete interaction.channel.send message

I have a function to delete a message after I send it:
export function del(interaction) {
setTimeout(() => interaction.deleteReply(), 1000);
}
I want to convert it so this will be working to interaction.channel.send, earlier I used interaction.reply(), but I don't want my bot to continue using reply to every command. I read Discord docs and didn't find anything helpful.
Any help on how to do this will be appreciated!
When you use interaction.channel.send you need to track the new message that you created (as it separate from the interaction).
Here is a quick sample where I have /ping command that creates and deletes a reply and a message:
module.exports = {
data: new SlashCommandBuilder()
.setName("ping")
.setDescription("Replies with Pong!"),
async execute(interaction) {
await interaction.reply("Pong!");
const msg = await interaction.channel.send("Pong! 2!");
//Delete reply
setTimeout(async () => {
await interaction.deleteReply();
}, 1000);
//delete message
setTimeout(async () => {
await msg.delete();
}, 1500);
},
};

How to make fs.readFile async await?

I have this nodejs code here which read a folder and process the file. The code works. But it is still printing all the file name first, then only read the file. How do I get a file and then read a content of the file first and not getting all the files first?
async function readingDirectory(directory) {
try {
fileNames = await fs.readdir(directory);
fileNames.map(file => {
const absolutePath = path.resolve(folder, file);
log(absolutePath);
fs.readFile(absolutePath, (err, data) => {
log(data); // How to make it async await here?
});
});
} catch {
console.log('Directory Reading Error');
}
}
readingDirectory(folder);
To use await, you need to use promise versions of fs.readFile() and fs.readdir() which you can get on fs.promises and if you want these to run sequentially, then use a for loop instead of .map():
async function readingDirectory(directory) {
const fileNames = await fs.promises.readdir(directory);
for (let file of fileNames) {
const absolutePath = path.join(directory, file);
log(absolutePath);
const data = await fs.promises.readFile(absolutePath);
log(data);
}
}
readingDirectory(folder).then(() => {
log("all done");
}).catch(err => {
log(err);
});

Upload synthesized speech from firebase function node.js server's tmp directory

I am trying to upload the audio returned by Google's Text-to-Speech API in a Firebase Function and having trouble writing the audio file to the Node.js server's temp directory. I receive the following error in my functions log:
Write ERROR: { Error: ENOENT: no such file or directory, open '/tmp/synthesized/output.mp3' at Error (native) errno: -2, code: 'ENOENT', syscall: 'open', path: '/tmp/synthesized/output.mp3' }
Here's my imports:
// Cloud Storage
import * as Storage from '#google-cloud/storage';
const gcs = new Storage();
import { tmpdir } from 'os';
import { join, dirname } from 'path';
import * as fs from 'fs';
import * as fse from 'fs-extra';
// Cloud Text to Speech
import * as textToSpeech from '#google-cloud/text-to-speech';
const client = new textToSpeech.TextToSpeechClient();
...and the part of my function I'm having trouble with:
// Construct the text-to-speech request
const request = {
input: { text: text },
voice: { languageCode: 'en-US', ssmlGender: 'NEUTRAL' },
audioConfig: { audioEncoding: 'MP3' },
};
// Creat temp directory
const workingDir = join(tmpdir(), 'synthesized');
const tmpFilePath = join(workingDir, 'output.mp3');
// Ensure temp directory exists
await fse.ensureDir(workingDir);
// Performs the Text-to-Speech request
client.synthesizeSpeech(request)
.then(responses => {
const response = responses[0];
// Write the binary audio content to a local file in temp directory
fs.writeFile(tmpFilePath, response.audioContent, 'binary', writeErr => {
if (writeErr) {
console.error('Write ERROR:', writeErr);
return;
}
// Upload audio to Firebase Storage
gcs.bucket(fileBucket).upload(tmpFilePath, {
destination: join(bucketDir, pageName)
})
.then(() => { console.log('audio uploaded successfully') })
.catch((error) => { console.log(error) });
});
})
.catch(err => {
console.error('Synthesize ERROR:', err);
});
What is wrong with my temp directory creation or fs.writeFile() function?
(Answer edited in response to question edit...)
In your original question, you invoked
client.synthesizeSpeech(request, (err, response) => {...})
following Node's http callback pattern, in which the callback function may initiate before the response is complete. Your subsequent code calls methods that assume response content; if the response is still empty, fs.writeFile() writes nothing initially, and subsequent methods cannot find the non-existent file. (Because fs.writeFile() follows the same callback pattern, you might even discover that output.mp3 file after the program exits, because fs will stream the input. But I bet your Firebase methods aren't waiting.)
The solution is to use Promises or async/await. Looking at the Google TextToSpeechClient class docs, it looks like the synthesizeSpeech method supports this:
Returns: Promise -> Array. The first element of the array is an object representing SynthesizeSpeechResponse.
Example:
client.synthesizeSpeech(request)
.then(responses => {
var response = responses[0];
// doThingsWith(response)
})
.catch(err => {
console.error(err);
});
That should solve the problem with client.synthesizeSpeech, but unfortunately fs.writeFile is still synchronous. If you were using Node >10 you could use a native fsPromise.writeFile method, and if you were using Node >8 you could use util.promisify() to convert fs.writeFile to promises. But you've indicated in comments that you are using Node 6, so we'll have to do things manually. Thieving from this reference:
const writeFilePromise = (file, data, option) => {
return new Promise((resolve, reject) => {
fs.writeFile(file, data, option, error => {
if (error) reject(error);
resolve("File created! Time for the next step!");
});
});
};
client.synthesizeSpeech(request)
.then(responses => {
const response = responses[0];
return writeFilePromise(tmpFilePath, response.audioContent, 'binary');
})
.then(() => {
return gcs.bucket(fileBucket).upload(tmpFilePath, {
destination: join(bucketDir, pageName)
});
})
.then(() => {
console.log('audio uploaded successfully');
return null;
})
.catch((error) => { console.log(error) });
I've written all of this using .then constructs, but naturally, you could also use async/await if you would rather do that. I hope this fixes things--it will force your Firebase code to wait until fs.writeFile has completed its job. I have also, unfortunately, smooshed all of the error checking into one final .catch block. And made things a bit verbose for clarity. I'm sure you can do better.

Firebase + Google cloud storage: Handling files of 1MB+ without stalling

I've run into a curious problem:
Basically I'm making a Firebase Cloud function where when someone uploads a file then that file is sent via email or API to somewhere else.
Everything works for tiny files (100K-ish), if slow, but anything above 1MB (haven't tested the exact size) stalls. Doesn't give any errors in the Firebase log, the function just never completes.
Here's the relevant code:
const Storage = require('#google-cloud/storage')({
projectId: 'bilagskortet',
keyFilename: './service-account-key.json'
});
const returnBase64 = (fileFullPathAndName) => {
console.log("Fetching file...")
// Downloads the file
return Storage
.bucket(bucketName)
.file(fileFullPathAndName)
.download()
.then((data) => {
const file = {};
file.file = new Buffer(data[0]).toString('base64');
return file;
})
.catch((error) => {
console.error("Didn't get file:", error);
});
}
This is used together with two other Promises to get everything about the file needed for the email:
Promise
.all([
StorageFile.returnDownloadURL(attachementRef)
.then(url => {
console.log("AttachmenetLink is: ")
console.log(typeof url);
console.log(url);
email.attachementLink = url
})
.catch(error => {
console.error("Error, didn't get link: ")
console.error(error)
}),
StorageFile.returnMetaData(attachementRef)
.then(metadata => {
console.log("Content type:")
console.log(metadata.contentType);
file.contentType = metadata.contentType;
})
.catch(error => {
console.error("Didn't get the metadata")
console.error(error);
}),
StorageFile.returnBase64(attachementRef)
.then(data => {
console.log("File is: ")
console.log(typeof data);
console.log(data);
file.data = data.file;
})
.catch(error => {
console.error("Error, didn't get file: ")
console.error(error)
})
])
.then(allData => {
// Define and send email with attachement (cut for brevity)
}).catch(error =>
console.error(error)
)
As I've said, the code works well if the file is tiny. Times out and dies if the file is for example a 1.7MB image (.png)
Anyone know what might be going on?
Last thing that's logged is the "AttachmentLink" and "Content Type" ones, and last thing in the StorageFile.returnBase64 function is "Fetching file..."

No end event when piping inside "open"

I am piping a download into a file, but wanting to make sure the file doesn't already exist. I've put the code up here for an easier exploration: https://tonicdev.com/tolmasky/streaming-piping-on-open-tester <-- this will show you the outputs (code also below inline).
So the thing is, it seems to work fine except for the done (end) event. The file ends up on the hard drive fine, each step is followed correctly (the structure is to ensure no "parallel" steps happen that aren't necessary -- if I do got.stream(url).pipe(fs.createWriteStream({ flags: ... })), then the download will actually get kicked off even if the createWriteStream returns an error because the file is already there -- undesirable for the network).
The code is the following:
var fs = require("fs");
var got = require("got");
await download("https://www.apple.com", "./index.html");
function download(aURL, aDestinationFilePath)
{
return new Promise(function(resolve, reject)
{
fs.createWriteStream(aDestinationFilePath, { flags: "wx" })
.on("open", function()
{
const writeStream = this;
console.log("SUCCESSFULLY OPENED!");
got.stream(aURL)
.on("response", function(aResponse)
{
const contentLength = +aResponse.headers["content-length"] || 0;
console.log(aResponse.headers);
console.log("STARTING DOWNLOAD! " + contentLength);
this.on("data", () => console.log("certainly getting data"))
this.pipe(writeStream)
.on("error", reject)
.on("end", () => console.log("DONE!"))
.on("end", resolve);
})
})
.on("error", function(anError)
{
if (anError.code === "EEXIST") { console.log("oh");
resolve();}
else
reject(anError);
});
});
}
According to the stream docs, readable.pipe returns the destination Writable stream, and the correct event emitted when a Writable is done would be Event: 'finish'.

Resources