Use readline.question after getting a raw key - javascript

I'm trying to do the following two things sequentially in a Node.js app:
Get an arbitrary key as user input. (could be a character pressed, or any other key like arrow up/down...)
I'd like to ask the user for an arbitrary text input.
Each step works but if I chain them, the key entered first is always appended to the second input. Additionally the question is written to the console twice.
Here's the full code sample:
// sample.js
const readline = require("readline")
// Read a raw key from stdin as Promise
const getKey = () => new Promise((res) => {
process.stdin.setRawMode(true)
process.stdin.once('keypress', (str, key) => {
process.stdin.setRawMode(false)
res(key)
});
})
// Just promisify the "question" method
const getText = (interface, str) =>
new Promise(res => interface.question(str, res))
const main = async () => {
readline.emitKeypressEvents(process.stdin);
const interface = readline.createInterface({
input: process.stdin,
output: process.stdout
});
console.log("Press a key!")
const key = await getKey()
console.log(`\nYou pressed key "${key.name}"`)
const name = await getText(interface, "What's your name?\n")
console.log(`Your name is "${name}"`)
}
main()
And here's a sample CLI session screencast:
The expected behavior would be that those inputs don't interfere with eachother. Does anyone have a solution for this problem?
My assumtion was that the character from the first input is still unconsumed inside the stdin stream. But attempts to force to consume it were not successfull.

Related

JavaScript version of while True loop

I wrote this bit of code
while True:
text = input('type here > ')
print text
I've tried the below but it doesnt seem to be working
I'm having bit of a struggle trying to create a JavaScript version of it, since it seems js while loops constantly rerun but python seems to wait for input before rerunning. I am using the readline module to receive input from the console.
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
while (true) {
rl.question('type here > ', text => {
console.log(text)
})
}
Is there something im getting wrong? Im fairly new to programming
Any solutions?
If what you are trying to do is get input from the console in JavaScript you can use NodeJS process.stdin the implementation below :
process.stdin.resume();
process.stdin.setEncoding('utf-8');
let input = '';
let currentLine = 0;
process.stdin.on('data', inputStdin => {
input += inputStdin;
});
process.stdin.on('end', _ => {
input = input.trim()
.split('\n')
.map(str => str.trim());
// calling the function that works on the input
doSomething();
});
function readInput() {
return input[currentLine++];
}
Sample function to do something with what you get from the console
function doSomething() {
console.log(readInput())
}

How can I start a REPL that can access local variables in node.js?

Just like from IPython import embed; embed() but for node.
I want to open a REPL shell programmatically and be able to at least read the local variables. Being able to change them as well is a plus.
As far I know, the closest you can get is by using the repl built-in module (which is used by node inspect itself):
// ... your code you want to debug
const repl = require("repl");
const replServer = repl.start({
prompt: "Your Own Repl > ",
useGlobal: true
});
// Expose variables
const localVar = 42
replServer.context.localVar = localVar;
By running node index.js (assuming you saved the above content in index.js) we get access to this custom repl:
$ node index.js
Your Own Repl > localVar
42
Your Own Repl >
(To exit, press Ctrl+C again or Ctrl+D or type .exit)
Your Own Repl >
However, this does not work like a debugger tool, but really, it's only a REPL.
You can build a REPL similar to the built-in Deno REPL and and evaluate expressions using the dangerous eval function. Through it you'll be able to access local variables and other things (e.g. window).
repl.ts
import { readLines, writeAll } from "https://deno.land/std#0.106.0/io/mod.ts";
export default async function repl(evaluate: (x: string) => unknown) {
await writeOutput("exit using ctrl+d or close()\n");
await writeOutput("> ");
for await (const input of readInputs()) {
try {
const value = evaluate(input);
const output = `${Deno.inspect(value, { colors: !Deno.noColor })}\n`;
await writeOutput(output);
await writeOutput("> ");
} catch (error) {
await writeError(error);
}
}
}
async function* readInputs(): AsyncIterableIterator<string> {
yield* readLines(Deno.stdin);
}
async function writeOutput(output: string) {
await writeAll(Deno.stdout, new TextEncoder().encode(output));
}
async function writeError(error: unknown) {
await writeAll(Deno.stderr, new TextEncoder().encode(`Uncaught ${error}\n`));
}
repl_demo.ts
import repl from "./repl.ts";
let a = 1;
let b = 2;
let c = 3;
await repl((x) => eval(x));
example usage
% deno run repl_demo.ts
exit using ctrl+d or close()
> a
1
> a = 40
40
> a + b
42
For deno (Title says Node.js, tag deno) you can use Deno.run to execute deno and write to stdin and read from stdout.
The following will do:
const p = Deno.run({
cmd: ["deno"],
stdin: "piped",
stdout: "piped",
stderr: "piped"
});
async function read(waitForMessage) {
const reader = Deno.iter(p.stdout)
let res = '';
for await(const chunk of reader) {
res += new TextDecoder().decode(chunk);
console.log('Chunk', res, '---')
// improve this, you should wait until the last chunk
// is read in case of a command resulting in a big output
if(!waitForMessage)
return res;
else if(res.includes(waitForMessage))
return res;
}
}
async function writeCommand(command) {
const msg = new TextEncoder().encode(command + '\n');
console.log('Command: ', command)
const readPromise = read();
// write command
await p.stdin.write(msg);
// Wait for output
const value = await readPromise
return value;
}
// Wait for initial output:
// Deno 1.0.0
// exit using ctrl+d or close()
await read('ctrl+d or close()');
await writeCommand('let x = 5;')
let value = await writeCommand('x') // read x
console.log('Value: ', value)
await writeCommand('x = 6;')
value = await writeCommand('x') // read x
console.log('Value: ', value)
If you run that snippet, the output will be:
Command: let x = 5;
Command: x
Value: 5
Command: x = 6;
Command: x
Value: 6
There are some improvements to be made, such as handling stderr but you get the idea.
This feature doesn't currently exist but is being proposed in https://github.com/denoland/deno/issues/7938.
The vision being something along the lines of
Deno.eval("Deno.repl()")
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
//to be sure this context is here
const ev = eval.bind(this);
function ask() {
rl.question('>', (code) => {
console.log(ev(code));
ask();
});
}
ask();
this code ask a input with readLine module and every time a reponse is provided the code is executed and a new input is askef

