manyplug 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/banner.png +0 -0
- package/bin/manyplug.js +107 -0
- package/config.json +14 -0
- package/docs/manyplug.1 +273 -0
- package/package.json +41 -0
- package/src/enable.js +86 -0
- package/src/init.js +111 -0
- package/src/install.js +204 -0
- package/src/list.js +90 -0
- package/src/registry-ops.js +45 -0
- package/src/remove.js +114 -0
- package/src/sync.js +179 -0
- package/src/ui.js +70 -0
- package/src/utils.js +71 -0
- package/src/validate.js +121 -0
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
CLI plugin manager for ManyBot.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @freakk.dev/manyplug
|
|
9
|
+
# or
|
|
10
|
+
npm link # for development
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Get help
|
|
17
|
+
manyplug help [command]
|
|
18
|
+
|
|
19
|
+
# Create new plugin
|
|
20
|
+
manyplug init my-plugin --category games
|
|
21
|
+
|
|
22
|
+
# Install from repository
|
|
23
|
+
manyplug install many-ai
|
|
24
|
+
|
|
25
|
+
# Install plugin from local path
|
|
26
|
+
manyplug install --local ../my-plugin
|
|
27
|
+
|
|
28
|
+
# List installed plugins
|
|
29
|
+
manyplug list
|
|
30
|
+
|
|
31
|
+
# Validate manyplug.json
|
|
32
|
+
manyplug validate
|
|
33
|
+
manyplug validate ./my-plugin
|
|
34
|
+
```
|
package/banner.png
ADDED
|
Binary file
|
package/bin/manyplug.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { program } from 'commander';
|
|
4
|
+
import { createRequire } from 'module';
|
|
5
|
+
import { installCommand } from '../src/install.js';
|
|
6
|
+
import { listCommand } from '../src/list.js';
|
|
7
|
+
import { removeCommand } from '../src/remove.js';
|
|
8
|
+
import { enableCommand, disableCommand } from '../src/enable.js';
|
|
9
|
+
import { syncCommand, updateCommand } from '../src/sync.js';
|
|
10
|
+
import { initCommand } from '../src/init.js';
|
|
11
|
+
import { validateCommand } from '../src/validate.js';
|
|
12
|
+
|
|
13
|
+
const pkg = createRequire(import.meta.url)('../package.json');
|
|
14
|
+
|
|
15
|
+
// ------------------------------------------------------------
|
|
16
|
+
// help
|
|
17
|
+
// ------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
function printHelp(cmd) {
|
|
20
|
+
if (!cmd) {
|
|
21
|
+
console.log(`manyplug ${pkg.version} — plugin manager for ManyBot`);
|
|
22
|
+
console.log('https://git.stxerr.dev/manyplug.git\n');
|
|
23
|
+
console.log('commands: init install remove list enable disable sync update validate');
|
|
24
|
+
console.log('options: -v/--version help <command>');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const c = program.commands.find(c => c.name() === cmd || c.aliases().includes(cmd));
|
|
29
|
+
if (!c) { console.error(`unknown command: ${cmd}`); process.exit(1); }
|
|
30
|
+
|
|
31
|
+
const args = c.registeredArguments
|
|
32
|
+
.map(a => (a.required ? `<${a._name}>` : `[${a._name}]`)).join(' ');
|
|
33
|
+
|
|
34
|
+
console.log(`manyplug ${c.name()} ${args}`);
|
|
35
|
+
console.log(c.description());
|
|
36
|
+
|
|
37
|
+
if (c.aliases().length)
|
|
38
|
+
console.log(`aliases: ${c.aliases().join(', ')}`);
|
|
39
|
+
|
|
40
|
+
const opts = c.options;
|
|
41
|
+
if (opts.length) {
|
|
42
|
+
console.log('\noptions:');
|
|
43
|
+
for (const o of opts)
|
|
44
|
+
console.log(` ${o.flags.padEnd(22)} ${o.description}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ------------------------------------------------------------
|
|
49
|
+
// program
|
|
50
|
+
// ------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
program
|
|
53
|
+
.name('manyplug')
|
|
54
|
+
.version(pkg.version, '-v, --version')
|
|
55
|
+
.helpOption(false);
|
|
56
|
+
|
|
57
|
+
program.command('help [command]').description('show help for a command')
|
|
58
|
+
.action(cmd => { printHelp(cmd); process.exit(0); });
|
|
59
|
+
|
|
60
|
+
program.command('init [name]').description('create new plugin boilerplate')
|
|
61
|
+
.option('-c, --category <cat>', 'category (games media utility service admin fun)', 'utility')
|
|
62
|
+
.option('--service', 'mark as background service plugin', false)
|
|
63
|
+
.action(initCommand);
|
|
64
|
+
|
|
65
|
+
program.command('install [plugins...]').description('install plugins from registry or local path')
|
|
66
|
+
.option('-l, --local <path>', 'install from local path')
|
|
67
|
+
.option('-y, --yes', 'skip confirmation')
|
|
68
|
+
.option('--needed', 'skip already up-to-date plugins')
|
|
69
|
+
.action(installCommand);
|
|
70
|
+
|
|
71
|
+
program.command('remove [plugins...]').alias('rm').description('remove installed plugins')
|
|
72
|
+
.option('-y, --yes', 'skip confirmation')
|
|
73
|
+
.option('--remove-deps', 'also uninstall npm dependencies')
|
|
74
|
+
.action(removeCommand);
|
|
75
|
+
|
|
76
|
+
program.command('list').alias('ls').description('list installed plugins (enabled only by default)')
|
|
77
|
+
.option('-a, --all', 'include disabled plugins')
|
|
78
|
+
.action(listCommand);
|
|
79
|
+
|
|
80
|
+
program.command('enable [plugins...]').description('enable plugins')
|
|
81
|
+
.action(enableCommand);
|
|
82
|
+
|
|
83
|
+
program.command('disable [plugins...]').description('disable plugins')
|
|
84
|
+
.action(disableCommand);
|
|
85
|
+
|
|
86
|
+
program.command('validate [path]').alias('val').description('validate manyplug.json')
|
|
87
|
+
.action(validateCommand);
|
|
88
|
+
|
|
89
|
+
program.command('sync').description('sync local registry with remote')
|
|
90
|
+
.option('-f, --force', 'save even if nothing changed')
|
|
91
|
+
.action(syncCommand);
|
|
92
|
+
|
|
93
|
+
program.command('update').description('install/update all plugins from remote')
|
|
94
|
+
.option('-y, --yes', 'skip confirmation')
|
|
95
|
+
.action(updateCommand);
|
|
96
|
+
|
|
97
|
+
// ------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
if (process.argv.length <= 2) { printHelp(); process.exit(0); }
|
|
100
|
+
|
|
101
|
+
program.on('command:*', ([op]) => {
|
|
102
|
+
console.error(`unknown command: ${op}`);
|
|
103
|
+
console.error('run "manyplug help" for usage');
|
|
104
|
+
process.exit(1);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
program.parse();
|
package/config.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mirrors": [
|
|
3
|
+
{
|
|
4
|
+
"name": "Stxerr.dev",
|
|
5
|
+
"fetch": "https://git.stxerr.dev/manyplug-repo.git/plain",
|
|
6
|
+
"git": "https://git.stxerr.dev/manyplug-repo.git"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"name": "Codeberg",
|
|
10
|
+
"fetch": "https://codeberg.org/synt-xerror/manyplug-repo/raw/branch/master",
|
|
11
|
+
"git": "https://codeberg.org/synt-xerror/manyplug-repo"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
package/docs/manyplug.1
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
.TH MANYPLUG 1 "2026-04-20" "v0.1.0" "ManyPlug Manual"
|
|
2
|
+
.SH NAME
|
|
3
|
+
manyplug \- CLI plugin manager for ManyBot
|
|
4
|
+
.SH SYNOPSIS
|
|
5
|
+
.B manyplug
|
|
6
|
+
.I command
|
|
7
|
+
.RI [ options ]
|
|
8
|
+
.RI [ arguments ]
|
|
9
|
+
.SH DESCRIPTION
|
|
10
|
+
.B manyplug
|
|
11
|
+
is a command-line plugin manager designed specifically for ManyBot, a WhatsApp bot framework.
|
|
12
|
+
It provides a complete plugin management system for creating, installing, enabling, disabling,
|
|
13
|
+
removing, and validating plugins.
|
|
14
|
+
.PP
|
|
15
|
+
Plugins can be installed from remote registries (via git) or from local paths.
|
|
16
|
+
Each plugin follows a standardized structure with metadata defined in
|
|
17
|
+
.BR manyplug.json .
|
|
18
|
+
.SH COMMANDS
|
|
19
|
+
.SS "init [name]"
|
|
20
|
+
Create a new plugin boilerplate with the proper directory structure.
|
|
21
|
+
.PP
|
|
22
|
+
Options:
|
|
23
|
+
.RS
|
|
24
|
+
.TP
|
|
25
|
+
.BR \-c ", " \-\-category " <cat>"
|
|
26
|
+
Plugin category: games, media, utility, service, admin, or fun (default: utility)
|
|
27
|
+
.TP
|
|
28
|
+
.B \-\-service
|
|
29
|
+
Mark the plugin as a service plugin (runs in background)
|
|
30
|
+
.RE
|
|
31
|
+
.SS "install [plugins...]"
|
|
32
|
+
Install one or more plugins from the registry or a local path.
|
|
33
|
+
.PP
|
|
34
|
+
Options:
|
|
35
|
+
.RS
|
|
36
|
+
.TP
|
|
37
|
+
.BR \-l ", " \-\-local " <path>"
|
|
38
|
+
Install from a local directory instead of the registry (single plugin only)
|
|
39
|
+
.TP
|
|
40
|
+
.BR \-y ", " \-\-yes
|
|
41
|
+
Skip confirmation prompts
|
|
42
|
+
.TP
|
|
43
|
+
.B \-\-needed
|
|
44
|
+
Skip reinstalling plugins that are already up-to-date (pacman-style)
|
|
45
|
+
.RE
|
|
46
|
+
.SS "remove [plugins...]"
|
|
47
|
+
Remove installed plugins. Aliased as
|
|
48
|
+
.BR rm .
|
|
49
|
+
.PP
|
|
50
|
+
Options:
|
|
51
|
+
.RS
|
|
52
|
+
.TP
|
|
53
|
+
.BR \-y ", " \-\-yes
|
|
54
|
+
Skip confirmation prompts
|
|
55
|
+
.TP
|
|
56
|
+
.B \-\-remove\-deps
|
|
57
|
+
Also remove npm dependencies installed for the plugin
|
|
58
|
+
.RE
|
|
59
|
+
.SS "list"
|
|
60
|
+
List all installed plugins (enabled only by default). Aliased as
|
|
61
|
+
.BR ls .
|
|
62
|
+
.PP
|
|
63
|
+
Options:
|
|
64
|
+
.RS
|
|
65
|
+
.TP
|
|
66
|
+
.BR \-a ", " \-\-all
|
|
67
|
+
Include disabled plugins in the listing
|
|
68
|
+
.RE
|
|
69
|
+
.SS "enable [plugins...]"
|
|
70
|
+
Enable one or more installed plugins. Enabled plugins are loaded by ManyBot on startup.
|
|
71
|
+
.SS "disable [plugins...]"
|
|
72
|
+
Disable one or more installed plugins. Disabled plugins remain installed but are not loaded.
|
|
73
|
+
.SS "sync"
|
|
74
|
+
Synchronize the local registry with remote repositories.
|
|
75
|
+
.PP
|
|
76
|
+
Options:
|
|
77
|
+
.RS
|
|
78
|
+
.TP
|
|
79
|
+
.B \-\-no\-add
|
|
80
|
+
Do not add new plugins discovered in the remote registry
|
|
81
|
+
.TP
|
|
82
|
+
.BR \-u ", " \-\-update
|
|
83
|
+
Install or update plugins from the remote registry
|
|
84
|
+
.RE
|
|
85
|
+
.SS "update"
|
|
86
|
+
Update all plugins from remote (equivalent to
|
|
87
|
+
.BR "sync --update" ).
|
|
88
|
+
.SS "validate [path]"
|
|
89
|
+
Validate a plugin's
|
|
90
|
+
.B manyplug.json
|
|
91
|
+
configuration file. Aliased as
|
|
92
|
+
.BR val .
|
|
93
|
+
.PP
|
|
94
|
+
If no path is specified, validates the current directory.
|
|
95
|
+
.SH "PLUGIN STRUCTURE"
|
|
96
|
+
A valid plugin follows this directory structure:
|
|
97
|
+
.PP
|
|
98
|
+
.RS
|
|
99
|
+
.nf
|
|
100
|
+
my-plugin/
|
|
101
|
+
├── manyplug.json # Plugin metadata (required)
|
|
102
|
+
├── index.js # Entry point (or as specified in main)
|
|
103
|
+
├── locale/ # Translations (recommended)
|
|
104
|
+
│ ├── pt.json
|
|
105
|
+
│ ├── en.json
|
|
106
|
+
│ └── es.json
|
|
107
|
+
└── README.md # Documentation
|
|
108
|
+
.fi
|
|
109
|
+
.RE
|
|
110
|
+
.SH "MANYPLUG.JSON"
|
|
111
|
+
The
|
|
112
|
+
.B manyplug.json
|
|
113
|
+
file defines plugin metadata. Required fields:
|
|
114
|
+
.PP
|
|
115
|
+
.RS
|
|
116
|
+
.TP
|
|
117
|
+
.B name
|
|
118
|
+
Plugin name (lowercase letters, numbers, hyphens; 2-50 characters)
|
|
119
|
+
.TP
|
|
120
|
+
.B version
|
|
121
|
+
Semantic version (e.g., "1.0.0")
|
|
122
|
+
.TP
|
|
123
|
+
.B category
|
|
124
|
+
One of: games, media, utility, service, admin, fun
|
|
125
|
+
.RE
|
|
126
|
+
.PP
|
|
127
|
+
Optional fields:
|
|
128
|
+
.PP
|
|
129
|
+
.RS
|
|
130
|
+
.TP
|
|
131
|
+
.B service
|
|
132
|
+
Set to true for background service plugins
|
|
133
|
+
.TP
|
|
134
|
+
.B description
|
|
135
|
+
Plugin description
|
|
136
|
+
.TP
|
|
137
|
+
.B author
|
|
138
|
+
Author name
|
|
139
|
+
.TP
|
|
140
|
+
.B license
|
|
141
|
+
License identifier (e.g., "MIT")
|
|
142
|
+
.TP
|
|
143
|
+
.B main
|
|
144
|
+
Entry point file (default: "index.js")
|
|
145
|
+
.TP
|
|
146
|
+
.B dependencies
|
|
147
|
+
npm dependencies object
|
|
148
|
+
.TP
|
|
149
|
+
.B externalDependencies
|
|
150
|
+
System command dependencies object
|
|
151
|
+
.RE
|
|
152
|
+
.SH "SERVICE PLUGINS"
|
|
153
|
+
Service plugins (marked with
|
|
154
|
+
.B "service: true"
|
|
155
|
+
) run continuously in the background rather than responding to commands.
|
|
156
|
+
They are useful for tasks like tracking user activity, scheduled jobs, or maintaining state.
|
|
157
|
+
.SH "EXTERNAL DEPENDENCIES"
|
|
158
|
+
Plugins can declare system-level dependencies in
|
|
159
|
+
.BR manyplug.json :
|
|
160
|
+
.PP
|
|
161
|
+
.RS
|
|
162
|
+
.nf
|
|
163
|
+
"externalDependencies": {
|
|
164
|
+
"ffmpeg": "ffmpeg",
|
|
165
|
+
"python3": {
|
|
166
|
+
"command": "python3",
|
|
167
|
+
"optional": true
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
.fi
|
|
171
|
+
.RE
|
|
172
|
+
.PP
|
|
173
|
+
The
|
|
174
|
+
.B validate
|
|
175
|
+
command checks if these commands exist in the system PATH.
|
|
176
|
+
.SH "REGISTRY"
|
|
177
|
+
ManyPlug maintains a local
|
|
178
|
+
.B registry.json
|
|
179
|
+
file tracking installed plugins, their versions, categories, and whether they are local or remote.
|
|
180
|
+
.PP
|
|
181
|
+
The remote registry is fetched from configured mirrors (see
|
|
182
|
+
.BR config.json ).
|
|
183
|
+
.SH "PLUGIN ENTRY POINT"
|
|
184
|
+
A plugin's entry file exports an async function receiving
|
|
185
|
+
.B msg
|
|
186
|
+
and
|
|
187
|
+
.B api
|
|
188
|
+
objects:
|
|
189
|
+
.PP
|
|
190
|
+
.RS
|
|
191
|
+
.nf
|
|
192
|
+
export default async function ({ msg, api }) {
|
|
193
|
+
if (!msg.is(CMD_PREFIX + "command")) return;
|
|
194
|
+
await msg.reply("Hello!");
|
|
195
|
+
}
|
|
196
|
+
.fi
|
|
197
|
+
.RE
|
|
198
|
+
.SH "FILES"
|
|
199
|
+
.TP
|
|
200
|
+
.I ./registry.json
|
|
201
|
+
Local plugin registry tracking installed plugins
|
|
202
|
+
.TP
|
|
203
|
+
.I ./config.json
|
|
204
|
+
ManyPlug configuration with mirror URLs
|
|
205
|
+
.TP
|
|
206
|
+
.I ./manyplug.json
|
|
207
|
+
Plugin manifest (in plugin directories)
|
|
208
|
+
.TP
|
|
209
|
+
.I ./manybot.conf
|
|
210
|
+
ManyBot plugin configuration (managed by enable/disable)
|
|
211
|
+
.SH "EXAMPLES"
|
|
212
|
+
Create a new utility plugin:
|
|
213
|
+
.PP
|
|
214
|
+
.RS
|
|
215
|
+
.B manyplug init my-plugin --category utility
|
|
216
|
+
.RE
|
|
217
|
+
.PP
|
|
218
|
+
Install plugins from the registry:
|
|
219
|
+
.PP
|
|
220
|
+
.RS
|
|
221
|
+
.B manyplug install fun games
|
|
222
|
+
.RE
|
|
223
|
+
.PP
|
|
224
|
+
Install from a local path:
|
|
225
|
+
.PP
|
|
226
|
+
.RS
|
|
227
|
+
.B manyplug install --local ./my-custom-plugin
|
|
228
|
+
.RE
|
|
229
|
+
.PP
|
|
230
|
+
Install without confirmation:
|
|
231
|
+
.PP
|
|
232
|
+
.RS
|
|
233
|
+
.B manyplug install plugin-name --yes
|
|
234
|
+
.RE
|
|
235
|
+
.PP
|
|
236
|
+
List all plugins including disabled:
|
|
237
|
+
.PP
|
|
238
|
+
.RS
|
|
239
|
+
.B manyplug list --all
|
|
240
|
+
.RE
|
|
241
|
+
.PP
|
|
242
|
+
Enable and disable plugins:
|
|
243
|
+
.PP
|
|
244
|
+
.RS
|
|
245
|
+
.B manyplug enable plugin-a plugin-b
|
|
246
|
+
.br
|
|
247
|
+
.B manyplug disable plugin-a
|
|
248
|
+
.RE
|
|
249
|
+
.PP
|
|
250
|
+
Sync with remote and update all plugins:
|
|
251
|
+
.PP
|
|
252
|
+
.RS
|
|
253
|
+
.B manyplug sync --update
|
|
254
|
+
.RE
|
|
255
|
+
.PP
|
|
256
|
+
Remove a plugin and its dependencies:
|
|
257
|
+
.PP
|
|
258
|
+
.RS
|
|
259
|
+
.B manyplug remove plugin-name --remove-deps
|
|
260
|
+
.RE
|
|
261
|
+
.PP
|
|
262
|
+
Validate a plugin configuration:
|
|
263
|
+
.PP
|
|
264
|
+
.RS
|
|
265
|
+
.B manyplug validate ./my-plugin
|
|
266
|
+
.RE
|
|
267
|
+
.SH "SEE ALSO"
|
|
268
|
+
.BR manybot (1)
|
|
269
|
+
.SH "AUTHORS"
|
|
270
|
+
Written by freakk.dev.
|
|
271
|
+
.br
|
|
272
|
+
Source available at: https://git.stxerr.dev/manyplug.git
|
|
273
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "manyplug",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "CLI plugin manager for ManyBot",
|
|
5
|
+
"man": ["docs/manyplug.1"],
|
|
6
|
+
"keywords": [
|
|
7
|
+
"manybot",
|
|
8
|
+
"plugin",
|
|
9
|
+
"cli",
|
|
10
|
+
"whatsapp"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "freakkdev",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "./bin/manyplug.js",
|
|
16
|
+
"bin": {
|
|
17
|
+
"manyplug": "bin/manyplug.js"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "vitest",
|
|
21
|
+
"test:run": "vitest run",
|
|
22
|
+
"test:coverage": "vitest run --coverage"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@freakk.dev/manyplug": "^1.0.1",
|
|
26
|
+
"chalk": "^5.3.0",
|
|
27
|
+
"cli-progress": "^3.12.0",
|
|
28
|
+
"commander": "^11.0.0",
|
|
29
|
+
"fs": "^0.0.1-security",
|
|
30
|
+
"fs-extra": "^11.3.4",
|
|
31
|
+
"gradient-string": "^3.0.0",
|
|
32
|
+
"ora": "^8.0.1",
|
|
33
|
+
"pino": "^9.14.0",
|
|
34
|
+
"pino-pretty": "^11.0.0",
|
|
35
|
+
"sqlite3": "^5.1.7"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@vitest/coverage-v8": "^4.1.4",
|
|
39
|
+
"vitest": "^4.1.4"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/enable.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
const CONF_PATH = path.join(process.cwd(), 'manybot.conf');
|
|
5
|
+
const PLUGINS_DIR = path.join(process.cwd(), 'src', 'plugins');
|
|
6
|
+
|
|
7
|
+
// ------------------------------------------------------------
|
|
8
|
+
// conf file — PLUGINS=[a,b,c]
|
|
9
|
+
// ------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
async function readEnabled() {
|
|
12
|
+
if (!await fs.pathExists(CONF_PATH)) return [];
|
|
13
|
+
const raw = await fs.readFile(CONF_PATH, 'utf-8');
|
|
14
|
+
const match = raw.match(/PLUGINS=\[\s*([\s\S]*?)\s*\]/);
|
|
15
|
+
if (!match) return [];
|
|
16
|
+
return match[1].split(',').map(s => s.trim()).filter(Boolean);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function writeEnabled(plugins) {
|
|
20
|
+
await fs.writeFile(CONF_PATH, `PLUGINS=[\n${plugins.map(p => p + ',').join('\n')}\n]\n`, 'utf-8');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ------------------------------------------------------------
|
|
24
|
+
// enable / disable commands
|
|
25
|
+
// ------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
async function toggle(names, action) {
|
|
28
|
+
if (!names.length) {
|
|
29
|
+
console.error(`usage: manyplug ${action} <plugin> [plugin2...]`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const t = Date.now();
|
|
34
|
+
const enabled = await readEnabled();
|
|
35
|
+
const set = new Set(enabled);
|
|
36
|
+
const results = [];
|
|
37
|
+
|
|
38
|
+
for (const name of names) {
|
|
39
|
+
if (action === 'enable' && !await fs.pathExists(path.join(PLUGINS_DIR, name))) {
|
|
40
|
+
console.error(`x ${name}: not installed`);
|
|
41
|
+
results.push({ name, changed: false, notFound: true });
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const was = set.has(name);
|
|
45
|
+
if (action === 'enable') set.add(name);
|
|
46
|
+
else set.delete(name);
|
|
47
|
+
const changed = set.has(name) !== was;
|
|
48
|
+
results.push({ name, changed });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const changed = results.filter(r => r.changed);
|
|
52
|
+
|
|
53
|
+
if (changed.length) {
|
|
54
|
+
try {
|
|
55
|
+
await writeEnabled([...set]);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
console.error(`error: ${e.message}`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (const r of results) {
|
|
63
|
+
if (r.notFound) continue; // already printed above
|
|
64
|
+
const symbol = action === 'enable' ? '+' : '-';
|
|
65
|
+
const note = r.changed ? '' : ` (already ${action}d)`;
|
|
66
|
+
console.log(`${symbol} ${r.name}${note}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (names.length > 1) {
|
|
70
|
+
const notFound = results.filter(r => r.notFound).length;
|
|
71
|
+
console.log(`${changed.length}/${names.length} changed (${((Date.now() - t) / 1000).toFixed(2)}s)${notFound ? ` ${notFound} not found` : ''}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (results.some(r => r.notFound)) process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function enableCommand(input) {
|
|
78
|
+
const names = Array.isArray(input) ? input : (input ? [input] : []);
|
|
79
|
+
return toggle(names, 'enable');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function disableCommand(input) {
|
|
83
|
+
const names = Array.isArray(input) ? input : (input ? [input] : []);
|
|
84
|
+
return toggle(names, 'disable');
|
|
85
|
+
}
|
|
86
|
+
|
package/src/init.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { formatSize } from './ui.js';
|
|
4
|
+
|
|
5
|
+
const VALID_CATEGORIES = ['games', 'media', 'utility', 'service', 'admin', 'fun'];
|
|
6
|
+
|
|
7
|
+
// ------------------------------------------------------------
|
|
8
|
+
// templates
|
|
9
|
+
// ------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
const manyplugJson = (name, category, service) => ({
|
|
12
|
+
name, version: '1.0.0',
|
|
13
|
+
description: `${name} plugin for ManyBot`,
|
|
14
|
+
category, service,
|
|
15
|
+
author: '', license: 'MIT', main: 'index.js',
|
|
16
|
+
dependencies: {}, externalDependencies: {}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const indexJs = (name) => `\
|
|
20
|
+
// ${name} — ManyBot plugin
|
|
21
|
+
import { CMD_PREFIX } from "../../config.js";
|
|
22
|
+
|
|
23
|
+
export default async function ({ msg, api }) {
|
|
24
|
+
if (!msg.is(CMD_PREFIX + "hi")) return;
|
|
25
|
+
await msg.reply("Hello!");
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const localePt = (name) => ({ plugin: { name, description: `Plugin ${name} para ManyBot` }, commands: {} });
|
|
30
|
+
const localeEn = (name) => ({ plugin: { name, description: `${name} plugin for ManyBot` }, commands: {} });
|
|
31
|
+
|
|
32
|
+
const gitignore = () => `node_modules/\npackage-lock.json\n*.log\n.vscode/\n.DS_Store\ncoverage/\n`;
|
|
33
|
+
|
|
34
|
+
const readme = (name) => `\
|
|
35
|
+
# ${name}
|
|
36
|
+
|
|
37
|
+
Plugin for ManyBot.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
\`\`\`bash
|
|
42
|
+
manyplug install ${name}
|
|
43
|
+
\`\`\`
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
Describe how to use your plugin here.
|
|
48
|
+
|
|
49
|
+
## License
|
|
50
|
+
|
|
51
|
+
MIT
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
// ------------------------------------------------------------
|
|
55
|
+
// init command
|
|
56
|
+
// ------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
export async function initCommand(name, options = {}) {
|
|
59
|
+
const t = Date.now();
|
|
60
|
+
|
|
61
|
+
if (!name || !/^[a-z0-9-]+$/.test(name)) {
|
|
62
|
+
console.error('error: name must be lowercase letters, numbers, and hyphens only');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const category = VALID_CATEGORIES.includes(options.category) ? options.category : 'utility';
|
|
67
|
+
if (!VALID_CATEGORIES.includes(options.category)) {
|
|
68
|
+
console.warn(`warn: unknown category, using "utility"`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const dir = path.resolve(name);
|
|
72
|
+
const service = options.service || false;
|
|
73
|
+
|
|
74
|
+
if (await fs.pathExists(dir)) {
|
|
75
|
+
process.stdout.write(`"${name}" already exists, overwrite? [y/N] `);
|
|
76
|
+
const answer = await new Promise(res => process.stdin.once('data', d => res(d.toString().trim().toLowerCase())));
|
|
77
|
+
if (answer !== 'y') { console.log('cancelled'); process.exit(0); }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const f = (...p) => path.join(dir, ...p);
|
|
81
|
+
|
|
82
|
+
console.log(`creating ${name} category=${category} service=${service}`);
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
await fs.ensureDir(f('locale'));
|
|
86
|
+
await fs.writeJson(f('manyplug.json'), manyplugJson(name, category, service), { spaces: 2 });
|
|
87
|
+
await fs.writeFile(f('index.js'), indexJs(name));
|
|
88
|
+
await fs.writeJson(f('locale', 'pt.json'), localePt(name), { spaces: 2 });
|
|
89
|
+
await fs.writeJson(f('locale', 'en.json'), localeEn(name), { spaces: 2 });
|
|
90
|
+
await fs.writeFile(f('.gitignore'), gitignore());
|
|
91
|
+
await fs.writeFile(f('README.md'), readme(name));
|
|
92
|
+
} catch (e) {
|
|
93
|
+
console.error(`error: ${e.message}`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const size = await fs.stat(dir).then(() => getDirSize(dir));
|
|
98
|
+
console.log(`done size=${formatSize(size)} time=${((Date.now() - t) / 1000).toFixed(2)}s`);
|
|
99
|
+
console.log(` cd ${name}`);
|
|
100
|
+
console.log(` manyplug validate .`);
|
|
101
|
+
console.log(` manyplug install --local .`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function getDirSize(dir) {
|
|
105
|
+
let total = 0;
|
|
106
|
+
for (const entry of await fs.readdir(dir, { withFileTypes: true })) {
|
|
107
|
+
const p = path.join(dir, entry.name);
|
|
108
|
+
total += entry.isDirectory() ? await getDirSize(p) : (await fs.stat(p)).size;
|
|
109
|
+
}
|
|
110
|
+
return total;
|
|
111
|
+
}
|