@texisoft/create-nexus-plugin 0.0.0-stage → 0.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/LICENSE +78 -0
- package/README.md +25 -2
- package/bin/create-nexus-plugin.js +18 -0
- package/package.json +44 -4
- package/src/scaffold.js +58 -0
- package/template/README.md +14 -0
- package/template/_gitignore +5 -0
- package/template/index.js +10 -0
- package/template/locales/de.json +3 -0
- package/template/locales/en.json +3 -0
- package/template/manifest.json +16 -0
- package/template/package.json.tpl +20 -0
- package/template/test/plugin.test.js.tpl +16 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
TEXISOFT PROPRIETARY LICENSE
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 texisoft. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This software, including its source code, documentation and associated
|
|
6
|
+
materials (the "Software"), is proprietary to texisoft ("Licensor"). It is made
|
|
7
|
+
available on public package registries for distribution convenience only.
|
|
8
|
+
Publication does NOT grant an open-source license and does NOT transfer or
|
|
9
|
+
waive any right of the Licensor.
|
|
10
|
+
|
|
11
|
+
1. Limited permission
|
|
12
|
+
Subject to this license, you may install and run the Software solely to
|
|
13
|
+
develop, test, build and publish plugins for the Nexus Discord bot platform
|
|
14
|
+
operated by the Licensor ("Nexus"), and only for as long as you comply with
|
|
15
|
+
this license. This permission is personal, non-exclusive, non-transferable,
|
|
16
|
+
non-sublicensable and revocable at any time.
|
|
17
|
+
|
|
18
|
+
2. Restrictions
|
|
19
|
+
Except where applicable law expressly forbids the restriction, you may not:
|
|
20
|
+
a) copy, modify, adapt, translate or create derivative works of the Software;
|
|
21
|
+
b) redistribute, publish, sell, rent, lease, sublicense or otherwise make
|
|
22
|
+
the Software or any part of it available to third parties, whether
|
|
23
|
+
modified or not, including re-publishing it to any package registry or
|
|
24
|
+
repository;
|
|
25
|
+
c) decompile, reverse engineer or attempt to derive source code, algorithms
|
|
26
|
+
or the sandbox, permission or protocol design, except for the source
|
|
27
|
+
that is delivered in readable form with the package, which remains
|
|
28
|
+
subject to this license;
|
|
29
|
+
d) use the Software, or knowledge gained from it, to build, operate or
|
|
30
|
+
support a platform, service or product that competes with or replaces
|
|
31
|
+
Nexus, or that is compatible with Nexus plugins;
|
|
32
|
+
e) circumvent, disable or probe the security, sandbox, permission, rate
|
|
33
|
+
limit, review or consent mechanisms of Nexus, or use the Software to
|
|
34
|
+
attack, overload or gain unauthorized access to Nexus or any third
|
|
35
|
+
party;
|
|
36
|
+
f) remove or alter copyright, license or attribution notices;
|
|
37
|
+
g) use the Software to train, fine-tune or evaluate machine-learning models,
|
|
38
|
+
or to build datasets for that purpose.
|
|
39
|
+
|
|
40
|
+
3. Your plugins
|
|
41
|
+
Plugins you write remain yours. Submitting a plugin to Nexus is governed by
|
|
42
|
+
the Nexus developer terms, not by this license. Bundled portions of the
|
|
43
|
+
Software that the build tooling inserts into your plugin may be distributed
|
|
44
|
+
only as part of that plugin and only to Nexus.
|
|
45
|
+
|
|
46
|
+
4. Ownership
|
|
47
|
+
The Software is licensed, not sold. The Licensor retains all right, title
|
|
48
|
+
and interest, including all intellectual property rights.
|
|
49
|
+
|
|
50
|
+
5. Termination
|
|
51
|
+
This license ends automatically and without notice if you breach it. On
|
|
52
|
+
termination you must stop using the Software and delete all copies. The
|
|
53
|
+
Licensor may also withdraw this license at any time by publishing a notice
|
|
54
|
+
or removing the Software.
|
|
55
|
+
|
|
56
|
+
6. No warranty
|
|
57
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
58
|
+
IMPLIED, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
|
|
59
|
+
NON-INFRINGEMENT, AVAILABILITY OR COMPATIBILITY WITH ANY FUTURE VERSION. THE
|
|
60
|
+
SOFTWARE IS A PRE-1.0 RELEASE AND ITS INTERFACES MAY CHANGE WITHOUT NOTICE.
|
|
61
|
+
|
|
62
|
+
7. Limitation of liability
|
|
63
|
+
TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE LICENSOR IS NOT LIABLE FOR ANY
|
|
64
|
+
INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR PUNITIVE DAMAGES, LOSS OF
|
|
65
|
+
DATA, PROFITS OR GOODWILL, ARISING FROM OR RELATED TO THE SOFTWARE. THE
|
|
66
|
+
LICENSOR'S TOTAL LIABILITY IS LIMITED TO EUR 100, EXCEPT FOR LIABILITY THAT
|
|
67
|
+
CANNOT BE EXCLUDED UNDER APPLICABLE LAW (INTENT, GROSS NEGLIGENCE, INJURY
|
|
68
|
+
TO LIFE, BODY OR HEALTH, AND LIABILITY UNDER THE PRODUCT LIABILITY ACT).
|
|
69
|
+
|
|
70
|
+
8. Governing law
|
|
71
|
+
This license is governed by the laws of the Federal Republic of Germany,
|
|
72
|
+
excluding its conflict-of-laws rules and the UN Convention on Contracts for
|
|
73
|
+
the International Sale of Goods. If you are a merchant or a legal entity,
|
|
74
|
+
the courts at the Licensor's registered seat have exclusive jurisdiction.
|
|
75
|
+
|
|
76
|
+
9. Severability
|
|
77
|
+
If a provision is invalid, the remaining provisions stay in force and the
|
|
78
|
+
invalid provision is replaced by the closest valid one.
|
package/README.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @texisoft/create-nexus-plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Legt ein neues Nexus-Plugin an.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm create @texisoft/nexus-plugin mein-plugin
|
|
7
|
+
cd mein-plugin
|
|
8
|
+
npm install
|
|
9
|
+
npm test
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Du bekommst ein lauffähiges Projekt mit einem Beispiel-Command, Übersetzungen und einem Test:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
manifest.json id, Version, Berechtigungen, Commands, Sprachen
|
|
16
|
+
index.js export default definePlugin({ setup(ctx) { ... } })
|
|
17
|
+
locales/ Übersetzungen für ctx.t
|
|
18
|
+
test/ Tests mit dem Testkit
|
|
19
|
+
package.json SDK, CLI und Testkit als Abhängigkeiten
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Weiter geht es mit `npx nexus-plugin validate`, `npx nexus-plugin dev --guild <TEST_GUILD_ID>` und `npx nexus-plugin publish`. Was diese Befehle tun, steht in der README von `@texisoft/nexus-plugin-cli`.
|
|
23
|
+
|
|
24
|
+
## Lizenz
|
|
25
|
+
|
|
26
|
+
Node.js 22 oder neuer. Das Paket ist proprietär, alle Rechte bleiben bei texisoft. Du darfst es nur nutzen, um Plugins für Nexus zu entwickeln, Genaueres steht in der `LICENSE`. Die Version ist noch 0.x, Schnittstellen können sich ändern.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from 'node:path'
|
|
3
|
+
import { scaffoldPlugin } from '../src/scaffold.js'
|
|
4
|
+
|
|
5
|
+
const [id, ...rest] = process.argv.slice(2)
|
|
6
|
+
|
|
7
|
+
if (!id || id.startsWith('-')) {
|
|
8
|
+
process.stderr.write('Aufruf: npm create @texisoft/nexus-plugin <plugin-id> [Anzeigename]\n')
|
|
9
|
+
process.exit(2)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const files = await scaffoldPlugin({ dir: resolve(process.cwd(), id), id, name: rest.join(' ') || undefined })
|
|
14
|
+
process.stdout.write(`Plugin "${id}" angelegt (${files.length} Dateien).\n\n cd ${id}\n npm install\n npm test\n npx nexus-plugin validate\n`)
|
|
15
|
+
} catch (error) {
|
|
16
|
+
process.stderr.write(`Fehler: ${error.message}\n`)
|
|
17
|
+
process.exit(1)
|
|
18
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@texisoft/create-nexus-plugin",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Gerüst für neue Nexus-Plugins (npm create @texisoft/nexus-plugin <id>).",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"nexus",
|
|
7
|
+
"discord",
|
|
8
|
+
"discord-bot",
|
|
9
|
+
"plugin",
|
|
10
|
+
"texisoft"
|
|
11
|
+
],
|
|
12
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/texisoft/discord-nexus-bot.git",
|
|
16
|
+
"directory": "packages/create-nexus-plugin"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/texisoft/discord-nexus-bot/tree/master/packages/create-nexus-plugin",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/texisoft/discord-nexus-bot/issues"
|
|
21
|
+
},
|
|
22
|
+
"type": "module",
|
|
23
|
+
"bin": {
|
|
24
|
+
"create-nexus-plugin": "./bin/create-nexus-plugin.js"
|
|
25
|
+
},
|
|
26
|
+
"main": "./src/scaffold.js",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": "./src/scaffold.js"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"bin",
|
|
32
|
+
"src",
|
|
33
|
+
"template",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=22"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@texisoft/nexus-plugin-sdk": "^0.1.0"
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/scaffold.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises'
|
|
2
|
+
import { dirname, join, relative } from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { PLUGIN_ID_REGEX } from '@texisoft/nexus-plugin-sdk'
|
|
5
|
+
|
|
6
|
+
export const TEMPLATE_DIR = fileURLToPath(new URL('../template', import.meta.url))
|
|
7
|
+
|
|
8
|
+
const RENAMES = { _gitignore: '.gitignore' }
|
|
9
|
+
|
|
10
|
+
function targetName(name) {
|
|
11
|
+
if (RENAMES[name]) return RENAMES[name]
|
|
12
|
+
return name.endsWith('.tpl') ? name.slice(0, -4) : name
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function titleFromId(id) {
|
|
16
|
+
return id.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(' ')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function walk(dir) {
|
|
20
|
+
const out = []
|
|
21
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
22
|
+
const full = join(dir, entry.name)
|
|
23
|
+
if (entry.isDirectory()) out.push(...await walk(full))
|
|
24
|
+
else out.push(full)
|
|
25
|
+
}
|
|
26
|
+
return out
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function assertEmpty(dir) {
|
|
30
|
+
try {
|
|
31
|
+
const info = await stat(dir)
|
|
32
|
+
if (!info.isDirectory()) throw new Error(`${dir} existiert und ist kein Verzeichnis`)
|
|
33
|
+
if ((await readdir(dir)).length > 0) throw new Error(`Zielverzeichnis ${dir} ist nicht leer`)
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if (error.code !== 'ENOENT') throw error
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function scaffoldPlugin({ dir, id, name, templateDir = TEMPLATE_DIR }) {
|
|
40
|
+
if (!PLUGIN_ID_REGEX.test(id)) {
|
|
41
|
+
throw new Error(`Ungültige Plugin-ID "${id}": erlaubt sind ^[a-z][a-z0-9-]{2,31}$`)
|
|
42
|
+
}
|
|
43
|
+
await assertEmpty(dir)
|
|
44
|
+
const values = { __ID__: id, __NAME__: name ?? titleFromId(id) }
|
|
45
|
+
const created = []
|
|
46
|
+
for (const file of await walk(templateDir)) {
|
|
47
|
+
const rel = relative(templateDir, file)
|
|
48
|
+
const parts = rel.split(/[\\/]/)
|
|
49
|
+
parts[parts.length - 1] = targetName(parts[parts.length - 1])
|
|
50
|
+
const target = join(dir, ...parts)
|
|
51
|
+
let content = await readFile(file, 'utf8')
|
|
52
|
+
for (const [token, value] of Object.entries(values)) content = content.replaceAll(token, value)
|
|
53
|
+
await mkdir(dirname(target), { recursive: true })
|
|
54
|
+
await writeFile(target, content)
|
|
55
|
+
created.push(parts.join('/'))
|
|
56
|
+
}
|
|
57
|
+
return created.sort()
|
|
58
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# __NAME__
|
|
2
|
+
|
|
3
|
+
Nexus-Plugin (`__ID__`).
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
npm install
|
|
7
|
+
npm test Tests mit dem Test-Kit
|
|
8
|
+
npx nexus-plugin validate Manifest, Permissions und Muster prüfen
|
|
9
|
+
npx nexus-plugin login Schlüssel einmalig speichern (nie im Projekt)
|
|
10
|
+
npx nexus-plugin dev Plugin lokal entwickeln, verbunden mit einer Test-Guild
|
|
11
|
+
npx nexus-plugin publish Bauen, hochladen und zur Prüfung einreichen
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Die vollständige Anleitung steht in `docs/PLUGINS-DEV.md` des Nexus-Repositories.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { definePlugin } from '@texisoft/nexus-plugin-sdk'
|
|
2
|
+
|
|
3
|
+
export default definePlugin({
|
|
4
|
+
setup(ctx) {
|
|
5
|
+
ctx.commands.on('hello', async (interaction, api) => {
|
|
6
|
+
const n = await ctx.storage.kv.incr('hello:count')
|
|
7
|
+
await api.reply({ content: ctx.t('hello.reply', { name: interaction.user.username, n }), ephemeral: true })
|
|
8
|
+
})
|
|
9
|
+
}
|
|
10
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "__ID__",
|
|
3
|
+
"name": "__NAME__",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"sdkVersion": "^0.1.0",
|
|
6
|
+
"description": "Beschreibe hier, was dein Plugin tut.",
|
|
7
|
+
"permissions": {
|
|
8
|
+
"commandsMax": 1,
|
|
9
|
+
"storageQuotaKb": 16
|
|
10
|
+
},
|
|
11
|
+
"commands": [
|
|
12
|
+
{ "name": "hello", "description": "Sagt Hallo und zählt mit", "options": [] }
|
|
13
|
+
],
|
|
14
|
+
"locales": { "default": "de", "supported": ["de", "en"] },
|
|
15
|
+
"main": "index.js"
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__ID__",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "node --test",
|
|
8
|
+
"validate": "nexus-plugin validate",
|
|
9
|
+
"build": "nexus-plugin build",
|
|
10
|
+
"dev": "nexus-plugin dev",
|
|
11
|
+
"publish:plugin": "nexus-plugin publish"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@texisoft/nexus-plugin-sdk": "^0.1.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@texisoft/nexus-plugin-cli": "^0.1.0",
|
|
18
|
+
"@texisoft/nexus-plugin-testkit": "^0.1.0"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { createProjectHarness } from '@texisoft/nexus-plugin-testkit'
|
|
5
|
+
|
|
6
|
+
const dir = fileURLToPath(new URL('..', import.meta.url))
|
|
7
|
+
|
|
8
|
+
test('/hello antwortet und zählt hoch', async () => {
|
|
9
|
+
const harness = await createProjectHarness(dir)
|
|
10
|
+
const first = await harness.runCommand('hello')
|
|
11
|
+
assert.equal(first.ok, true)
|
|
12
|
+
assert.match(first.reply.content, /Nummer 1\./)
|
|
13
|
+
const second = await harness.runCommand('hello')
|
|
14
|
+
assert.match(second.reply.content, /Nummer 2\./)
|
|
15
|
+
assert.equal(harness.storage.get('hello:count'), 2)
|
|
16
|
+
})
|