create-seedcord 0.4.2 → 0.5.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 +8 -21
- package/dist/devtools-DVub5sJz.mjs +4 -0
- package/dist/index.mjs +223 -11452
- package/package.json +9 -9
- package/templates/.vscode/settings.json.hbs +4 -2
- package/templates/AGENTS.md.hbs +2 -2
- package/templates/README.md.hbs +28 -5
- package/templates/bunfig.toml.hbs +1 -1
- package/templates/cloudflare.config.ts.hbs +18 -0
- package/templates/env.hbs +5 -5
- package/templates/gitattributes.hbs +3 -0
- package/templates/gitignore.hbs +7 -1
- package/templates/package.json.hbs +17 -6
- package/templates/pnpm-workspace.yaml.hbs +7 -0
- package/templates/seedcord.config.ts.hbs +3 -4
- package/templates/src/bot.ts.hbs +7 -8
- package/templates/src/commands/Ping.ts.hbs +1 -1
- package/templates/src/handlers/Ping.ts.hbs +2 -2
- package/templates/src/index.ts.hbs +2 -0
- package/templates/src/plugins/Uptime.ts.hbs +16 -0
- package/templates/tsconfig.json.hbs +18 -2
- package/dist/index.mjs.map +0 -1
- package/templates/tsconfig.build.json.hbs +0 -11
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-seedcord",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"description": "Scaffold a new seedcord bot",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"discord",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"starter"
|
|
13
13
|
],
|
|
14
14
|
"engines": {
|
|
15
|
-
"node": "
|
|
15
|
+
"node": "^24.22.1 || >=26.12.1",
|
|
16
16
|
"bun": ">=1.4.2"
|
|
17
17
|
},
|
|
18
18
|
"bin": {
|
|
@@ -40,15 +40,15 @@
|
|
|
40
40
|
"LICENSE"
|
|
41
41
|
],
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@
|
|
44
|
-
"@seedcord/errors": "^0.
|
|
45
|
-
"@seedcord/eslint-config": "^
|
|
46
|
-
"@seedcord/tsconfig": "^
|
|
43
|
+
"@seedcord/cli-prompts": "^0.0.2",
|
|
44
|
+
"@seedcord/errors": "^0.14.0",
|
|
45
|
+
"@seedcord/eslint-config": "^3.0.0",
|
|
46
|
+
"@seedcord/tsconfig": "^3.0.0",
|
|
47
47
|
"@seedcord/tsdown-config": "^2.0.1",
|
|
48
|
-
"@seedcord/types": "^0.
|
|
48
|
+
"@seedcord/types": "^0.15.0",
|
|
49
49
|
"@seedcord/vitest-config": "^0.0.1",
|
|
50
50
|
"discord.js": "^14.27.0",
|
|
51
|
-
"handlebars": "^4.7.
|
|
51
|
+
"handlebars": "^4.7.10",
|
|
52
52
|
"package-manager-detector": "^1.8.0",
|
|
53
53
|
"tsx": "^4.23.15",
|
|
54
54
|
"typescript": "^6.0.3"
|
|
@@ -70,5 +70,5 @@
|
|
|
70
70
|
"test:watch": "vitest dev",
|
|
71
71
|
"coverage": "vitest run --coverage"
|
|
72
72
|
},
|
|
73
|
-
"readme": "<div align=\"center\">\n <picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://cdn.seedcord.org/assets/wordmark-dark.webp\" />\n <img src=\"https://cdn.seedcord.org/assets/wordmark-light.webp\" alt=\"seedcord\" width=\"440\" />\n </picture>\n</div>\n\n<div align=\"center\">\n <h3>The whole Discord bot, typed end to end</h3>\n <a href=\"https://seedcord.org\">Website</a> ·\n <a href=\"https://seedcord.org/guide\">Guide</a> ·\n <a href=\"https://seedcord.org/docs\">Reference</a> ·\n <a href=\"https://
|
|
73
|
+
"readme": "<div align=\"center\">\n <picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://cdn.seedcord.org/assets/wordmark-dark.webp\" />\n <img src=\"https://cdn.seedcord.org/assets/wordmark-light.webp\" alt=\"seedcord\" width=\"440\" />\n </picture>\n</div>\n\n<div align=\"center\">\n <h3>The whole Discord bot, typed end to end</h3>\n <a href=\"https://seedcord.org\">Website</a> ·\n <a href=\"https://seedcord.org/guide\">Guide</a> ·\n <a href=\"https://seedcord.org/docs\">Reference</a> ·\n <a href=\"https://seedcord.org/chat/\">Discord</a>\n</div>\n\n<br />\n\n<div align=\"center\">\n\n[](https://www.npmjs.com/package/create-seedcord) [](https://nodejs.org) [](https://bun.com) [](LICENSE)\n\n</div>\n\n## About\n\n`create-seedcord` sets up a new seedcord bot. It asks a few questions, writes the project, and can install its dependencies and make the first git commit. You run it once, when you start a new bot.\n\nseedcord is pre-1.0, so a minor version can break your bot. Each release's changelog says what changed and how to update.\n\n## Usage\n\n```sh\npnpm create seedcord my-bot\n```\n\n`npm create seedcord`, `yarn create seedcord`, and `bun create seedcord` work too, and the project uses that package manager.\n\nIt asks whether your bot is a gateway or an http bot, where an http bot runs, what a gateway bot reacts to, your bot token, and the servers to test your commands in. The [first bot guide](https://seedcord.org/guide/first-bot) walks through each question.\n\nEvery question has a flag, and `--help` prints them all. npm passes flags on only after a `--`:\n\n```sh\nnpm create seedcord my-bot -- --transport gateway --capabilities reactions\n```\n\n## What you get\n\nA TypeScript project with one slash command, its handler, a sample plugin, `seedcord.config.ts`, ESLint, Prettier, and a `.env` for your token. A gateway bot also gets a sample event handler. An http bot on Cloudflare Workers also gets a `cloudflare.config.ts` and a `deploy` script.\n\n`pnpm dev` starts the bot with hot reload.\n"
|
|
74
74
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files.watcherExclude": {
|
|
3
|
-
"**/seedcord-gen.d.ts": true
|
|
3
|
+
"**/seedcord-gen.d.ts": true,
|
|
4
|
+
"**/.seedcord/**": true
|
|
4
5
|
},
|
|
5
6
|
"search.exclude": {
|
|
6
7
|
"**/seedcord-gen.d.ts": true
|
|
7
8
|
},
|
|
8
9
|
"files.readonlyInclude": {
|
|
9
|
-
"**/seedcord-gen.d.ts": true
|
|
10
|
+
"**/seedcord-gen.d.ts": true,
|
|
11
|
+
"**/.seedcord/**": true
|
|
10
12
|
}
|
|
11
13
|
}
|
package/templates/AGENTS.md.hbs
CHANGED
|
@@ -10,7 +10,7 @@ Rules for an AI coding agent working in this repo.
|
|
|
10
10
|
|
|
11
11
|
The API reference is at <https://seedcord.org/docs>, one page per symbol.
|
|
12
12
|
|
|
13
|
-
This project runs on the {{#if isGateway}}gateway{{else}}http{{/if}} transport and imports everything from `{{ transportPackage }}`. A page marks anything that differs between the two.
|
|
13
|
+
This project runs on the {{#if isGateway}}gateway{{else}}http{{/if}} transport{{#if isEdge}} on Cloudflare Workers{{/if}} and imports everything from `{{ transportPackage }}`. A page marks anything that differs between the two transports.
|
|
14
14
|
|
|
15
15
|
## How a command works here
|
|
16
16
|
|
|
@@ -35,7 +35,7 @@ Run `{{ pm.run }} codegen` after you add a command or change its options. It wri
|
|
|
35
35
|
|
|
36
36
|
## Connections and state
|
|
37
37
|
|
|
38
|
-
Anything that holds a connection, keeps state between interactions, or runs in the background belongs in a plugin, either an existing one or your own. Read <https://seedcord.org/guide/plugins.md> and <https://seedcord.org/guide/plugins
|
|
38
|
+
Anything that holds a connection, keeps state between interactions, or runs in the background belongs in a plugin, either an existing one or your own. Read <https://seedcord.org/guide/plugins.md> and <https://seedcord.org/guide/writing-plugins.md> before you add one.
|
|
39
39
|
|
|
40
40
|
## Imports
|
|
41
41
|
|
package/templates/README.md.hbs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# {{ projectName }}
|
|
2
2
|
|
|
3
|
-
A Discord bot built with [seedcord](https://seedcord.org){{#if isGateway}} on the gateway transport{{else}} on the http transport{{/if}}.
|
|
3
|
+
A Discord bot built with [seedcord](https://seedcord.org){{#if isGateway}} on the gateway transport{{else}} on the http transport{{#if isEdge}}, running on Cloudflare Workers{{/if}}{{/if}}.
|
|
4
4
|
|
|
5
5
|
## Running it
|
|
6
6
|
|
|
@@ -17,11 +17,22 @@ That starts the dev server with hot reload{{#unless isGateway}} and opens a tunn
|
|
|
17
17
|
| script | what it does |
|
|
18
18
|
| --- | --- |
|
|
19
19
|
| `{{ pm.run }} dev` | dev server with hot reload |
|
|
20
|
+
{{#if isEdge}}
|
|
21
|
+
| `{{ pm.run }} build` | build the worker into `.cloudflare/output/` |
|
|
22
|
+
| `{{ pm.run }} deploy` | build, then upload the worker with `cf deploy` |
|
|
23
|
+
{{else}}
|
|
20
24
|
| `{{ pm.run }} build` | compile to `dist/` |
|
|
21
25
|
| `{{ pm.run }} start` | run the compiled build |
|
|
26
|
+
{{/if}}
|
|
22
27
|
| `{{ pm.run }} codegen` | regenerate `src/seedcord-gen.d.ts` |
|
|
23
|
-
|
|
28
|
+
{{#if isEdge}}
|
|
29
|
+
| `{{ pm.run }} types` | regenerate the binding types in `.cloudflare/types/` |
|
|
30
|
+
{{/if}}
|
|
31
|
+
| `{{ pm.run }} commands` | list, deploy, or clean up your commands on Discord |
|
|
32
|
+
| `{{ pm.run }} commands:deploy` | send your commands to Discord |
|
|
24
33
|
| `{{ pm.run }} tc` | type-check |
|
|
34
|
+
| `{{ pm.run }} lint` | eslint |
|
|
35
|
+
| `{{ pm.run }} check` | type-check, lint, and check formatting |
|
|
25
36
|
|
|
26
37
|
<!-- prettier-ignore-end -->
|
|
27
38
|
|
|
@@ -36,16 +47,28 @@ Run `codegen` after you add a command or change its options. It writes the types
|
|
|
36
47
|
| key | required |
|
|
37
48
|
| --- | --- |
|
|
38
49
|
| `DISCORD_BOT_TOKEN` | yes |
|
|
39
|
-
{{#unless isGateway}}
|
|
40
|
-
| `DISCORD_PUBLIC_KEY` | yes |
|
|
41
|
-
{{/unless}}
|
|
42
50
|
| `UNKNOWN_EXCEPTION_WEBHOOK_URL` | no |
|
|
43
51
|
| `HANDLED_EXCEPTION_WEBHOOK_URL` | no |
|
|
52
|
+
| `TESTING_GUILD_IDS` | no, empty deploys your commands globally |
|
|
53
|
+
{{#unless isEdge}}
|
|
44
54
|
| `ENV` | no, defaults to `development` |
|
|
55
|
+
{{/unless}}
|
|
45
56
|
|
|
46
57
|
<!-- prettier-ignore-end -->
|
|
47
58
|
|
|
48
59
|
The two webhook keys take Discord webhook URLs. Set them to get error reports posted to a channel.
|
|
60
|
+
{{#if isEdge}}
|
|
61
|
+
|
|
62
|
+
A worker gets only the keys that `cloudflare.config.ts` declares. Add a line there for each new one your bot reads. `{{ pm.run }} dev` takes their values from `.env`.
|
|
63
|
+
|
|
64
|
+
Cloudflare refuses to deploy a new worker until every declared secret has a value. Put the production values in a file like `.env.production`, then deploy the first time with:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
{{ pm.run }} build && {{ pm.exec }} cf deploy --prebuilt --secrets-file .env.production
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
After that, `{{ pm.run }} deploy` is enough. `TESTING_GUILD_IDS` can stay empty there, since a deployed worker never deploys commands.
|
|
71
|
+
{{/if}}
|
|
49
72
|
|
|
50
73
|
## Where things go
|
|
51
74
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{{#if isEdge}}
|
|
2
|
+
import { bindings, defineConfig } from 'cf/config';
|
|
3
|
+
|
|
4
|
+
// cf workers types calls this without a mode
|
|
5
|
+
export default defineConfig(({ mode = 'production' }) => ({
|
|
6
|
+
worker: {
|
|
7
|
+
name: '{{ projectName }}',
|
|
8
|
+
compatibilityDate: '2026-08-04',
|
|
9
|
+
env: {
|
|
10
|
+
DISCORD_BOT_TOKEN: bindings.secret(),
|
|
11
|
+
UNKNOWN_EXCEPTION_WEBHOOK_URL: bindings.secret(),
|
|
12
|
+
HANDLED_EXCEPTION_WEBHOOK_URL: bindings.secret(),
|
|
13
|
+
TESTING_GUILD_IDS: bindings.secret(),
|
|
14
|
+
ENV: bindings.text(mode)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}));
|
|
18
|
+
{{/if}}
|
package/templates/env.hbs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Your bot token, from the Bot page of your app in the Discord Developer Portal
|
|
2
2
|
DISCORD_BOT_TOKEN={{ token }}
|
|
3
|
-
{{#unless isGateway}}
|
|
4
|
-
|
|
5
|
-
# Public key, from the General Information page of the same app
|
|
6
|
-
DISCORD_PUBLIC_KEY={{ publicKey }}
|
|
7
|
-
{{/unless}}
|
|
8
3
|
|
|
9
4
|
# Discord webhooks the framework posts error reports to. Leave empty to skip reporting.
|
|
10
5
|
UNKNOWN_EXCEPTION_WEBHOOK_URL=
|
|
11
6
|
HANDLED_EXCEPTION_WEBHOOK_URL=
|
|
12
7
|
|
|
8
|
+
# Comma-separated ids of the servers your commands deploy to while you build. Leave empty to deploy globally.
|
|
9
|
+
TESTING_GUILD_IDS={{ testingGuildIds }}
|
|
10
|
+
{{#unless isEdge}}
|
|
11
|
+
|
|
13
12
|
ENV=development
|
|
13
|
+
{{/unless}}
|
package/templates/gitignore.hbs
CHANGED
|
@@ -4,17 +4,28 @@
|
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"private": true,
|
|
6
6
|
"engines": {
|
|
7
|
-
{{#if
|
|
7
|
+
{{#if cliOnBun}}"bun": "{{ engines.bun }}"{{else}}"node": "{{ engines.node }}"{{/if}}
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"dev": "seedcord dev",
|
|
11
11
|
"build": "seedcord build",
|
|
12
|
+
{{#if isEdge}}
|
|
13
|
+
"deploy": "seedcord build && cf deploy --prebuilt",
|
|
14
|
+
{{else}}
|
|
12
15
|
"start": "node --enable-source-maps dist/index.mjs",
|
|
16
|
+
{{/if}}
|
|
13
17
|
"codegen": "seedcord codegen",
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
"
|
|
18
|
+
{{#if isEdge}}
|
|
19
|
+
"types": "cf workers types",
|
|
20
|
+
{{/if}}
|
|
21
|
+
"commands": "seedcord commands",
|
|
22
|
+
"commands:deploy": "seedcord commands deploy",
|
|
23
|
+
"tc": "tsc --noEmit",
|
|
24
|
+
"lint": "eslint \"src/**/*.ts\" --cache",
|
|
25
|
+
"lint:fix": "eslint \"src/**/*.ts\" --fix --cache",
|
|
26
|
+
"fmt": "prettier --write \"src/**/*.{ts,json,md}\" --cache",
|
|
27
|
+
"fmt:check": "prettier --check \"src/**/*.{ts,json,md}\" --cache",
|
|
28
|
+
"check": "{{ pm.run }} tc && {{ pm.run }} lint && {{ pm.run }} fmt:check"{{#if isEdge}},
|
|
29
|
+
"postinstall": "cf workers types"{{/if}}
|
|
19
30
|
}
|
|
20
31
|
}
|
|
@@ -3,12 +3,11 @@ import { defineConfig } from 'seedcord';
|
|
|
3
3
|
export default defineConfig({
|
|
4
4
|
root: './src',
|
|
5
5
|
instance: './bot.ts',
|
|
6
|
-
|
|
6
|
+
{{#unless isEdge}}
|
|
7
|
+
build: { start: './index.ts' },
|
|
8
|
+
{{/unless}}
|
|
7
9
|
// set false to hold the running arc and live dot still, which cuts idle redraws by ~80%
|
|
8
10
|
idleAnimation: true,
|
|
9
|
-
build: {
|
|
10
|
-
tsconfig: './tsconfig.build.json'
|
|
11
|
-
},
|
|
12
11
|
hmr: {
|
|
13
12
|
// set true to run tsc --watch alongside the bot, at the cost of a second node process
|
|
14
13
|
typecheck: false
|
package/templates/src/bot.ts.hbs
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import { resolve } from 'node:path';
|
|
2
|
-
|
|
3
1
|
{{#if isGateway}}
|
|
4
2
|
import { Seedcord } from '@seedcord/gateway';
|
|
5
3
|
import { GatewayIntentBits{{#if partials}}, Partials{{/if}} } from 'discord.js';
|
|
6
4
|
{{else}}
|
|
7
5
|
import { Seedcord } from '@seedcord/http';
|
|
8
6
|
{{/if}}
|
|
9
|
-
import { Envapter } from 'envapt';
|
|
7
|
+
import { Converters, Envapter } from 'envapt';
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
import { Uptime } from '#plugins/Uptime'
|
|
12
10
|
|
|
13
11
|
export const seedcord = new Seedcord({
|
|
14
12
|
bot: {
|
|
@@ -23,13 +21,14 @@ export const seedcord = new Seedcord({
|
|
|
23
21
|
},
|
|
24
22
|
{{/if}}
|
|
25
23
|
interactions: {
|
|
26
|
-
path:
|
|
24
|
+
path: './handlers'
|
|
27
25
|
},
|
|
28
26
|
commands: {
|
|
29
|
-
path:
|
|
27
|
+
path: './commands',
|
|
28
|
+
guilds: Envapter.getAs('TESTING_GUILD_IDS', Converters.array(), [])
|
|
30
29
|
}{{#if isGateway}},
|
|
31
30
|
events: {
|
|
32
|
-
path:
|
|
31
|
+
path: './events'
|
|
33
32
|
}{{/if}}
|
|
34
33
|
},
|
|
35
34
|
subscribers: {
|
|
@@ -39,6 +38,6 @@ export const seedcord = new Seedcord({
|
|
|
39
38
|
notifications: {
|
|
40
39
|
developerUsername: '{{ developerUsername }}'
|
|
41
40
|
}
|
|
42
|
-
});
|
|
41
|
+
}).attach('uptime', Uptime);
|
|
43
42
|
|
|
44
43
|
export default seedcord;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SlashHandler, SlashRoute, timestampFromSnowflake } from '{{ transportPackage }}';
|
|
1
|
+
import { SlashHandler, SlashRoute, timestampFromSnowflake, toEpochSeconds } from '{{ transportPackage }}';
|
|
2
2
|
|
|
3
3
|
@SlashRoute('ping')
|
|
4
4
|
export class Ping extends SlashHandler<'ping'> {
|
|
@@ -7,7 +7,7 @@ export class Ping extends SlashHandler<'ping'> {
|
|
|
7
7
|
const lines = ['### Pong', `Roundtrip **${roundtrip}ms**`];
|
|
8
8
|
|
|
9
9
|
if (this.options.getBoolean('detailed')) {
|
|
10
|
-
lines.push(`
|
|
10
|
+
lines.push(`Up since **<t:${toEpochSeconds(this.core.uptime.startedAt)}:R>**`);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
await this.reply(lines.join('\n'));
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Plugin } from '{{ transportPackage }}';
|
|
2
|
+
|
|
3
|
+
import type { EpochMs } from '{{ transportPackage }}';
|
|
4
|
+
|
|
5
|
+
export class Uptime extends Plugin {
|
|
6
|
+
public startedAt = 0 as EpochMs;
|
|
7
|
+
|
|
8
|
+
public async init(): Promise<void> {
|
|
9
|
+
this.startedAt = Date.now() as EpochMs;
|
|
10
|
+
await Promise.resolve();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public elapsed(): number {
|
|
14
|
+
return Date.now() - this.startedAt;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -3,8 +3,24 @@
|
|
|
3
3
|
"extends": "@seedcord/tsconfig/node",
|
|
4
4
|
"compilerOptions": {
|
|
5
5
|
"rootDir": "./",
|
|
6
|
-
"outDir": "./dist"
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"types": ["node", "vite/client"],
|
|
8
|
+
{{#if isEdge}}
|
|
9
|
+
"customConditions": ["workerd"],
|
|
10
|
+
{{/if}}
|
|
11
|
+
"paths": {
|
|
12
|
+
"#src/*": ["./src/*"],
|
|
13
|
+
"#components/*": ["./src/components/*"],
|
|
14
|
+
"#plugins/*": ["./src/plugins/*"]
|
|
15
|
+
}
|
|
7
16
|
},
|
|
8
|
-
"include": [
|
|
17
|
+
"include": [
|
|
18
|
+
"src/**/*.ts",
|
|
19
|
+
"eslint.config.ts",
|
|
20
|
+
"prettier.config.ts",
|
|
21
|
+
"seedcord.config.ts"{{#if isEdge}},
|
|
22
|
+
"cloudflare.config.ts",
|
|
23
|
+
".cloudflare/types/index.d.ts"{{/if}}
|
|
24
|
+
],
|
|
9
25
|
"exclude": ["node_modules", "dist"]
|
|
10
26
|
}
|