spiralcord-full 2.1.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/bin/spiral.js +496 -0
- package/package.json +55 -0
- package/src/index.js +7 -0
- package/src/plugins/manager.js +413 -0
- package/src/runtime.js +180 -0
- package/src/test.js +361 -0
- package/src/web/public/index.html +358 -0
- package/src/web/server.js +184 -0
package/bin/spiral.js
ADDED
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const { execSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const program = new Command();
|
|
9
|
+
|
|
10
|
+
program
|
|
11
|
+
.name('spiral')
|
|
12
|
+
.description('Spiralcord - Discord Bot Runtime & Maker')
|
|
13
|
+
.version('2.0.0');
|
|
14
|
+
|
|
15
|
+
program
|
|
16
|
+
.command('install <repo>')
|
|
17
|
+
.description('Clone a Spiralcord bot to current directory')
|
|
18
|
+
.option('--no-install', 'Skip npm install')
|
|
19
|
+
.action(async (repo, options) => {
|
|
20
|
+
const cwd = process.cwd();
|
|
21
|
+
|
|
22
|
+
if (fs.existsSync(path.join(cwd, 'spiral.json'))) {
|
|
23
|
+
console.error('Error: spiral.json already exists here. Use "spiral update" to update.');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (fs.existsSync(path.join(cwd, '.git'))) {
|
|
28
|
+
console.error('Error: Already a git repo. Use "spiral update" to update.');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let repoUrl = repo;
|
|
33
|
+
if (!repo.includes('github.com') && !repo.startsWith('http')) {
|
|
34
|
+
repoUrl = `https://github.com/${repo}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log(`Installing from ${repoUrl}...`);
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
execSync('git init', { cwd, stdio: 'inherit' });
|
|
41
|
+
execSync(`git remote add origin ${repoUrl}`, { cwd, stdio: 'inherit' });
|
|
42
|
+
execSync('git fetch origin main --depth 1', { cwd, stdio: 'inherit' });
|
|
43
|
+
execSync('git checkout -b main FETCH_HEAD', { cwd, stdio: 'inherit' });
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('Failed to clone repository.');
|
|
46
|
+
try { execSync('git clean -fd', { cwd, stdio: 'inherit' }); } catch {}
|
|
47
|
+
try { fs.rmSync(path.join(cwd, '.git'), { recursive: true }); } catch {}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (options.install !== false) {
|
|
52
|
+
console.log('\nInstalling dependencies...');
|
|
53
|
+
try {
|
|
54
|
+
execSync('npm install', { cwd, stdio: 'inherit' });
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error('Failed to install dependencies. Run "npm install" manually.');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log('\nBot installed!');
|
|
61
|
+
console.log('\nNext steps:');
|
|
62
|
+
console.log(' Edit spiral.json to add your bot token');
|
|
63
|
+
console.log(' Run: spiral run');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
program
|
|
67
|
+
.command('update')
|
|
68
|
+
.description('Update bot from GitHub (preserves token and configs)')
|
|
69
|
+
.option('--no-install', 'Skip npm install')
|
|
70
|
+
.action(async (options) => {
|
|
71
|
+
const cwd = process.cwd();
|
|
72
|
+
|
|
73
|
+
if (!fs.existsSync(path.join(cwd, 'spiral.json'))) {
|
|
74
|
+
console.error('Error: spiral.json not found. Run "spiral install" first.');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!fs.existsSync(path.join(cwd, '.git'))) {
|
|
79
|
+
console.error('Error: Not a git repo. Run "spiral install" first.');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log('Saving configs...');
|
|
84
|
+
|
|
85
|
+
const spiralConfig = fs.readFileSync(path.join(cwd, 'spiral.json'), 'utf-8');
|
|
86
|
+
|
|
87
|
+
const pluginConfigs = {};
|
|
88
|
+
const pluginsDir = path.join(cwd, 'plugins');
|
|
89
|
+
if (fs.existsSync(pluginsDir)) {
|
|
90
|
+
for (const plugin of fs.readdirSync(pluginsDir)) {
|
|
91
|
+
const configPath = path.join(pluginsDir, plugin, 'config.json');
|
|
92
|
+
if (fs.existsSync(configPath)) {
|
|
93
|
+
pluginConfigs[plugin] = fs.readFileSync(configPath, 'utf-8');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
console.log('Pulling updates...');
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
execSync('git fetch origin main', { cwd, stdio: 'inherit' });
|
|
102
|
+
execSync('git reset --hard origin/main', { cwd, stdio: 'inherit' });
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error('Failed to pull updates.');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log('Restoring configs...');
|
|
109
|
+
|
|
110
|
+
fs.writeFileSync(path.join(cwd, 'spiral.json'), spiralConfig);
|
|
111
|
+
|
|
112
|
+
if (fs.existsSync(pluginsDir)) {
|
|
113
|
+
for (const [plugin, config] of Object.entries(pluginConfigs)) {
|
|
114
|
+
const configPath = path.join(pluginsDir, plugin, 'config.json');
|
|
115
|
+
if (fs.existsSync(path.join(pluginsDir, plugin))) {
|
|
116
|
+
fs.writeFileSync(configPath, config);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (options.install !== false) {
|
|
122
|
+
console.log('\nInstalling dependencies...');
|
|
123
|
+
try {
|
|
124
|
+
execSync('npm install', { cwd, stdio: 'inherit' });
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error('Failed to install dependencies. Run "npm install" manually.');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.log('\nBot updated!');
|
|
131
|
+
console.log('Run: spiral run');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
program
|
|
135
|
+
.command('run')
|
|
136
|
+
.description('Start the bot runtime')
|
|
137
|
+
.action(async () => {
|
|
138
|
+
const configPath = path.join(process.cwd(), 'spiral.json');
|
|
139
|
+
|
|
140
|
+
if (!fs.existsSync(configPath)) {
|
|
141
|
+
console.error('Error: spiral.json not found. Use "spiral install" to clone a bot.');
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
146
|
+
|
|
147
|
+
if (!config.token || config.token === 'YOUR_DISCORD_TOKEN_HERE') {
|
|
148
|
+
console.error('Error: Please set your bot token in spiral.json');
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const SpiralRuntime = require('../src/runtime');
|
|
153
|
+
const runtime = new SpiralRuntime(config);
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
await runtime.start();
|
|
157
|
+
console.log('Bot is running! Press Ctrl+C to stop.');
|
|
158
|
+
|
|
159
|
+
process.on('SIGINT', async () => {
|
|
160
|
+
console.log('\nShutting down...');
|
|
161
|
+
await runtime.stop();
|
|
162
|
+
process.exit(0);
|
|
163
|
+
});
|
|
164
|
+
} catch (error) {
|
|
165
|
+
console.error('Failed to start bot:', error.message);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
program
|
|
171
|
+
.command('web')
|
|
172
|
+
.description('Start the web plugin manager')
|
|
173
|
+
.option('-p, --port <port>', 'Port number', '3000')
|
|
174
|
+
.action(async (options) => {
|
|
175
|
+
const SpiralWeb = require('../src/web/server');
|
|
176
|
+
const web = new SpiralWeb(null);
|
|
177
|
+
|
|
178
|
+
process.env.PORT = options.port;
|
|
179
|
+
web.port = parseInt(options.port);
|
|
180
|
+
|
|
181
|
+
web.start();
|
|
182
|
+
console.log(`\nSpiralcord Web Manager started!`);
|
|
183
|
+
console.log(`Open http://localhost:${options.port} in your browser`);
|
|
184
|
+
console.log(`Press Ctrl+C to stop`);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
program
|
|
188
|
+
.command('test')
|
|
189
|
+
.description('Test all plugins for errors and conflicts')
|
|
190
|
+
.action(async () => {
|
|
191
|
+
const pluginsDir = path.join(process.cwd(), 'plugins');
|
|
192
|
+
|
|
193
|
+
if (!fs.existsSync(pluginsDir)) {
|
|
194
|
+
console.error('Error: No plugins directory found. Run "spiral install" first.');
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
console.log('Testing plugins...\n');
|
|
199
|
+
|
|
200
|
+
const SpiralTester = require('../src/test');
|
|
201
|
+
const tester = new SpiralTester(pluginsDir);
|
|
202
|
+
const result = await tester.test();
|
|
203
|
+
|
|
204
|
+
for (const r of result.results) {
|
|
205
|
+
const icon = r.status === 'ok' ? '\x1b[32m✓\x1b[0m' :
|
|
206
|
+
r.status === 'warning' ? '\x1b[33m⚠\x1b[0m' :
|
|
207
|
+
'\x1b[31m✗\x1b[0m';
|
|
208
|
+
|
|
209
|
+
const extra = r.commands > 0 ? ` (${r.commands} commands)` : '';
|
|
210
|
+
console.log(`${icon} ${r.name}${extra}`);
|
|
211
|
+
|
|
212
|
+
for (const issue of r.issues) {
|
|
213
|
+
console.log(` → ${issue}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
console.log('\n' + '─'.repeat(40));
|
|
218
|
+
|
|
219
|
+
const passedColor = result.errors === 0 ? '\x1b[32m' : '\x1b[31m';
|
|
220
|
+
console.log(`${passedColor}Results: ${result.passed} passed, ${result.warnings} warnings, ${result.errors} errors\x1b[0m`);
|
|
221
|
+
|
|
222
|
+
if (result.details.errors.length > 0) {
|
|
223
|
+
console.log('\n\x1b[31mErrors:\x1b[0m');
|
|
224
|
+
for (const err of result.details.errors) {
|
|
225
|
+
console.log(` ✗ ${err}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (result.details.warnings.length > 0) {
|
|
230
|
+
console.log('\n\x1b[33mWarnings:\x1b[0m');
|
|
231
|
+
for (const warn of result.details.warnings) {
|
|
232
|
+
console.log(` ⚠ ${warn}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (result.errors > 0) {
|
|
237
|
+
console.log('\n\x1b[31mFix errors before running the bot.\x1b[0m');
|
|
238
|
+
process.exit(1);
|
|
239
|
+
} else {
|
|
240
|
+
console.log('\n\x1b[32mAll tests passed! Run "spiral run" to start.\x1b[0m');
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const pluginCmd = program
|
|
245
|
+
.command('plugin')
|
|
246
|
+
.description('Manage plugins');
|
|
247
|
+
|
|
248
|
+
pluginCmd
|
|
249
|
+
.command('list')
|
|
250
|
+
.description('List installed plugins')
|
|
251
|
+
.action(() => {
|
|
252
|
+
const pluginsDir = path.join(process.cwd(), 'plugins');
|
|
253
|
+
|
|
254
|
+
if (!fs.existsSync(pluginsDir)) {
|
|
255
|
+
console.log('No plugins directory found. Use "spiral install" to clone a bot.');
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const enabled = [];
|
|
260
|
+
const disabled = [];
|
|
261
|
+
|
|
262
|
+
const folders = fs.readdirSync(pluginsDir);
|
|
263
|
+
for (const f of folders) {
|
|
264
|
+
const isDisabled = f.startsWith('.') && (f.endsWith('.disabled') || !fs.existsSync(path.join(pluginsDir, f, 'plugin.json')));
|
|
265
|
+
const pluginDirName = f.endsWith('.disabled') ? f.slice(1, -9) : f;
|
|
266
|
+
const pluginPath = path.join(pluginsDir, pluginDirName, 'plugin.json');
|
|
267
|
+
const manifestPath = path.join(pluginsDir, f, 'plugin.json');
|
|
268
|
+
|
|
269
|
+
const actualManifestPath = fs.existsSync(manifestPath) ? manifestPath : pluginPath;
|
|
270
|
+
if (fs.existsSync(actualManifestPath)) {
|
|
271
|
+
const manifest = JSON.parse(fs.readFileSync(actualManifestPath, 'utf-8'));
|
|
272
|
+
if (isDisabled) {
|
|
273
|
+
disabled.push(manifest);
|
|
274
|
+
} else {
|
|
275
|
+
enabled.push(manifest);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (enabled.length === 0 && disabled.length === 0) {
|
|
281
|
+
console.log('No plugins installed.');
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
console.log('Installed plugins:');
|
|
286
|
+
for (const m of enabled) {
|
|
287
|
+
console.log(` [enabled] ${m.name} v${m.version}: ${m.description}`);
|
|
288
|
+
}
|
|
289
|
+
for (const m of disabled) {
|
|
290
|
+
console.log(` [disabled] ${m.name} v${m.version}: ${m.description}`);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
pluginCmd
|
|
295
|
+
.command('install <source>')
|
|
296
|
+
.description('Install a plugin from GitHub or npm')
|
|
297
|
+
.action(async (source) => {
|
|
298
|
+
const cwd = process.cwd();
|
|
299
|
+
const pluginsDir = path.join(cwd, 'plugins');
|
|
300
|
+
|
|
301
|
+
if (!fs.existsSync(pluginsDir)) {
|
|
302
|
+
fs.mkdirSync(pluginsDir, { recursive: true });
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
let repoUrl = source;
|
|
306
|
+
let pluginName = '';
|
|
307
|
+
|
|
308
|
+
if (source.includes('github.com')) {
|
|
309
|
+
repoUrl = source;
|
|
310
|
+
const match = source.match(/github\.com\/[^\/]+\/([^\/\.]+)/);
|
|
311
|
+
pluginName = match ? match[1] : '';
|
|
312
|
+
} else if (source.startsWith('http')) {
|
|
313
|
+
repoUrl = source;
|
|
314
|
+
const match = source.match(/\/([^\/\.]+?)(?:\.git)?$/);
|
|
315
|
+
pluginName = match ? match[1] : '';
|
|
316
|
+
} else {
|
|
317
|
+
pluginName = source;
|
|
318
|
+
repoUrl = `https://github.com/${source}`;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (!pluginName) {
|
|
322
|
+
console.error('Could not determine plugin name from source.');
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const pluginDir = path.join(pluginsDir, pluginName);
|
|
327
|
+
|
|
328
|
+
if (fs.existsSync(pluginDir)) {
|
|
329
|
+
console.error(`Plugin "${pluginName}" already exists. Remove it first.`);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
console.log(`Installing plugin "${pluginName}"...`);
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
execSync(`git clone --depth 1 ${repoUrl} "${pluginDir}"`, { stdio: 'inherit' });
|
|
337
|
+
|
|
338
|
+
const gitDir = path.join(pluginDir, '.git');
|
|
339
|
+
if (fs.existsSync(gitDir)) {
|
|
340
|
+
fs.rmSync(gitDir, { recursive: true });
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const manifestPath = path.join(pluginDir, 'plugin.json');
|
|
344
|
+
if (!fs.existsSync(manifestPath)) {
|
|
345
|
+
console.error('Error: plugin.json not found in the cloned repository.');
|
|
346
|
+
fs.rmSync(pluginDir, { recursive: true });
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
351
|
+
console.log(`Installed ${manifest.name} v${manifest.version}`);
|
|
352
|
+
} catch (error) {
|
|
353
|
+
console.error('Failed to install plugin:', error.message);
|
|
354
|
+
if (fs.existsSync(pluginDir)) {
|
|
355
|
+
fs.rmSync(pluginDir, { recursive: true });
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
pluginCmd
|
|
361
|
+
.command('enable <name>')
|
|
362
|
+
.description('Enable a plugin')
|
|
363
|
+
.action((name) => {
|
|
364
|
+
const pluginsDir = path.join(process.cwd(), 'plugins');
|
|
365
|
+
const pluginDir = path.join(pluginsDir, name);
|
|
366
|
+
const disabledDir = path.join(pluginsDir, `.${name}.disabled`);
|
|
367
|
+
|
|
368
|
+
if (fs.existsSync(pluginDir)) {
|
|
369
|
+
console.log(`Plugin "${name}" is already enabled.`);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (fs.existsSync(disabledDir)) {
|
|
374
|
+
fs.renameSync(disabledDir, pluginDir);
|
|
375
|
+
console.log(`Plugin "${name}" enabled.`);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
console.error(`Plugin "${name}" not found.`);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
pluginCmd
|
|
383
|
+
.command('disable <name>')
|
|
384
|
+
.description('Disable a plugin by renaming its folder')
|
|
385
|
+
.action((name) => {
|
|
386
|
+
const pluginsDir = path.join(process.cwd(), 'plugins');
|
|
387
|
+
const pluginDir = path.join(pluginsDir, name);
|
|
388
|
+
const disabledDir = path.join(pluginsDir, `.${name}.disabled`);
|
|
389
|
+
|
|
390
|
+
if (!fs.existsSync(pluginDir)) {
|
|
391
|
+
console.error(`Plugin "${name}" not found.`);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
fs.renameSync(pluginDir, disabledDir);
|
|
396
|
+
console.log(`Plugin "${name}" disabled.`);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
pluginCmd
|
|
400
|
+
.command('create <name>')
|
|
401
|
+
.description('Create a new plugin')
|
|
402
|
+
.action((name) => {
|
|
403
|
+
const cwd = process.cwd();
|
|
404
|
+
const pluginDir = path.join(cwd, 'plugins', name);
|
|
405
|
+
|
|
406
|
+
if (fs.existsSync(pluginDir)) {
|
|
407
|
+
console.error(`Plugin "${name}" already exists.`);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
fs.mkdirSync(pluginDir, { recursive: true });
|
|
412
|
+
|
|
413
|
+
const manifest = {
|
|
414
|
+
name: name,
|
|
415
|
+
version: '1.0.0',
|
|
416
|
+
description: `${name} plugin`,
|
|
417
|
+
hooks: ['message_received']
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
const config = {
|
|
421
|
+
enabled: true,
|
|
422
|
+
message: `Hello from ${name}!`
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
const schema = {
|
|
426
|
+
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
427
|
+
title: `${name} Plugin Config`,
|
|
428
|
+
type: 'object',
|
|
429
|
+
properties: {
|
|
430
|
+
enabled: { type: 'boolean', default: true },
|
|
431
|
+
message: { type: 'string', default: `Hello from ${name}!` }
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
const code = `module.exports = {
|
|
436
|
+
hooks: {
|
|
437
|
+
message_received: async (payload, runtime) => {
|
|
438
|
+
const { message } = payload;
|
|
439
|
+
if (message.content.startsWith('!${name}')) {
|
|
440
|
+
const config = runtime.getPluginConfig('${name}');
|
|
441
|
+
await message.reply(config.message);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
};`;
|
|
446
|
+
|
|
447
|
+
fs.writeFileSync(path.join(pluginDir, 'plugin.json'), JSON.stringify(manifest, null, 2));
|
|
448
|
+
fs.writeFileSync(path.join(pluginDir, 'config.json'), JSON.stringify(config, null, 2));
|
|
449
|
+
fs.writeFileSync(path.join(pluginDir, 'config.schema.json'), JSON.stringify(schema, null, 2));
|
|
450
|
+
fs.writeFileSync(path.join(pluginDir, 'index.js'), code);
|
|
451
|
+
|
|
452
|
+
console.log(`Plugin "${name}" created at plugins/${name}/`);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
pluginCmd
|
|
456
|
+
.command('remove <name>')
|
|
457
|
+
.description('Remove a plugin')
|
|
458
|
+
.action((name) => {
|
|
459
|
+
const pluginsDir = path.join(process.cwd(), 'plugins', name);
|
|
460
|
+
const disabledDir = path.join(process.cwd(), 'plugins', `.${name}.disabled`);
|
|
461
|
+
|
|
462
|
+
if (fs.existsSync(pluginsDir)) {
|
|
463
|
+
fs.rmSync(pluginsDir, { recursive: true });
|
|
464
|
+
console.log(`Plugin "${name}" removed.`);
|
|
465
|
+
} else if (fs.existsSync(disabledDir)) {
|
|
466
|
+
fs.rmSync(disabledDir, { recursive: true });
|
|
467
|
+
console.log(`Plugin "${name}" removed.`);
|
|
468
|
+
} else {
|
|
469
|
+
console.log(`Plugin "${name}" not found.`);
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
pluginCmd
|
|
474
|
+
.command('edit <name>')
|
|
475
|
+
.description('Show plugin file location for editing')
|
|
476
|
+
.action((name) => {
|
|
477
|
+
const pluginsDir = path.join(process.cwd(), 'plugins', name);
|
|
478
|
+
|
|
479
|
+
if (!fs.existsSync(pluginsDir)) {
|
|
480
|
+
console.error(`Plugin "${name}" not found.`);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
console.log(`Plugin "${name}" files:`);
|
|
485
|
+
const files = fs.readdirSync(pluginsDir);
|
|
486
|
+
files.forEach(f => {
|
|
487
|
+
const fullPath = path.join(pluginsDir, f);
|
|
488
|
+
const stat = fs.statSync(fullPath);
|
|
489
|
+
if (stat.isFile()) {
|
|
490
|
+
console.log(` ${f}`);
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
console.log(`\nDirectory: ${pluginsDir}`);
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "spiralcord-full",
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "Spiralcord Full - Discord Bot Runtime with Voice, Music, AI, Database & Image Support",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"spiral": "./bin/spiral.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node bin/spiral.js"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"discord",
|
|
14
|
+
"bot",
|
|
15
|
+
"runtime",
|
|
16
|
+
"plugin",
|
|
17
|
+
"spiralcord",
|
|
18
|
+
"discord-bot",
|
|
19
|
+
"bot-framework",
|
|
20
|
+
"voice",
|
|
21
|
+
"music",
|
|
22
|
+
"ai",
|
|
23
|
+
"database",
|
|
24
|
+
"canvas"
|
|
25
|
+
],
|
|
26
|
+
"author": "",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"commander": "^12.1.0",
|
|
30
|
+
"discord.js": "^14.16.3",
|
|
31
|
+
"express": "^4.21.0",
|
|
32
|
+
"@discordjs/voice": "^0.17.0",
|
|
33
|
+
"@discordjs/opus": "^0.9.0",
|
|
34
|
+
"discord-player": "^6.6.0",
|
|
35
|
+
"ffmpeg-static": "^5.2.0",
|
|
36
|
+
"@napi-rs/canvas": "^0.1.44",
|
|
37
|
+
"sharp": "^0.33.0",
|
|
38
|
+
"mongoose": "^8.0.0",
|
|
39
|
+
"better-sqlite3": "^11.0.0",
|
|
40
|
+
"quick.db": "^9.1.0",
|
|
41
|
+
"openai": "^4.20.0",
|
|
42
|
+
"axios": "^1.6.0",
|
|
43
|
+
"dotenv": "^16.3.0",
|
|
44
|
+
"chalk": "^4.1.2",
|
|
45
|
+
"ms": "^2.1.3",
|
|
46
|
+
"uuid": "^9.0.0"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=16.0.0"
|
|
50
|
+
},
|
|
51
|
+
"files": [
|
|
52
|
+
"bin/",
|
|
53
|
+
"src/"
|
|
54
|
+
]
|
|
55
|
+
}
|