How to create arguments from a message

I am trying to make a music bot for my discord server, I have it set to play the music, but I can't figure out how to make it play a link that the user inputs
client.on("message", message => {
if (message.content.startsWith("^play")) {
let channel = client.channels.get('496722898858278912');
const ytdl = require('ytdl-core');
const streamOptions = {
seek: 0,
volume: 1
};
const broadcast = client.createVoiceBroadcast();
channel.join()
.then(connection => {
const stream = ytdl(('https://www.youtube.com/watch?v=XAWgeLF9EVQ'), {filter: 'audioonly'});
broadcast.playStream(stream);
const dispatcher = connection.playBroadcast(broadcast);
});
}
});
The link in the code would be replaced with the user submitted link.
To create arguments, you can split the content of the message using ' ' as separator, the arguments are the elements of that array except for the first (that's the command):
// message.content: "^play https://youtube.com/blablabla other arguments"
let args = message.content.split(' '); // ["^play", "https://youtube.com/blablabla", "other", "arguments"]: the string got splitted into different parts
args.shift(); // remove the first element (the command)
// you can do all your stuff, when you need the link you find it in args[0]
const stream = ytdl((args[0]), { filter : 'audioonly' });
Please note that the first argument is not granted to be a valid youtube link, so check it or prepare your code to handle an invalid argument.

How to compare old and new value with Cloud Functions for Firebase with .onWrite or onchange?

Lets take the following data structure:
Now I want to refresh the accessTokenFacebook with a Firebase Function.
I tested two option:
the onWrite, and the:
the onChanged
The onWrite looks the best to me, but with the following function:
exports.getFacebookAccessTokenOnchange = functions.database.ref('/users/{uid}/userAccountInfo/lastLogin').onWrite(event => {
const lastLogin = event.data;
let dateObject = new Date();
let currentDate = dateObject.toUTCString();
return lastLogin.ref.parent.parent.child('services').child('facebook').update({'accessTokenFacebook': currentDate});
});
Something happens I don'understand/can solve: when I delete a whole userUID-record (for a cleanup), the userUID-record automatically create, then only with the following path {uid}/services/facebood/accesTokenFacebook...
It seems that a deletion also triggers a onWrite.
I also tried the .onchange, but that one only triggers when there is still no accessTokenFacebook. When the change make this one, the change never triggered again.
So the next thing I want to do is a comparison between the old and new value. Do you have an example? Or is there a better solution?
UPDATE:
Cloud Functions recently introduced changes to the API as noted here.
Now (>= v1.0.0)
exports.dbWrite = functions.database.ref('/path').onWrite((change, context) => {
const beforeData = change.before.val(); // data before the write
const afterData = change.after.val(); // data after the write
});
Before (<= v0.9.1)
exports.dbWrite = functions.database.ref('/path').onWrite((event) => {
const beforeData = event.data.previous.val(); // data before the write
const afterData = event.data.val(); // data after the write
});
Now that these functions are deprecated and this is the number one search result for this subject, here is the new updated version that Cloud Functions now use.
exports.yourFunction = functions.database.ref('/path/{randomPath}/goes/here').onWrite((change, context) => {
// Your value after the write
const newVal = change.after.val();
// Your value before the write
const oldVal = change.before.val();
});

How do I implement tab completion in node.js shell?

I was looking for this feature in node.js and I haven't found it.
Can I implement it myself? As far as I know, node.js doesn't load any file at it's startup (like Bash does with .bashrc) and I haven't noticed any way to somehow override shell prompt.
Is there a way to implement it without writing custom shell?
You could monkey-patch the REPL. Note that you must use the callback version of the completer, otherwise it won't work correctly:
var repl = require('repl').start()
var _completer = repl.completer.bind(repl)
repl.completer = function(line, cb) {
// ...
_completer(line, cb)
}
Just as a reference.
readline module has readline.createInterface(options) method that accepts an optional completer function that makes a tab completion.
function completer(line) {
var completions = '.help .error .exit .quit .q'.split(' ')
var hits = completions.filter(function(c) { return c.indexOf(line) == 0 })
// show all completions if none found
return [hits.length ? hits : completions, line]
}
and
function completer(linePartial, callback) {
callback(null, [['123'], linePartial]);
}
link to the api docs: http://nodejs.org/api/readline.html#readline_readline_createinterface_options
You can implement tab functionality using completer function like below.
const readline = require('readline');
/*
* This function returns an array of matched strings that starts with given
* line, if there is not matched string then it return all the options
*/
var autoComplete = function completer(line) {
const completions = 'var const readline console globalObject'.split(' ');
const hits = completions.filter((c) => c.startsWith(line));
// show all completions if none found
return [hits.length ? hits : completions, line];
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
completer: autoComplete
});
rl.setPrompt("Type some character and press Tab key for auto completion....\n");
rl.prompt();
rl.on('line', (data) => {
console.log(`Received: ${data}`);
});
Reference :
https://self-learning-java-tutorial.blogspot.com/2018/10/nodejs-readlinecreateinterfaceoptions_2.html

Categories

Resources