I have a command file for a discord bot that contains the command and a piece of parsing logic contained within a function that I want to reuse within my index.js
// file: ./commands/scrumPrompt.js
// The function
const extractDeets = function (f, scrum) {
let items = [];
let re = new RegExp("(\n[ -]*" + f + ".*)", "g");
let replace = new RegExp("[ -]*" + f + "[ ]+");
for (const item of scrum.matchAll(re)) {
items.push(item[1].trim().replace(replace, ""));
}
return items;
};
// The actual command itself within the same file
module.exports = {
name: "scrum",
usage: `!scrum < followed by your message > as per Standup format - refer !show for showing the format`,
description: "Reply to standup prompt",
async execute(message, args) {
if (message.channel.type === "text") {
if (!args.length)
return message.reply(
"Please Provide your scrum as per the format in help menu !scrum < your message >"
);
else {
if (message.author.id !== -1) {
const client = new MongoClient(MONGO_URI);
try {
const database = client.db(DB_NAME);
const members = database.collection("members");
const query = { user_id: message.author.id };
const membersdetail = await members.findOne(query);
if (membersdetail !== null) {
// since this method returns the matched document, not a cursor, print it directly
//console.log("Adding Scrum for ", membersdetail.email);
let userscrum = args.splice(0).join(" ");
// Check if multiple !scrum commands are present in developer scrum message
if (userscrum.includes("!scrum") == false) {
// Expects notations of "-" to exist
let [f, e, b, o, bl] = ["f", "e", "b", "o", "x"];
let features = extractDeets(f, userscrum);
let enhancements = extractDeets(e, userscrum);
let bugs = extractDeets(b, userscrum);
let others = extractDeets(o, userscrum);
let blockers = extractDeets(bl, userscrum);
.
.
.
};
I want to keep the name of the function as extractDeets() itself so that it doesn't mess with the usage within the command as well. I'm not completely sure how to export it into the index.js because it's already kind of being imported here:
// Imports the command file + adds the command to the bot commands collection
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
bot.commands.set(command.name, command);
}
I'm unsure of how to add the function as another import, maybe I should export it into another file and then import it from there? I'm not sure if that's possible or doable here. I've tried directly importing from here but then the command doesn't work, which is troublesome.
You can do it like this:
module.exports = { extractDeets };
Later, you can import it like this:
const { extractDeets } = require('../your_file');
Related
I'm writing an extension in Visual Studio Code that should generate assembler code from the current open code "C" or "C++". And after hovering over a line of code in "C" or "C++", the section of code generated from this line should be highlighted. Kind of like a comparison.
extensions.js
const { exec } = require("child_process");
const vscode = require("vscode");
const path = require('path');
function activate(context) {
let decorationType = vscode.window.createTextEditorDecorationType({
backgroundColor: 'rgba(200, 200, 200, 0.5)'
});
let mapping = {};
// Register the hover provider
vscode.languages.registerHoverProvider(['c', 'cpp'], {
provideHover(document, position) {
let lineNum = position.line;
let assemblerEditor = vscode.window.visibleTextEditors.find(e => e.document.languageId === 'assembler');
if (assemblerEditor) {
let assemblerLineNum = mapping[lineNum];
if (assemblerLineNum !== undefined) {
assemblerEditor.setDecorations(decorationType, [
new vscode.Range(assemblerLineNum, 0, assemblerLineNum, Number.MAX_SAFE_INTEGER)
]);
}
}
}
});
// Register the command that generates the assembly code and opens it
let disposable = vscode.commands.registerCommand(
"extension.showAssembly",
function () {
let editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
// Choice compiler
let config = vscode.workspace.getConfiguration();
let compiler = config.get("cvass.compiler.choice");
let currentFilePath = editor.document.uri.fsPath;
let dirPath = path.dirname(currentFilePath);
let oFile = path.join(dirPath, 'output.s');
let iFile = currentFilePath;
exec(`${compiler} -S -o "${oFile}" "${iFile}"`, (error, stdout, stderr) => {
if (error) {
vscode.window.showErrorMessage(error);
return;
}
// populate the mapping object
let output = stdout + stderr;
let lines = output.split('\n');
lines.forEach((line, index) => {
if (line.startsWith(currentFilePath)) {
let cLineNum = parseInt(line.match(/:(\d+)/)[1]);
mapping[cLineNum] = index;
}
});
vscode.workspace.openTextDocument(oFile).then(doc => {
vscode.window.showTextDocument(doc, vscode.ViewColumn.Beside, true);
});
});
});
context.subscriptions.push(disposable);
}
exports.activate = activate;
function deactivate() { }
module.exports = {
activate,
deactivate
};
In the code above, my attempt at implementation.
For generation I use "g++" and "clang++"
Expected: that the generated source code file will open in a new tab to the right of the "C", "C++" source code. And when you hover over a line of code in "C", "C++" in the assembler code, the section of code created from this line will be highlighted.
The very generation of the assembler and opening it on the right in a new tab is ready.
I really hope for your help, it is not possible to write this functionality at all.
I'm trying to replicate the code in this article:
https://depth-first.com/articles/2020/08/24/smiles-validation-in-the-browser/
What I'm trying to do different is that I'm using a textarea instead of input to take multi-line input. In addition to displaying an error message, I also want to display the entry which doesn't pass the validation.
The original validation script is this:
const path = '/target/wasm32-unknown-unknown/release/smival.wasm';
const read_smiles = instance => {
return smiles => {
const encoder = new TextEncoder();
const encoded = encoder.encode(`${smiles}\0`);
const length = encoded.length;
const pString = instance.exports.alloc(length);
const view = new Uint8Array(
instance.exports.memory.buffer, pString, length
);
view.set(encoded);
return instance.exports.read_smiles(pString);
};
};
const watch = instance => {
const read = read_smiles(instance);
document.querySelector('input').addEventListener('input', e => {
const { target } = e;
if (read(target.value) === 0) {
target.classList.remove('invalid');
} else {
target.classList.add('invalid');
}
});
}
(async () => {
const response = await fetch(path);
const bytes = await response.arrayBuffer();
const wasm = await WebAssembly.instantiate(bytes, { });
watch(wasm.instance);
})();
For working with a textarea, I've changed the watch function to this and added a <p id="indicator"> element to the html to display an error:
const watch = instance => {
const read = read_smiles(instance);
document.querySelector("textarea").addEventListener('input', e => {
const { target } = e;
var lines_array = target.value.split('/n');
var p = document.getElementById("indicator");
p.style.display = "block";
p.innerHTML = "The size of the input is : " + lines_array.length;
if (read(target.value) === 0) {
target.classList.remove('invalid');
} else {
target.classList.add('invalid');
}
});
}
I'm not even able to get a count of entries that fail the validation. I believe this is async js and I'm just a beginner in JavaScript so it's hard to follow what is happening here, especially the part where the function e is referencing itself.
document.querySelector("textarea").addEventListener('input', e => {
const { target } = e;
Can someone please help me in understanding this complicated code and figuring out how to get a count of entries that fail the validation and also printing the string/index of the same for helping the user?
There is a mistake in you code to count entries in the textarea:
var lines_array = target.value.split('\n'); // replace /n with \n
You are asking about the function e is referencing itself:
The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables. You can find more informations Mdn web docs - Destructuring object
I have a test folder with files
file
file (1)
file (2)
If the file exists I add a suffix to a new filename, to prevent overwriting the file. For example
if file exists new name should be file (1)
if file (1) exists new name should be file (2)
if file (2) exists new name should be file (3)
and so on.
The following function works fine, except the value is not returned so I can assign it later.
async function dest_exists_new_name(file) {
const access = fs.promises.access
try {
await access(file, fs.F_OK)
// file exists - generate new name
const info = path.parse(file)
const dir = info.dir
let name = info.name
const ext = info.ext
// generate suffix
let suffix = ' (1)'
const suffix_regex = / \([0-9]+\)$/
if (suffix_regex.test(name)) { // if suffix exists -> increment it
const num = name.split(' ').reverse()[0].replace(/[()]/g,'')
const next_num = parseInt(num) + 1
suffix = ' (' + next_num + ')'
name = name.replace(suffix_regex, '') // remove old suffix
}
// generate suffix end
const new_name = path.join(dir, name + suffix + ext)
// recurse until dest not exists
await dest_exists_new_name(new_name)
} catch {
// file not exist - return its name
// console.log works OK
console.log('new name ->', file)
// return doesn't work - returns undefined if the previous name exists, but works ok if the name doesn't exists
return file
}
}
await dest_exists_new_name('/path/file')
new name -> /path/file (3) // console.log - works OK
undefined // returns undefined, if file previously exists
The question is how can I correctly return the new file name value?
If there are any culprits in such a solution like
accidental file rewriting
infinite recursion
other issues
I will be grateful for the hints on how to improve the function.
Your function will return file, but being an async function, you need to await its return and you cannot do so outside of an async scope. Thus, if you just console.log() its "istantaneous" value, it will indeed return a pending promise, as the return value has not been resolved yet. You may retrieve the correct return value by including your function in an async scope, like this:
let a = async () => {
console.log(await dest_exists_new_name('/path/file'))
}
a();
This will output:
new name -> /path/file
/path/file //here's your file
Now, by adding return await dest_exists_new_name(new_name) you should be able to achive what you want and both console.log() and return the new, non-existent, file name. Here's a complete, reproducible example:
const fs = require('fs');
const path = require('path');
async function dest_exists_new_name(file) {
const access = fs.promises.access
try {
await access(file, fs.F_OK)
const info = path.parse(file)
const dir = info.dir
let name = info.name
const ext = info.ext
let suffix = ' (1)'
const suffix_regex = / \([0-9]+\)$/
if (suffix_regex.test(name)) {
const num = name.split(' ').reverse()[0].replace(/[()]/g, '')
const next_num = parseInt(num) + 1
suffix = ' (' + next_num + ')'
name = name.replace(suffix_regex, '')
}
const new_name = path.join(dir, name + suffix + ext)
return await dest_exists_new_name(new_name)
} catch {
console.log('new name ->', file)
return file
}
}
//Here, make sure that the path to "file" is correct
let a = async() => console.log(await dest_exists_new_name(path.join(__dirname, './file')));
a();
Output, having up to file (2) in the same folder:
new name -> /path/to/file (3)
/path/to/file (3)
Check you try catch and how you are receiving your variable.
async function dest_exists_new_name(file) {
try {
const res = await dest_exists_new_name(file1); // recursion result
} catch (err) {
return Promise.resolve("file not found");
}
}
// usage
let res = await dest_exists_new_name(fileArg);
First of all, you should use await, since it's async function:
// recurse until dest not exists
await dest_exists_new_name(new_name)
About recursion - IMHO, it's always better to use cycle (if it doesn't make code too complicated).
Mixing async-await & promises is not very good. Ideally you should use one style.
I prefer to use destructuring, lambda functions, and other modern features.
So, my variant for async-await, without recursion:
const fs = require('fs');
const path = require('path');
// better to create outside of func, since it doesn't depend on context
const suffix_regex = / \([0-9]+\)$/
const defaultSuffix = ' (1)'
const access = async (...params) => new Promise((resolve) => fs.access(...params, (err) => (err) ? resolve(false) : resolve(true)))
const generate_new_suffix = ({ dir, name, ext }) => {
if (suffix_regex.test(name)) { // if suffix exists -> increment it
const num = name.split(' ').reverse()[0].replace(/[()]/g, '')
const suffix = `(${+num + 1})`;
const cleanName = name.replace(suffix_regex, '') // remove old suffix
return path.join(dir, cleanName + suffix + ext)
}
return path.join(dir, name + defaultSuffix + ext)
}
const dest_exists_new_name = async (file) => {
let newNameGenerated = false
let newFileName = file
while (await access(newFileName, fs.F_OK)) {
console.log(newFileName);
const info = path.parse(newFileName)
newFileName = generate_new_suffix(info)
};
console.log('new name ->', newFileName)
return newFileName
}
(async () => {
console.log(await dest_exists_new_name(path.join(__dirname, 'file')))
})();
The title explains my problem. I am trying to get a string that has quotation marks around it so I can use Node.js to pass into a weather module. Here's my code so far (I have not set the var CityToSearch yet in this code which is what I need help with)
And also yes I'm using Discord.js to send messages.
const Discord = require('discord.js')
const bot = new Discord.Client()
const PREFIX = '/';
const embed = new Discord.MessageEmbed()
const ping = require('minecraft-server-util')
const weather = require('weather-js')
bot.on('message', message => {
if (message.channel.type === 'dm') {return}
let args = message.content.substring(PREFIX.length).split(' ')
if(message.content.startsWith(PREFIX))
switch (args[0]) {
case 'weather':
if (args.includes('"')){
var CityToSearch =
}
weather.find({search: `city, ${CityToSearch}`, degreeType: 'F'}, function(err, result) {
if(err) console.log(err);
var currentw = new Discord.MessageEmbed()
.setColor(0x00ffff)
.setTitle(`Current Weather in ${args[1]} in state ${args[2]}`)
.addField('Temperature', result[0].current.temperature)
.addField('Sky Text', result[0].current.skytext)
.addField('Humidity', result[0].current.humidity)
.addField('Wind Speed & Direction', result[0].current.winddisplay)
.addField('Feels Like', result[0].current.feelslike)
.addField('Location', result[0].current.observationpoint)
.addField('Time', result[0].current.observationtime)
.addField('Date', result[0].current.date)
message.channel.send(currentw)
});
You can split the actual string with ". So that the string will be split and the string at index 1 will be the city you are looking for.
const str = '/weather "San Fransico" California';
console.log(str.split('"'));
console.log(str.split('"')[1]);
I would NOT split the arguments on spaces initially. You can use the Regular Expression below with your arguments to yank out the command, and then parse the inputs as needed:
const args = message.content.substring(PREFIX.length).match(/\/([^\s]+) (.+)/)
if (args) { // valid input?
const command = args[1]
const input = args[2]
switch (command) {
case 'weather':
const cityMatch = input.match(/"([^"]+)"/)
const CityToSearch = (cityMatch) ? cityMatch[1] : input.split(/\s/)[0]
weather.find({search: `city, ${CityToSearch}` ...)
// other commands...
}
}
with my discord bot I am working on a help command.
My command list file which the help command accesses is:
{
"Help": {
"name":"Help",
"group":"User",
"desc":"Displays a list of commands",
"usage":"help [group OR command]"
},
"Purge": {
"name":"Purge",
"group":"Admin",
"desc":"Deletes a specified number of messages",
"usage":"Purge <amount>"
}
}
These just define group, name, and usage of the commands. The code for the help command so far is:
const Discord = require('discord.js');
const bot = new Discord.Client();
const client = new Discord.Client();
const weather = require('weather-js');
const fs = require('fs');
const commands = JSON.parse(fs.readFileSync('Storage/commands.json', 'utf8'))
const token = "<my token>"
const prefix = 'cb!';
bot.on('message', message => {
// Variables
let msg = message.content.toUpperCase();
let sender = message.author;
let cont = message.content.slice(prefix.length).split(" ");
let args = cont.shift().toLowerCase();
if (message.content.startsWith(prefix+'help')) {
console.log('ok i hate this')
const embed = new Discord.RichEmbed()
.setColor(0x1D82B6)
let commandsFound = 0;
for (var cmd in commands) {
if (commands[cmd].group.toUpperCase() === 'USER') {
commandsFound++
embed.addField(`${commands[cmd].name}`, `**Description:** ${commands[cmd].desc}\n**Usage:** ${prefix + commands[cmd].usage}`);
}
}
embed.setFooter(`Currently showing user commands. To view another group do ${prefix}help [group / command]`)
embed.setDescription(`**${commandsFound} commands found** - <> means required, [] means optional`)
message.author.send({embed})
message.channel.send({embed: {
color: 0x1D82B6,
description: `**Check your DMs ${message.author}!**`
}})
} else {
// Variables
let groupFound = '';
for (var cmd in commands) {
if (args.join(" ").trim().toUpperCase() === commands[cmd].group.toUpperCase()) {
groupFound = commands[cmd].group.toUpperCase();
break;
}
}
if (groupFound != '') {
for (var cmd in commands) {
const embed = new Discord.RichEmbed()
.setColor(0x1D82B6)
let commandsFound = 0;
if (commands[cmd].group.toUpperCase() === groupFound) {
commandsFound++
embed.addField(`${commands[cmd].name}`, `**Description:** ${commands[cmd].desc}\n**Usage:** ${prefix + commands[cmd].usage}`);
}
}
embed.setFooter(`Currently showing ${groupFound} commands. To view another group do ${prefix}help [group / command]`)
embed.setDescription(`**${commandsFound} commands found** - <> means required, [] means optional`)
message.author.send({embed})
message.channel.send({embed: {
color: 0x1D82B6,
description: `**Check your DMs ${message.author}!**`
}})
}
}
});
If I were to type "cb!help admin" I would get this error in the console
if (args.join(" ").trim().toUpperCase() === commands[cmd].group.toUpperCase()) {
^
TypeError: args.join is not a function
What might I do to fix this? I've also tried if (args[0].join... but that doesn't work.
As always, thanks for taking the time to read this. I'm basing this off of out dated code so there are maybe some other errors. I'm trying too fix them all.
args has one definition in your code sample:
let args = cont.shift().toLowerCase();
This makes args a string - string do not have the method join() as join() is part of the Array prototype chain, which strings do not inherit from. shift() will return the first element of the cont array, so you may just want to call args.toUpperCase(), although I would recommend renaming your variables to make the meaning of what args is clearer.
You should try to swap your two variables at the top so that
let args = message.content.slice(prefix.length).split(' ');
let cont = args.shift().toLowerCase();
Then just put args in your if statement
if (args[0].toUpperCase === commands[cmd].group.toUpperCase) { /* Your code... */ }
Your args are already stored in an array so there is no need to trim them and the join function is also therefore not needed.
Hope this helps!
your missing client, insert client, message, args