promisify, multi-argument interactive mode commands, readme, license
This commit is contained in:
parent
7633dda7a5
commit
037ccb62c4
19
LICENSE.txt
Normal file
19
LICENSE.txt
Normal file
@ -0,0 +1,19 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright © 2020 Evert "Diamond" Prants <evert@lunasqu.ee>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
software and associated documentation files (the “Software”), to deal in the Software
|
||||
without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
63
README.md
Normal file
63
README.md
Normal file
@ -0,0 +1,63 @@
|
||||
# Squeebot CLI
|
||||
This package provides the runtime and tooling for Squeebot 3.x.x!
|
||||
This package provides two binaries via npm: `squeebot` and `squeebotd`.
|
||||
|
||||
## Running Squeebot
|
||||
1. Create an environment: `$ squeebot new <name> [<path>]`
|
||||
2. Execute the environment in interactive mode using `squeebotd`: `$ squeebotd -i <path>/<name>.json`
|
||||
3. Install plugins (documented below)
|
||||
|
||||
`squeebotd` just takes the path to the generated json file and creates all other necessary files and directories by itself.
|
||||
The primary configuration will be located in `<path>/configs/squeebot.json`.
|
||||
|
||||
## Installing plugins
|
||||
In order to install plugins, you have to add a repository. Repositories are JSON files served over HTTP.
|
||||
|
||||
For example, installing core plugins (Interactive mode `-i` on `squeebotd`):
|
||||
1. `repository install https://(TODO)/repository.json`
|
||||
2. `plugin install control mqtt`
|
||||
3. `plugin list`
|
||||
|
||||
## Interactive mode commands
|
||||
The following commands are available when starting in interactive mode:
|
||||
* `repository help`
|
||||
* `plugin help`
|
||||
* `channel help`
|
||||
* `quit`
|
||||
|
||||
## Creating a repository
|
||||
In order to create a repository, you need to do the following:
|
||||
1. Create a new repository: `$ squeebot repository new <name> [<path>]`
|
||||
2. Build your new repository: `$ squeebot repository build <path>`
|
||||
3. Your built plugins, both archived and unarchived, are in `<path>/.out`.
|
||||
|
||||
Repositories are created with a TypeScript build environment by default. If you do not wish to use TypeScript (not recommended) for your plugins,
|
||||
add the `-t` flag to `squeebot repository new` command.
|
||||
|
||||
The build command supports a `-w` argument which will execute the entire build command again when you make changes (watch mode).
|
||||
**Including deployments, if `-d` is present!**
|
||||
|
||||
### Creating plugins
|
||||
Within your new repository, each directory you make will be a plugin. The plugin **must** contain the following:
|
||||
1. `plugin.json` - Manifest for your plugin.
|
||||
2. `plugin.js` - Has to be a JavaScript file which exports a class inherited from `Plugin` at `@squeebot/core/lib/plugin`.
|
||||
|
||||
**Note:** `plugin.js` only has to exist in the distribution, so `.ts` is fine, but you have to use `$ squeebot repository build <path>`
|
||||
to build them into JavaScript files.
|
||||
|
||||
Plugin manifest (`plugin.json`) example:
|
||||
```
|
||||
{
|
||||
"name": "plugin-name", // The name of your plugin, must match the name of the directory
|
||||
"version": "0.0.0", // The version of your plugin, must be semantic versioning!
|
||||
"description": "", // Optional description for this plugin
|
||||
"dependencies": [], // List of plugins this plugin depends on
|
||||
"npmDependencies": [], // List of npm modules this plugin depends on. Supports versions, example 'thing@1.0.0'
|
||||
}
|
||||
```
|
||||
|
||||
### Deploying the repository
|
||||
You can configure deployment options using a file called `deployment.json` in your repository root. Currently supported deployment methods:
|
||||
1. TODO!
|
||||
|
||||
In order to activate your configured deployment, use the `-d` flag when building your repository. `-o` flag skips build and deploys immediately.
|
252
src/cli.ts
252
src/cli.ts
@ -6,18 +6,17 @@ import { Squeebot } from './core';
|
||||
export class SqueebotCLI {
|
||||
constructor(private bot: Squeebot) {}
|
||||
|
||||
private checkUpdate(repo: IRepository): void {
|
||||
this.bot.repositoryManager.checkForUpdates(repo).then((updatable) => {
|
||||
if (updatable.length) {
|
||||
logger.log('[%s] The following plugins can be updated:', repo.name,
|
||||
updatable.map((u) => u.name).join(', '));
|
||||
} else {
|
||||
logger.log('[%s] All plugins are up-to-date!', repo.name);
|
||||
}
|
||||
}, (e) => logger.error(e.message));
|
||||
private async checkUpdate(repo: IRepository): Promise<void> {
|
||||
const updatable = await this.bot.repositoryManager.checkForUpdates(repo);
|
||||
if (updatable.length) {
|
||||
logger.log('[%s] The following plugins can be updated:', repo.name,
|
||||
updatable.map((u) => u.name).join(', '));
|
||||
} else {
|
||||
logger.log('[%s] All plugins are up-to-date!', repo.name);
|
||||
}
|
||||
}
|
||||
|
||||
private repositoryCommand(...args: any[]): void {
|
||||
private async repositoryCommand(...args: any[]): Promise<void> {
|
||||
const help = 'repository add <url> | update <name> | remove <name>';
|
||||
if (!args[0] || args[0] === 'help') {
|
||||
logger.log(help);
|
||||
@ -34,9 +33,10 @@ export class SqueebotCLI {
|
||||
return;
|
||||
}
|
||||
|
||||
this.bot.repositoryManager.installRepository(args[1]).then((repo) => {
|
||||
for (const urlp of args.slice(1)) {
|
||||
const repo = await this.bot.repositoryManager.installRepository(urlp);
|
||||
logger.log('Installed repository %s!', repo.name);
|
||||
}, (e) => logger.error(e.message));
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
case 'rem':
|
||||
@ -47,9 +47,10 @@ export class SqueebotCLI {
|
||||
return;
|
||||
}
|
||||
|
||||
this.bot.repositoryManager.uninstallRepository(args[1]).then(() => {
|
||||
logger.log('Installed repository %s.', args[1]);
|
||||
}, (e) => logger.error(e.message));
|
||||
for (const namep of args.slice(1)) {
|
||||
await this.bot.repositoryManager.uninstallRepository(namep);
|
||||
logger.log('Installed repository %s.', namep);
|
||||
};
|
||||
break;
|
||||
case 'u':
|
||||
case 'upd':
|
||||
@ -57,25 +58,27 @@ export class SqueebotCLI {
|
||||
if (!args[1]) {
|
||||
const repos = this.bot.repositoryManager.getAll();
|
||||
for (const repo of repos) {
|
||||
this.checkUpdate(repo);
|
||||
await this.checkUpdate(repo);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const repo = this.bot.repositoryManager.getRepoByName(args[1]);
|
||||
if (!repo) {
|
||||
logger.error('No such repository found.');
|
||||
return;
|
||||
}
|
||||
for (const namep of args.slice(1)) {
|
||||
const repo = this.bot.repositoryManager.getRepoByName(namep);
|
||||
if (!repo) {
|
||||
logger.error('No such repository "%s" found.', namep);
|
||||
return;
|
||||
}
|
||||
|
||||
this.checkUpdate(repo);
|
||||
await this.checkUpdate(repo);
|
||||
};
|
||||
break;
|
||||
default:
|
||||
logger.log(help);
|
||||
}
|
||||
}
|
||||
|
||||
private pluginCommand(...args: any[]): void {
|
||||
private async pluginCommand(...args: any[]): Promise<void> {
|
||||
const help = 'plugin install | update | uninstall | enable | disable | start | stop | list | running [<name>]';
|
||||
if (!args[0] || args[0] === 'help' || (!args[1] && args[0] !== 'list' && args[0] !== 'running')) {
|
||||
logger.log(help);
|
||||
@ -87,17 +90,18 @@ export class SqueebotCLI {
|
||||
case 'u':
|
||||
case 'install':
|
||||
case 'update':
|
||||
this.bot.repositoryManager.installPlugin(args[1]).then((mf) => {
|
||||
for (const name of args.slice(1)) {
|
||||
const mf = await this.bot.repositoryManager.installPlugin(name);
|
||||
logger.log('Installed plugin %s version %s!', mf.name, mf.version);
|
||||
}, (e) => console.error(e.message));
|
||||
|
||||
}
|
||||
break;
|
||||
case 'uninst':
|
||||
case 'remove':
|
||||
case 'uninstall':
|
||||
this.bot.repositoryManager.uninstallPlugin(args[1]).then(() => {
|
||||
logger.log('Uninstalled plugin %s.', args[1]);
|
||||
}, (e) => console.error(e.message));
|
||||
for (const name of args.slice(1)) {
|
||||
await this.bot.repositoryManager.uninstallPlugin(name)
|
||||
logger.log('Uninstalled plugin %s.', name);
|
||||
}
|
||||
break;
|
||||
case 'list':
|
||||
logger.log('Installed plugins:',
|
||||
@ -111,73 +115,79 @@ export class SqueebotCLI {
|
||||
case 'run':
|
||||
case 'load':
|
||||
case 'start':
|
||||
const plugin = this.bot.pluginManager.getAvailableByName(args[1]);
|
||||
if (!plugin) {
|
||||
logger.error('No such plugin is available. Maybe try installing it? plugin install', args[1]);
|
||||
return;
|
||||
for (const name of args.slice(1)) {
|
||||
const plugin = this.bot.pluginManager.getAvailableByName(name);
|
||||
if (!plugin) {
|
||||
logger.error('"%s" is not available. Maybe try installing it? plugin install', name, name);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.bot.pluginManager.load(plugin);
|
||||
logger.log('Started plugin "%s" successfully.', name);
|
||||
}
|
||||
|
||||
this.bot.pluginManager.load(plugin).then((p) => {
|
||||
logger.log('Started plugin "%s" successfully.', args[1]);
|
||||
}, (e) => logger.error(e.stack));
|
||||
|
||||
break;
|
||||
case 'stop':
|
||||
case 'kill':
|
||||
if (!this.bot.pluginManager.getAvailableByName(args[1])) {
|
||||
logger.error('No such plugin is available.');
|
||||
return;
|
||||
for (const name of args.slice(1)) {
|
||||
if (!this.bot.pluginManager.getAvailableByName(name)) {
|
||||
logger.error('No such plugin is available.');
|
||||
return;
|
||||
}
|
||||
|
||||
logger.log('Stopping plugin', name);
|
||||
|
||||
this.bot.stream.emitTo(name, 'pluginUnload', name);
|
||||
}
|
||||
|
||||
logger.log('Stopping plugin', args[1]);
|
||||
|
||||
this.bot.stream.emitTo(args[1], 'pluginUnload', args[1]);
|
||||
break;
|
||||
case 'enable':
|
||||
if (!this.bot.pluginManager.getAvailableByName(args[1])) {
|
||||
logger.error('No such plugin is available.');
|
||||
return;
|
||||
for (const name of args.slice(1)) {
|
||||
if (!this.bot.pluginManager.getAvailableByName(name)) {
|
||||
logger.error('No such plugin "%s" is available.', name);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.log('Enabling plugin', name);
|
||||
|
||||
if (!this.bot.config.config.enabled) {
|
||||
this.bot.config.config.enabled = [name];
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.bot.config.config.enabled.indexOf(name) === -1) {
|
||||
this.bot.config.config.enabled.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
logger.log('Enabling plugin', args[1]);
|
||||
|
||||
if (!this.bot.config.config.enabled) {
|
||||
this.bot.config.config.enabled = [args[1]];
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.bot.config.config.enabled.indexOf(args[1]) === -1) {
|
||||
this.bot.config.config.enabled.push(args[1]);
|
||||
}
|
||||
|
||||
this.bot.config.save();
|
||||
await this.bot.config.save();
|
||||
break;
|
||||
case 'disable':
|
||||
if (!this.bot.pluginManager.getAvailableByName(args[1])) {
|
||||
logger.error('No such plugin is available.');
|
||||
return;
|
||||
for (const name of args.slice(1)) {
|
||||
if (!this.bot.pluginManager.getAvailableByName(name)) {
|
||||
logger.error('No such plugin "%s" is available.', name);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.log('Disabling plugin', name);
|
||||
|
||||
if (!this.bot.config.config.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const indx = this.bot.config.config.enabled.indexOf(name);
|
||||
if (indx > -1) {
|
||||
this.bot.config.config.enabled.splice(indx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
logger.log('Disabling plugin', args[1]);
|
||||
|
||||
if (!this.bot.config.config.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const indx = this.bot.config.config.enabled.indexOf(args[1]);
|
||||
if (indx > -1) {
|
||||
this.bot.config.config.enabled.splice(indx, 1);
|
||||
}
|
||||
|
||||
this.bot.config.save();
|
||||
await this.bot.config.save();
|
||||
break;
|
||||
default:
|
||||
logger.log(help);
|
||||
}
|
||||
}
|
||||
|
||||
private channelCommand(...args: any[]): void {
|
||||
const help = 'channel new | del | addplugin | delplugin [<name>] [<plugin>]';
|
||||
private async channelCommand(...args: any[]): Promise<void> {
|
||||
const help = 'channel new | del | list | addplugin | delplugin [<name>] [<plugin>]';
|
||||
if (!args[0] || args[0] === 'help' || (!args[1] && args[0] !== 'list')) {
|
||||
logger.log(help);
|
||||
return;
|
||||
@ -201,15 +211,17 @@ export class SqueebotCLI {
|
||||
case 'del':
|
||||
case 'delete':
|
||||
case 'remove':
|
||||
const chan = this.bot.channelManager.getChannelByName(args[1]);
|
||||
if (!chan) {
|
||||
logger.error('No such channel exists!');
|
||||
return;
|
||||
for (const name of args.slice(1)) {
|
||||
const chan = this.bot.channelManager.getChannelByName(name);
|
||||
if (!chan) {
|
||||
logger.error('No such channel "%s" exists!', name);
|
||||
return;
|
||||
}
|
||||
|
||||
this.bot.channelManager.removeChannel(chan);
|
||||
|
||||
logger.log('Channel "%s" removed!', name);
|
||||
}
|
||||
|
||||
this.bot.channelManager.removeChannel(chan);
|
||||
|
||||
logger.log('Channel removed!');
|
||||
break;
|
||||
case 'addp':
|
||||
case 'addplugin':
|
||||
@ -224,11 +236,18 @@ export class SqueebotCLI {
|
||||
return;
|
||||
}
|
||||
|
||||
if (chan1.plugins.indexOf(args[2]) !== -1) {
|
||||
chan1.plugins.push(args[2]);
|
||||
}
|
||||
for (const name of args.slice(2)) {
|
||||
if (chan1.plugins.indexOf(name) === -1) {
|
||||
chan1.plugins.push(name);
|
||||
}
|
||||
|
||||
logger.log('Plugin added to channel!');
|
||||
logger.log('Plugin "%s" added to channel!', name);
|
||||
}
|
||||
break;
|
||||
case 'list':
|
||||
logger.log('Channels:\n', this.bot.channelManager.getAll().map((chan) => {
|
||||
return ` => ${chan.name}: ${chan.plugins.join(', ')} (${chan.enabled ? 'enabled' : 'disabled'})`;
|
||||
}).join('\n'));
|
||||
break;
|
||||
case 'remp':
|
||||
case 'delp':
|
||||
@ -245,37 +264,43 @@ export class SqueebotCLI {
|
||||
return;
|
||||
}
|
||||
|
||||
const idx = chan2.plugins.indexOf(args[2]);
|
||||
if (idx !== -1) {
|
||||
chan2.plugins.splice(idx, 1);
|
||||
for (const name of args.slice(2)) {
|
||||
const idx = chan2.plugins.indexOf(args[2]);
|
||||
if (idx !== -1) {
|
||||
chan2.plugins.splice(idx, 1);
|
||||
}
|
||||
logger.log('Plugin "%s" added to channel!', name);
|
||||
}
|
||||
|
||||
logger.log('Plugin added to channel!');
|
||||
break;
|
||||
case 'enable':
|
||||
const chan3 = this.bot.channelManager.getChannelByName(args[1]);
|
||||
if (!chan3) {
|
||||
logger.error('No such channel exists!');
|
||||
return;
|
||||
}
|
||||
for (const name of args.slice(1)) {
|
||||
const chan = this.bot.channelManager.getChannelByName(name);
|
||||
if (!chan) {
|
||||
logger.error('No such channel "%s" exists!', name);
|
||||
return;
|
||||
}
|
||||
|
||||
chan3.enabled = true;
|
||||
logger.log('Channel enabled!');
|
||||
chan.enabled = true;
|
||||
logger.log('Channel "%s" enabled!', name);
|
||||
}
|
||||
break;
|
||||
case 'disable':
|
||||
const chan4 = this.bot.channelManager.getChannelByName(args[1]);
|
||||
if (!chan4) {
|
||||
logger.error('No such channel exists!');
|
||||
return;
|
||||
}
|
||||
for (const name of args.slice(1)) {
|
||||
const chan = this.bot.channelManager.getChannelByName(name);
|
||||
if (!chan) {
|
||||
logger.error('No such channel "%s" exists!', name);
|
||||
return;
|
||||
}
|
||||
|
||||
chan4.enabled = false;
|
||||
logger.log('Channel disabled!');
|
||||
chan.enabled = false;
|
||||
logger.log('Channel "%s" disabled!', name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this.bot.config.config.channels = this.bot.channelManager.getAll();
|
||||
this.bot.config.save();
|
||||
await this.bot.config.save();
|
||||
}
|
||||
|
||||
public attach(rl: ReadLine) {
|
||||
@ -290,17 +315,20 @@ export class SqueebotCLI {
|
||||
case 'r':
|
||||
case 'repo':
|
||||
case 'repository':
|
||||
this.repositoryCommand(...split.slice(1));
|
||||
this.repositoryCommand(...split.slice(1)).catch(
|
||||
e => logger.error(e.message));
|
||||
break;
|
||||
case 'p':
|
||||
case 'pl':
|
||||
case 'plugin':
|
||||
this.pluginCommand(...split.slice(1));
|
||||
this.pluginCommand(...split.slice(1)).catch(
|
||||
e => logger.error(e.message));
|
||||
break;
|
||||
case 'c':
|
||||
case 'chan':
|
||||
case 'channel':
|
||||
this.channelCommand(...split.slice(1));
|
||||
this.channelCommand(...split.slice(1)).catch(
|
||||
e => logger.error(e.message));
|
||||
break;
|
||||
case 'stop':
|
||||
case 'exit':
|
||||
@ -309,6 +337,6 @@ export class SqueebotCLI {
|
||||
this.bot.shutdown();
|
||||
break;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ const tsConfig = {
|
||||
};
|
||||
|
||||
const gitignore = `/node_modules/
|
||||
/.out/
|
||||
deployment.json`;
|
||||
|
||||
function dummyEnvironment(location: string): IEnvironment {
|
||||
@ -108,6 +109,7 @@ async function newRepository(
|
||||
await fs.writeJson(path.join(location, 'tsconfig.json'), tsConfig);
|
||||
gitIgnore += '\n*.js';
|
||||
gitIgnore += '\n*.d.ts';
|
||||
gitIgnore += '\n*.tsbuildinfo';
|
||||
|
||||
console.log('Adding TypeScript scripts to package.json');
|
||||
const pkgjson = path.join(location, 'package.json');
|
||||
|
Loading…
Reference in New Issue
Block a user