Dynamic command handler with shared and seperate commands - javascript

I am setting up a command handler for multiple channels (Twitch). At the moment I have all the commands divided in folders user specific and the generic ones. Accessing them by using a map(). I would like each user/channel to have access to only their folder and the generic one. The map key is name in a .js file.
So what would be the best way to do it? Ive tried mapping over the generic folder and the folder that matches the user name on login, but I am not aware of a way to change the "command" in client.commands.set(key, value) so it would be client.(nameChannel).set(key, value). Then I would be able to probably assign the default and user specific folder to the map.
Also fs.dirReadSync lists all of the .js files in a folder and sub folder. How do I access all of them at once in require? Wildcards don't seem to work so do I need to list them like shown below?
I want to be able to add more later and not hardcode them one-by-one if possible.
//hardcode example.
var moduleA = require( "./module-a.js" );
var moduleB = require( "../../module-b.js" );
var moduleC = require( "/my-library/module-c.js" );
The piece of code below is still a work in progress. What I like to achieve:
exclude channel specific commands from being called from other channels.
know if/what the standard or recommended approach is.
how to require() all .js from the readDir sync in one require.
client.commands = new Map();
//add commands property/method to client instance. A map to iterate over.
const commandFiles = fs.readdirSync("./commands").filter(file => file.endsWith(".js"));
//reads file system and filter .js files only. returns an array of those items.
for (const file of commandFiles) {
const command = require(`./commands/${file}`); //this only grabs the results in commands itself can't use wildcards..
//sets a new item in the collection.
//Key of map is command name.
client.commands.set(command.name, command);
}
//input validation etc here
//check command
try {
client.commands.get(commandFromMessage).execute(channel, commandFromMessage, argument);
} catch (error) {
console.error(error);
}
pastebin of the folder tree: https://pastebin.com/XNJt98Ha

You can set string names as your keys in regular objects.
const channelSpecificCommands = new Map();
const channelFolder= fs.readdirSync(`./commands/${channelSpecificDir}`);
for(const file in channelFolder) {
const commandFromFile = require(`./commands/{${channelSpecificDir}/${file}`)
channelSpecificCommands.set(commandFromFile.name, commandFromFile);
}
client[channelSpecificDir] = channelSpecificCommands;
For your second question - you should be using .json files instead of .js for this. You can load json files with JSON.parse(fs.readFileSync('myjsonfile.json', 'utf8')) to get the data without any issues that come with dynamic module resolution.

Related

Search Function for folder with files

I'm coding a bot with Discord.js, and was wondering how to create a function for a play command.
!play
faded
I want the code to search through a set folder, located at ./MusicFiles and find the filename closest to the given argument after the command !play. How would I do this and how would I give the full name of the file so it can be used by the bot?
You can use the npm packages levenary and fs. levenary is a package that will calculate the Levenshtein Distance between two strings. For example:
levenary('cat', ['cow', 'dog', 'pig']);
//=> 'cow'
You can use this in combination with fs, a package with can return an array of all files within a directory.
const fs = require('fs');
const levenary = require('levenary')
const files = fs.readdirSync('./MusicFiles') // get every file in this directory
const songFile = levenary(args[0], files) // get the file

Update (write to) an object in a separate JS file using Node

