From 7633dda7a57a4e75bab37cc6f3cbfa2235323204 Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Sat, 28 Nov 2020 21:08:56 +0200 Subject: [PATCH] Interactive mode, cli changes --- package-lock.json | 146 ++++++++++++++++++++- package.json | 2 + src/cli.ts | 314 ++++++++++++++++++++++++++++++++++++++++++++++ src/core.ts | 22 ++-- src/squeebot.ts | 26 ++-- src/squeebotd.ts | 14 ++- 6 files changed, 504 insertions(+), 20 deletions(-) create mode 100644 src/cli.ts diff --git a/package-lock.json b/package-lock.json index 89e5b73..d912154 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,8 +33,11 @@ "@squeebot/core": { "version": "file:../core", "requires": { + "@types/semver": "^7.3.4", "dateformat": "^4.0.0", - "fs-extra": "^9.0.1" + "fs-extra": "^9.0.1", + "semver": "^7.3.2", + "tar": "^6.0.5" }, "dependencies": { "@babel/code-frame": { @@ -73,6 +76,11 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.9.tgz", "integrity": "sha512-JsoLXFppG62tWTklIoO4knA+oDTYsmqWxHRvd4lpmfQRNhX6osheUOWETP2jMoV/2bEHuMra8Pp3Dmo/stBFcw==" }, + "@types/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ==" + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -123,6 +131,11 @@ "supports-color": "^5.3.0" } }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -177,6 +190,14 @@ "universalify": "^1.0.0" } }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -283,6 +304,23 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, "mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -319,9 +357,9 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" }, "sprintf-js": { "version": "1.0.3", @@ -336,6 +374,26 @@ "has-flag": "^3.0.0" } }, + "tar": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz", + "integrity": "sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + } + } + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -356,7 +414,6 @@ "minimatch": "^3.0.4", "mkdirp": "^0.5.3", "resolve": "^1.3.2", - "semver": "^5.3.0", "tslib": "^1.13.0", "tsutils": "^2.29.0" } @@ -383,6 +440,11 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -395,12 +457,31 @@ "@types/node": "*" } }, + "@types/minipass": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/minipass/-/minipass-2.2.0.tgz", + "integrity": "sha512-wuzZksN4w4kyfoOv/dlpov4NOunwutLA/q7uc00xU02ZyUY+aoM5PWIXEKBMnm0NHd4a+N71BMjq+x7+2Af1fg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/node": { "version": "14.14.8", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.8.tgz", "integrity": "sha512-z/5Yd59dCKI5kbxauAJgw6dLPzW+TNOItNE00PkpzNwUIEwdj/Lsqwq94H5DdYBX7C13aRA0CY32BK76+neEUA==", "dev": true }, + "@types/tar": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/tar/-/tar-4.0.4.tgz", + "integrity": "sha512-0Xv+xcmkTsOZdIF4yCnd7RkOOyfyqPaqJ7RZFKnwdxfDbkN3eAAE9sHl8zJFqBz4VhxolW9EErbjR1oyH7jK2A==", + "dev": true, + "requires": { + "@types/minipass": "*", + "@types/node": "*" + } + }, "@types/yargs": { "version": "15.0.10", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.10.tgz", @@ -477,6 +558,11 @@ "supports-color": "^5.3.0" } }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -553,6 +639,14 @@ "universalify": "^1.0.0" } }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -681,6 +775,23 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, "mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -770,6 +881,26 @@ "has-flag": "^3.0.0" } }, + "tar": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz", + "integrity": "sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + } + } + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -861,6 +992,11 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==" }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "yargs": { "version": "16.1.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.1.1.tgz", diff --git a/package.json b/package.json index 22e3e5b..220bf66 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "devDependencies": { "@types/fs-extra": "^9.0.4", "@types/node": "^14.14.8", + "@types/tar": "^4.0.4", "@types/yargs": "^15.0.10", "tslint": "^6.1.3", "typescript": "^4.0.5" @@ -30,6 +31,7 @@ "@squeebot/core": "file:../core", "fs-extra": "^9.0.1", "node-watch": "^0.7.0", + "tar": "^6.0.5", "yargs": "^16.1.1" } } diff --git a/src/cli.ts b/src/cli.ts new file mode 100644 index 0000000..fb4f569 --- /dev/null +++ b/src/cli.ts @@ -0,0 +1,314 @@ +import { logger } from '@squeebot/core/lib/core'; +import { IRepository } from '@squeebot/core/lib/plugin/repository'; +import { ReadLine } from 'readline'; +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 repositoryCommand(...args: any[]): void { + const help = 'repository add | update | remove '; + if (!args[0] || args[0] === 'help') { + logger.log(help); + return; + } + + switch (args[0]) { + case 'a': + case 'i': + case 'add': + case 'install': + if (!args[1]) { + logger.error('URL is required'); + return; + } + + this.bot.repositoryManager.installRepository(args[1]).then((repo) => { + logger.log('Installed repository %s!', repo.name); + }, (e) => logger.error(e.message)); + break; + case 'r': + case 'rem': + case 'remove': + case 'uninstall': + if (!args[1]) { + logger.error('Name is required'); + return; + } + + this.bot.repositoryManager.uninstallRepository(args[1]).then(() => { + logger.log('Installed repository %s.', args[1]); + }, (e) => logger.error(e.message)); + break; + case 'u': + case 'upd': + case 'update': + if (!args[1]) { + const repos = this.bot.repositoryManager.getAll(); + for (const repo of repos) { + this.checkUpdate(repo); + } + return; + } + + const repo = this.bot.repositoryManager.getRepoByName(args[1]); + if (!repo) { + logger.error('No such repository found.'); + return; + } + + this.checkUpdate(repo); + break; + default: + logger.log(help); + } + } + + private pluginCommand(...args: any[]): void { + const help = 'plugin install | update | uninstall | enable | disable | start | stop | list | running []'; + if (!args[0] || args[0] === 'help' || (!args[1] && args[0] !== 'list' && args[0] !== 'running')) { + logger.log(help); + return; + } + + switch (args[0]) { + case 'i': + case 'u': + case 'install': + case 'update': + this.bot.repositoryManager.installPlugin(args[1]).then((mf) => { + 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)); + break; + case 'list': + logger.log('Installed plugins:', + this.bot.pluginManager.availablePlugins.map((mf) => mf.name).join(', ')); + break; + case 'running': + logger.log('Currently running plugins:', + this.bot.pluginManager.getLoaded().map((p) => p.manifest.name).join(', ')); + break; + case 's': + 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; + } + + 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; + } + + 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; + } + + 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(); + break; + case 'disable': + if (!this.bot.pluginManager.getAvailableByName(args[1])) { + logger.error('No such plugin is available.'); + return; + } + + 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(); + break; + default: + logger.log(help); + } + } + + private channelCommand(...args: any[]): void { + const help = 'channel new | del | addplugin | delplugin [] []'; + if (!args[0] || args[0] === 'help' || (!args[1] && args[0] !== 'list')) { + logger.log(help); + return; + } + + switch(args[0]) { + case 'new': + if (this.bot.channelManager.getChannelByName(args[1])) { + logger.error('A channel by that name already exists!'); + break; + } + + this.bot.channelManager.addChannel({ + name: args[1], + plugins: [], + enabled: true, + }); + + logger.log('Channel added!'); + break; + case 'del': + case 'delete': + case 'remove': + const chan = this.bot.channelManager.getChannelByName(args[1]); + if (!chan) { + logger.error('No such channel exists!'); + return; + } + + this.bot.channelManager.removeChannel(chan); + + logger.log('Channel removed!'); + break; + case 'addp': + case 'addplugin': + const chan1 = this.bot.channelManager.getChannelByName(args[1]); + if (!chan1) { + logger.error('No such channel exists!'); + return; + } + + if (!args[2]) { + logger.error('A plugin name is required.'); + return; + } + + if (chan1.plugins.indexOf(args[2]) !== -1) { + chan1.plugins.push(args[2]); + } + + logger.log('Plugin added to channel!'); + break; + case 'remp': + case 'delp': + case 'remplugin': + case 'delplugin': + const chan2 = this.bot.channelManager.getChannelByName(args[1]); + if (!chan2) { + logger.error('No such channel exists!'); + return; + } + + if (!args[2]) { + logger.error('A plugin name is required.'); + return; + } + + const idx = chan2.plugins.indexOf(args[2]); + if (idx !== -1) { + chan2.plugins.splice(idx, 1); + } + + 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; + } + + chan3.enabled = true; + logger.log('Channel enabled!'); + break; + case 'disable': + const chan4 = this.bot.channelManager.getChannelByName(args[1]); + if (!chan4) { + logger.error('No such channel exists!'); + return; + } + + chan4.enabled = false; + logger.log('Channel disabled!'); + break; + } + + this.bot.config.config.channels = this.bot.channelManager.getAll(); + this.bot.config.save(); + } + + public attach(rl: ReadLine) { + rl.on('line', (line: string) => { + const split = line.split(' '); + + if (!split[0]) { + return; + } + + switch(split[0]) { + case 'r': + case 'repo': + case 'repository': + this.repositoryCommand(...split.slice(1)); + break; + case 'p': + case 'pl': + case 'plugin': + this.pluginCommand(...split.slice(1)); + break; + case 'c': + case 'chan': + case 'channel': + this.channelCommand(...split.slice(1)); + break; + case 'stop': + case 'exit': + case 'quit': + case 'shutdown': + this.bot.shutdown(); + break; + } + }) + } +} \ No newline at end of file diff --git a/src/core.ts b/src/core.ts index 528e2e5..e319acd 100644 --- a/src/core.ts +++ b/src/core.ts @@ -4,6 +4,8 @@ import { ChannelManager } from '@squeebot/core/lib/channel'; import { PluginManager, PluginMetaLoader } from '@squeebot/core/lib/plugin'; +import { RepositoryManager } from '@squeebot/core/lib/plugin/repository'; + import { NPMExecutor } from '@squeebot/core/lib/npm'; import { Configuration, IEnvironment } from '@squeebot/core/lib/types'; @@ -24,19 +26,24 @@ export class Squeebot { public stream: ScopedEventEmitter = new ScopedEventEmitter(); - private pluginManager: PluginManager = new PluginManager( + public pluginManager: PluginManager = new PluginManager( [], this.stream, this.environment, this.npm); + + public repositoryManager: RepositoryManager = new RepositoryManager( + this.environment, + this.pluginManager, + ); - private channelManager: ChannelManager = new ChannelManager(this.stream); + public channelManager: ChannelManager = new ChannelManager(this.stream); - private pluginLoader: PluginMetaLoader = new PluginMetaLoader( + public pluginLoader: PluginMetaLoader = new PluginMetaLoader( this.environment, ); - private config: Configuration = new Configuration( + public config: Configuration = new Configuration( this.environment, path.join(this.environment.configurationPath, 'squeebot.json'), defaultConfiguration, @@ -44,7 +51,7 @@ export class Squeebot { private shuttingDown = false; - constructor(private environment: IEnvironment) {} + constructor(public environment: IEnvironment) {} public async initialize(autostart: boolean = true): Promise { // Load configuration @@ -59,11 +66,12 @@ export class Squeebot { // Give manager all loaded manifests this.pluginManager.addAvailable(plugins); + // Load repositories + await this.repositoryManager.loadFromFiles(); + // Start channels this.channelManager.initialize(this.config.get('channels')); - logger.debug(this.channelManager); - if (autostart) { // Start enabled plugins await this.startPlugins(); diff --git a/src/squeebot.ts b/src/squeebot.ts index 4badf26..cb17689 100644 --- a/src/squeebot.ts +++ b/src/squeebot.ts @@ -2,8 +2,8 @@ import child_process from 'child_process'; import fs from 'fs-extra'; import watch from 'node-watch'; import path from 'path'; -import readline from 'readline'; import yargs from 'yargs'; +import tar from 'tar'; import { logger } from '@squeebot/core/lib/core'; import { NPMExecutor } from '@squeebot/core/lib/npm'; @@ -168,7 +168,7 @@ async function deploy( console.log('Copying plugins to', pluginsPath); const listAllFiles = await fs.readdir(outDir); for (const f of listAllFiles) { - if (f === 'repository') { + if (f === 'repository' || f.indexOf('.tgz') !== -1) { continue; } const dst = path.join(pluginsPath, f); @@ -220,8 +220,6 @@ async function buildRepository( const loader = new PluginMetaLoader(env); const plugins = await loader.loadAll(false); const savedList: any[] = []; - let indexFileContents = 'name=' + meta.name + '\n'; - indexFileContents += 'created=' + Math.floor(Date.now() / 1000) + '\n'; console.log('Found the following plugins:', plugins.map((plugin) => { return `${plugin.name}@${plugin.version}`; @@ -232,7 +230,6 @@ async function buildRepository( name: plugin.name, version: plugin.version, }); - indexFileContents += `${plugin.name} ${plugin.version} "${plugin.description}"\n`; }); meta.plugins = savedList; @@ -251,7 +248,12 @@ async function buildRepository( console.log('Creating repository index'); await fs.remove(outDir); await fs.ensureDir(outDir); - await fs.writeFile(path.join(outDir, 'repository'), indexFileContents); + await fs.writeJSON(path.join(outDir, 'repository.json'), { + created: Math.floor(Date.now() / 1000), + name: meta.name, + plugins: meta.plugins, + schema: 1, + }); console.log('Copying plugins'); for (const plugin of plugins) { @@ -273,6 +275,16 @@ async function buildRepository( } } + console.log('Creating tarballs'); + for (const plugin of plugins) { + const plOut = path.join(outDir, plugin.name); + await tar.c({ + gzip: true, + file: plOut + '.plugin.tgz', + C: outDir, + }, [plugin.name]); + } + if (doDeploy == null) { console.log('Done!'); return; @@ -287,7 +299,7 @@ async function watchRepository( location?: string, out = true, doDeploy?: string, - onlyDeploy = false): void { + onlyDeploy = false): Promise { if (!location) { location = process.cwd(); diff --git a/src/squeebotd.ts b/src/squeebotd.ts index 1174c13..6041e40 100644 --- a/src/squeebotd.ts +++ b/src/squeebotd.ts @@ -8,6 +8,7 @@ import { loadEnvironment } from '@squeebot/core/lib/core'; import { IEnvironment } from '@squeebot/core/lib/types'; import { Squeebot } from './core'; +import { SqueebotCLI } from './cli'; const rl = readline.createInterface({ input: process.stdin, @@ -29,7 +30,13 @@ async function start(argv: any): Promise { const sb = new Squeebot(env); - await sb.initialize(); + await sb.initialize(argv.e !== true); + + // Create a CLI if interactive mode is enabled + if (argv.i === true) { + const sbrl = new SqueebotCLI(sb); + sbrl.attach(rl); + } sb.attachReadline(rl); } @@ -50,6 +57,11 @@ yargs.scriptName('squeebotd') alias: 'no-enable', describe: 'Do not automatically execute enabled plugins on startup', type: 'boolean', + }) + .option('i', { + alias: 'interactive', + describe: 'Enable built-in command line interface', + type: 'boolean', }); });