I'm fairly new to Node, and am wracking my brains on how to achieve the following:
I have a config file that looks something like this:
// various es imports
export default {
input: {
index: 'src/index.ts',
Button: 'src/Button/index.ts',
Spinner: 'src/Spinner/index.ts',
'icons/Notification': 'src/_shared/components/icons/Notification.tsx',
'icons/Heart': 'src/_shared/components/icons/Heart.tsx',
},
//.. other properties
}
From my node script, i need to somehow read this file and do the following:
Delete any entries in the input object that have a key starting
with icons/
Append new entries to the input object.
Write these changes back to the original config file.
Is there a recommended way to do this in Node, i've been looking at a couple of libs, like replace-in-file but none seem to be suited to this particular case.
Just faced the same concern, here is how I solved it :
1. Gets your file content
If it is not a .js file, then use fs.readFileSync (or fs.readFile) like so :
const fs = require('fs');
const path = require('path');
const myObjectAsString = fs.readFileSync(
path.join( process.cwd(), 'my-file.txt' ), // use path.join for cross-platform
'utf-8' // Otherwise you'll get buffer instead of a plain string
);
// process myObjectAsString to get it as something you can manipulate
Here I am using process.cwd(), in case of a CLI app, it will gives you the current working directory path.
If it is a .js file (eg. a JavaScript config file like webpack.config.js for instance), then simply use require native function, as you would do with regular internal file or NPM module, and you will get all module.export content :
const path = require('path');
const myObject = require( path.join( process.cwd(), 'myFile.js') );
2. Modify your object as you want
// ...
myObject.options.foo = 'An awesome option value';
3. Then rewrite it back to the file
You can simply use fs.writeFileSync to achieve that :
// ...
fs.writeFileSync( path.join(process.cwd(), 'my-file.txt', myObject );
If you want to write a plain JavaScript Object, then you can use util.inspect() native method and you may also use fs.appendFileSync :
// ...
// If we wants to adds the 'module.exports = ' part before
fs.writeFileSync( process.cwd() + '/file.js', 'module.exports = ');
// Writes the plain object to the file
fs.appendFileSync( process.cwd() + '/file.js', util.inspect(options));

Files in subdirectory aren't found in Node.js

I need somehow to loop over subdirectories but it returns me an error: ENOENT: no such file or directory, stat 'text3.txt'
Here are the files I use:
main.js
files
|_file1.txt
|_file2.txt
dir
|_text3.txt
Here is my main.js:
fs = require('fs'), aes = require('aes256'),
key = 'abc';
enc = file => {
return aes.encrypt(key,file)
}
decr = encr => {
return aes.decrypt(key,encr)
}
clf = dir => {
files = fs.readdirSync(dir);
// Filter files
for(let i of files){
stat = fs.statSync(i)
if(stat.isFile()){
newFiles.push(i)
}
else dirs.push(i)
}
// Encrypt folders
for(let file of newFiles){
fl = fs.readFileSync(file).toString();
fs.writeFileSync(file,enc(fl));
}
}
clf('./')
for(let c of dirs) clf(c);
Decrypt and ecnrypt func-s use aes256 encryption and return strings. Then clf function checks if files are not folders and pushes folders to an array. Then we encrypt files in main directory, but nothing happens in sub directories, it returns an error instead:
ENOENT: no such file or directory, stat 'text3.txt'
But text3.txt IS in dir directory!! Then why I have an error?
First off, declare every single variable you use. This is a recipe for disaster using undeclared variables. I would not even attempt to work on code like this without first declaring every single variable to the proper scope using let or const.
Second, when you do fs.statSync(i), the i here is just a plain filename with no path. If you do console.log(i), you will see it is only a filename. So, to reference the right file, you have to add the path back onto it from the your readdirSync(dir) and then pass that full path to fs.statSync().
You will find path.join() as a convenient way to combine a path with a filename.

Gulp plugin "gulp-filter" not restoring files in the stream as intended

I have a gulp task that does a pretty simple task, it searches for all files in a folder, filter html files, validate them and then restore file stream and push every file type in the destination folder. This gulpfile:
// define gulp
var gulp = require('gulp');
// define plug-ins
var filter = require('gulp-filter');
var w3cjs = require('gulp-w3cjs');
var newer = require('gulp-newer');
// define paths
var src_path = 'src';
var dest_path = 'public';
// Copy all files from /src, validate html files, and and push everything inside /public
gulp.task('files', function() {
return gulp.src(src_path + '/*') //search for all files
.pipe(newer(dest_path)) // if new go on, if old skip
.pipe(filter('*.html')) // filter html files
.pipe(w3cjs()) // validate filtered files
.pipe(filter('*.html').restore()) // restore files in pre-filter state
.pipe(gulp.dest(dest_path)) // push in destination folder
});
It seems that the "restore" is not restoring files, infact only html files are being pushed in production (/public) folder, what could be wrong? Thanks for any help.
maybe assign a variable to var filter = filter('*.html') and use it to restore .pipe(filter.restore()).

grunt-init: How can I copy or create an empty directory?

I want copy an empty directory from my root folder into the new initialized project. I want do this, to provide an initial directory structure for new projects.
I have such empty directories in the folder 'my-template/root/' but that will not be copied by:
// Files to copy (and process).
var files = init.filesToCopy(props);
// Actually copy (and process) files.
init.copyAndProcess(files, props);
I tried this:
// to copy also the empty directories
init.copy('myDir1/');
init.copy('myDir2/');
But than grunt-init crashes with:
Error: Unable to read "grunt-init-example/root/myDir1/" file (Error code: EISDIR).
What have I to do, to get an empty directory to the destination folder?
I use grunt#0.4.0 and grunt-init#0.2.0.
Inspired by the answer of Nick Mitchinson, I use this workaround:
// module dependencies
var join = require("path").join;
// empty directories will not be copied, so we need to create them manual
grunt.file.mkdir( join(init.destpath(), 'myDir1') );
grunt.file.mkdir( join(init.destpath(), 'myDir2') );
Update:
Alternative solution is to add .gitkeep files to the empty directories. Than the directories are no more empty and will be listed with filesToCopy().
For more details about .gitkeep look here: https://stackoverflow.com/a/7229996/496587
Try taking a look this this article
The part relevant to creating a directory is:
var root = path.normalize(__dirname+"/relative/path/you/want");
grunt.file.mkdir(root);
however, reading the whole this would probably be good.
I create an empty __empty_file.txt file inside the empty folders which I want to get copied.
Inside my template.js file I do the following:
Store files which matches with our (__empty_file.txt) by iterating over files object returned by init.filesToCopy(props) to an array.
Create directories using grunt.file.mkdir for each item in the above array and remove this file from files reference which was returned by init.filesToCopy. This has to be called before the call to init.copyAndProcess.

Categories