create-chapter 0.2.2 → 0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Quentin Truffy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # create-chapter
2
+
3
+ An alias of [`create-chapterjs`](https://www.npmjs.com/package/create-chapterjs), so that both spellings create a [ChapterJS](https://www.chapterjs.org) Discord bot:
4
+
5
+ ```bash
6
+ pnpm create chapter my-bot
7
+ ```
8
+
9
+ ```bash
10
+ pnpm create chapterjs my-bot
11
+ ```
12
+
13
+ Same questions, same templates, same version: this package only runs `create-chapterjs` with the arguments it receives. See [its README](https://www.npmjs.com/package/create-chapterjs) and the [quickstart](https://www.chapterjs.org/quickstart).
14
+
15
+ ## License
16
+
17
+ [MIT](./LICENSE)
package/dist/index.js CHANGED
@@ -1,96 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import * as p from '@clack/prompts';
3
- import { readFileSync } from 'node:fs';
4
- import { relative, resolve } from 'node:path';
5
- import { detectPackageManager, install, installedVersions, packageManagers, runScript, } from './package-manager.js';
6
- import { choose } from './choose.js';
7
- import { createProject, validateTargetDir } from './project.js';
8
- import { listTemplates } from './templates.js';
9
- import { nodeTooOld } from './node-version.js';
10
- /** Stops cleanly when the user presses Ctrl+C or Esc on a prompt. */
11
- function orExit(value) {
12
- if (p.isCancel(value)) {
13
- p.cancel('Cancelled, nothing was created.');
14
- process.exit(0);
15
- }
16
- return value;
17
- }
18
- // create-chapter and chapterjs are always released with the same version.
19
- const { version } = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
20
- p.intro(`Create a ChapterJS bot (v${version})`);
21
- const tooOld = nodeTooOld();
22
- if (tooOld) {
23
- p.cancel(tooOld);
24
- process.exit(1);
25
- }
26
- // Started now so it runs while the user answers the first question.
27
- const versionsCheck = installedVersions();
28
- // `pnpm create chapter my-bot` skips the first question when the folder is valid.
29
- const argDir = process.argv[2];
30
- const argError = argDir === undefined ? undefined : validateTargetDir(argDir);
31
- if (argError)
32
- p.log.warn(argError);
33
- const dir = argDir !== undefined && argError === undefined
34
- ? argDir.trim()
35
- : orExit(await p.text({
36
- message: 'Where should the project be created? (. for the current folder)',
37
- placeholder: 'my-bot',
38
- defaultValue: 'my-bot',
39
- validate: value => validateTargetDir(value || 'my-bot'),
40
- })).trim();
41
- const versions = await versionsCheck;
42
- const pm = orExit(await choose({
43
- message: 'Which package manager do you want to use?',
44
- initialValue: detectPackageManager(),
45
- choices: packageManagers.map(value => {
46
- const version = versions[value];
47
- return version === undefined
48
- ? { value, label: `${value} (not installed)`, disabled: true }
49
- : { value, hint: `v${version}` };
50
- }),
51
- onlyChoice: value => `Using ${value}, the only package manager installed`,
52
- }));
53
- if (pm === undefined) {
54
- p.cancel(`No package manager found. Install one of ${packageManagers.join(', ')}, then run this command again.`);
55
- process.exit(1);
56
- }
57
- const templates = await listTemplates();
58
- const template = orExit(await choose({
59
- message: 'Which template do you want to start from?',
60
- choices: templates.map(({ name, description }) => ({
61
- value: name,
62
- hint: description,
63
- })),
64
- onlyChoice: value => `Using the ${value} template`,
65
- }));
66
- if (template === undefined) {
67
- p.cancel('No template found. Reinstall create-chapter and try again.');
68
- process.exit(1);
69
- }
70
- await createProject({
71
- dir,
72
- template,
73
- chapterjsVersion: `^${version}`,
74
- packageManager: pm,
75
- });
76
- p.log.success(`Project created in ${resolve(dir)}`);
77
- const spinner = p.spinner();
78
- spinner.start(`Installing dependencies with ${pm}`);
79
- const result = await install(pm, dir);
80
- if (result.ok) {
81
- spinner.stop(`Dependencies installed with ${pm}`);
82
- }
83
- else {
84
- spinner.error(`The dependencies could not be installed with ${pm}`);
85
- if (result.output)
86
- p.log.message(result.output);
87
- }
88
- const cd = relative(process.cwd(), resolve(dir));
89
- const steps = [
90
- ...(cd ? [`cd ${cd.includes(' ') ? `"${cd}"` : cd}`] : []),
91
- ...(result.ok ? [] : [`${pm} install`]),
92
- 'Copy .env.example to .env and fill in BOT_TOKEN and DEV_GUILD_ID',
93
- runScript(pm, 'dev'),
94
- ];
95
- p.note(steps.join('\n'), 'Next steps');
96
- p.outro('Happy building!');
2
+ // Alias so `pnpm create chapter` works too: runs the create-chapterjs CLI.
3
+ // @ts-expect-error create-chapterjs only ships a bin, without type declarations.
4
+ await import('create-chapterjs/dist/index.js');
5
+ export {};
package/package.json CHANGED
@@ -1,21 +1,34 @@
1
1
  {
2
2
  "name": "create-chapter",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
+ "description": "Alias of create-chapterjs: creates a ChapterJS Discord bot with pnpm create chapter",
5
+ "keywords": [
6
+ "discord",
7
+ "discord-bot",
8
+ "bot",
9
+ "framework",
10
+ "typescript",
11
+ "slash-commands",
12
+ "esm",
13
+ "create",
14
+ "scaffold",
15
+ "template",
16
+ "chapterjs"
17
+ ],
4
18
  "license": "MIT",
5
19
  "repository": {
6
20
  "type": "git",
7
21
  "url": "git+https://github.com/chapterjs/chapterjs.git",
8
22
  "directory": "packages/create-chapter"
9
23
  },
10
- "homepage": "https://chapterjs.dev",
24
+ "homepage": "https://www.chapterjs.org",
11
25
  "bugs": "https://github.com/chapterjs/chapterjs/issues",
12
26
  "type": "module",
13
27
  "bin": {
14
28
  "create-chapter": "./dist/index.js"
15
29
  },
16
30
  "files": [
17
- "dist",
18
- "templates"
31
+ "dist"
19
32
  ],
20
33
  "devDependencies": {
21
34
  "@chapterjs/test-utils": "0.0.0",
@@ -25,7 +38,7 @@
25
38
  "vitest": "^5.0.3"
26
39
  },
27
40
  "dependencies": {
28
- "@clack/prompts": "^1.8.1"
41
+ "create-chapterjs": "^0.3.0"
29
42
  },
30
43
  "engines": {
31
44
  "node": ">=24"
package/dist/choose.js DELETED
@@ -1,27 +0,0 @@
1
- import * as p from '@clack/prompts';
2
- /**
3
- * A select menu that never asks a question with only one answer: when a single
4
- * choice can be picked, it is taken right away and the user is told why.
5
- * Returns `undefined` when no choice can be picked, and the cancel symbol when
6
- * the user presses Ctrl+C or Esc.
7
- */
8
- export async function choose({ message, choices, initialValue, onlyChoice, }) {
9
- const enabled = choices.filter(choice => !choice.disabled);
10
- const [first] = enabled;
11
- if (!first)
12
- return undefined;
13
- if (enabled.length === 1) {
14
- p.log.info(onlyChoice(first.value));
15
- return first.value;
16
- }
17
- // clack's option type can't be resolved for a generic `Value`, so the menu is
18
- // typed with `string`: it only ever returns one of `choices`.
19
- const picked = await p.select({
20
- message,
21
- initialValue: enabled.some(choice => choice.value === initialValue)
22
- ? initialValue
23
- : first.value,
24
- options: choices,
25
- });
26
- return picked;
27
- }
@@ -1,13 +0,0 @@
1
- /** The oldest Node.js a ChapterJS bot runs on, as `engines` says. */
2
- export const MINIMUM_NODE = 24;
3
- /**
4
- * What to tell the person when this Node.js is older than ChapterJS needs,
5
- * or `null` when it is recent enough: refused before creating anything,
6
- * since the bot could not run.
7
- */
8
- export function nodeTooOld(version = process.versions.node) {
9
- const major = Number(version.split('.')[0]);
10
- if (major >= MINIMUM_NODE)
11
- return null;
12
- return `ChapterJS needs Node.js ${MINIMUM_NODE} or newer, and this is Node.js ${version}. Install the LTS version from https://nodejs.org, then run the command again.`;
13
- }
@@ -1,59 +0,0 @@
1
- import { spawn } from 'node:child_process';
2
- import { tmpdir } from 'node:os';
3
- /** In menu order: pnpm, the one ChapterJS itself uses, comes first. */
4
- export const packageManagers = ['pnpm', 'npm', 'yarn', 'bun'];
5
- /**
6
- * The package manager that launched `create chapter`, read from the user agent
7
- * it sets (e.g. `pnpm/11.0.0 npm/? node/v24.0.0 darwin arm64`).
8
- */
9
- export function detectPackageManager() {
10
- const name = process.env.npm_config_user_agent?.split('/')[0];
11
- return packageManagers.find(pm => pm === name) ?? 'pnpm';
12
- }
13
- /**
14
- * The installed version of each package manager, or `undefined` when it isn't
15
- * installed. All are checked at once, so this takes as long as the slowest one.
16
- */
17
- export async function installedVersions() {
18
- const versions = await Promise.all(packageManagers.map(pm => new Promise(resolve => {
19
- let output = '';
20
- const child = spawn(pm, ['--version'], {
21
- // Outside the current folder: a `packageManager` field in a parent
22
- // package.json makes corepack refuse to run any other manager.
23
- cwd: tmpdir(),
24
- // Corepack must never stop to ask before downloading a manager.
25
- env: { ...process.env, COREPACK_ENABLE_DOWNLOAD_PROMPT: '0' },
26
- shell: process.platform === 'win32',
27
- stdio: ['ignore', 'pipe', 'ignore'],
28
- });
29
- child.stdout.on('data', (chunk) => (output += chunk));
30
- child.on('error', () => resolve(undefined));
31
- child.on('close', code => resolve(code === 0 ? output.trim() || undefined : undefined));
32
- })));
33
- return Object.fromEntries(packageManagers.map((pm, i) => [pm, versions[i]]));
34
- }
35
- /** The command that runs a package.json script, e.g. `pnpm dev` or `npm run dev`. */
36
- export function runScript(pm, script) {
37
- return pm === 'npm' ? `npm run ${script}` : `${pm} ${script}`;
38
- }
39
- /** Runs `<pm> install` in `cwd`, capturing the output instead of printing it. */
40
- export function install(pm, cwd) {
41
- return new Promise(resolve => {
42
- let output = '';
43
- const child = spawn(pm, ['install'], {
44
- cwd,
45
- // Package managers are `.cmd` shims on Windows, which only a shell can run.
46
- shell: process.platform === 'win32',
47
- stdio: ['ignore', 'pipe', 'pipe'],
48
- });
49
- child.stdout.on('data', (chunk) => (output += chunk));
50
- child.stderr.on('data', (chunk) => (output += chunk));
51
- child.on('error', (error) => {
52
- const reason = error.code === 'ENOENT'
53
- ? `${pm} is not installed on this computer.`
54
- : error.message;
55
- resolve({ ok: false, output: reason });
56
- });
57
- child.on('close', code => resolve({ ok: code === 0, output: output.trim() }));
58
- });
59
- }
package/dist/project.js DELETED
@@ -1,82 +0,0 @@
1
- import { existsSync, readdirSync, statSync } from 'node:fs';
2
- import { cp, mkdir, readFile, rename, writeFile } from 'node:fs/promises';
3
- import { basename, join, resolve } from 'node:path';
4
- import { templatesDir } from './templates.js';
5
- /** Files that don't make a folder "used": a project can still be created there. */
6
- const ignoredFiles = new Set([
7
- '.git',
8
- '.DS_Store',
9
- 'Thumbs.db',
10
- '.idea',
11
- '.vscode',
12
- ]);
13
- /** Returns why `input` can't hold a new project, or `undefined` when it can. */
14
- export function validateTargetDir(input) {
15
- const dir = input?.trim();
16
- if (!dir)
17
- return 'Type a folder name, or . to use the current folder.';
18
- const path = resolve(dir);
19
- if (!existsSync(path))
20
- return undefined;
21
- if (!statSync(path).isDirectory())
22
- return `${dir} is a file, not a folder. Pick another name.`;
23
- const used = readdirSync(path).some(file => !ignoredFiles.has(file));
24
- if (used) {
25
- return path === process.cwd()
26
- ? 'The current folder is not empty. Type a name to create a new folder instead.'
27
- : `The folder ${dir} is not empty. Pick another name.`;
28
- }
29
- return undefined;
30
- }
31
- /** A valid npm package name from a folder name, e.g. `Mon Bot Été` → `mon-bot-ete`. */
32
- export function toPackageName(dir) {
33
- const name = basename(resolve(dir))
34
- // `é` → `e`: split letters from their accents, then drop the accents.
35
- .normalize('NFD')
36
- .replace(/\p{Diacritic}/gu, '')
37
- .toLowerCase()
38
- .replace(/[^a-z0-9-~._]+/g, '-')
39
- .replace(/-{2,}/g, '-')
40
- // npm names can't start with `.` or `_`, and 214 characters is the maximum.
41
- .replace(/^[-._]+/, '')
42
- .slice(0, 214)
43
- .replace(/[-.]+$/, '');
44
- return name || 'chapter-bot';
45
- }
46
- /**
47
- * What pnpm needs to install a project whose framework comes with esbuild
48
- * (which `chapterjs build` compiles the bot with). esbuild has an install
49
- * script it does not need; pnpm refuses to go on until the project says whether that
50
- * script may run. It may not: no script of a dependency runs.
51
- * `allowBuilds` is read by pnpm 11, `ignoredBuiltDependencies` by pnpm 10.
52
- */
53
- export const PNPM_WORKSPACE = `# esbuild works without its install script: pnpm is told not to run it.
54
- allowBuilds:
55
- esbuild: false
56
- ignoredBuiltDependencies:
57
- - esbuild
58
- `;
59
- /** Copies the template into `dir` and names the project after its folder. */
60
- export async function createProject({ dir, template, chapterjsVersion, packageManager, templatesRoot = templatesDir, }) {
61
- const root = resolve(dir);
62
- await cp(join(templatesRoot, template), root, { recursive: true });
63
- // Where the files the bot sends go (`asset()`): git and npm keep no empty
64
- // folder, so no template can ship it; every project gets it here.
65
- await mkdir(join(root, 'public'), { recursive: true });
66
- // npm strips `.gitignore` from published packages, so templates ship it as `_gitignore`.
67
- const gitignore = join(root, '_gitignore');
68
- if (existsSync(gitignore))
69
- await rename(gitignore, join(root, '.gitignore'));
70
- const pkgPath = join(root, 'package.json');
71
- const pkg = JSON.parse(await readFile(pkgPath, 'utf8'));
72
- pkg.name = toPackageName(dir);
73
- // The template's description is for the menu, not for the user's bot.
74
- delete pkg.description;
75
- pkg.dependencies = { ...pkg.dependencies, chapterjs: chapterjsVersion };
76
- await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
77
- // Only for the package manager that reads it: nothing to explain to the
78
- // others.
79
- if (packageManager === 'pnpm') {
80
- await writeFile(join(root, 'pnpm-workspace.yaml'), PNPM_WORKSPACE);
81
- }
82
- }
package/dist/templates.js DELETED
@@ -1,31 +0,0 @@
1
- import { existsSync } from 'node:fs';
2
- import { readdir, readFile } from 'node:fs/promises';
3
- import { fileURLToPath } from 'node:url';
4
- import { join } from 'node:path';
5
- /** `templates/` ships next to `dist/` in the published package. */
6
- export const templatesDir = fileURLToPath(new URL('../templates/', import.meta.url));
7
- /**
8
- * Every folder of `templates/` with a package.json is a template: adding one
9
- * needs no code change.
10
- */
11
- export async function listTemplates(dir = templatesDir) {
12
- const entries = await readdir(dir, { withFileTypes: true });
13
- const templates = await Promise.all(entries
14
- .filter(entry => entry.isDirectory())
15
- .map(async (entry) => {
16
- const pkgPath = join(dir, entry.name, 'package.json');
17
- if (!existsSync(pkgPath))
18
- return undefined;
19
- const pkg = JSON.parse(await readFile(pkgPath, 'utf8'));
20
- const description = typeof pkg.description === 'string' ? pkg.description : undefined;
21
- return { name: entry.name, description };
22
- }));
23
- // `default` first, so it is the preselected choice.
24
- return templates
25
- .filter(template => template !== undefined)
26
- .sort((a, b) => a.name === 'default'
27
- ? -1
28
- : b.name === 'default'
29
- ? 1
30
- : a.name.localeCompare(b.name));
31
- }
@@ -1,4 +0,0 @@
1
- # Your bot token: https://discord.com/developers/applications → your app → Bot → Reset Token
2
- BOT_TOKEN=
3
- # The ID of the server used by `chapterjs dev` (right-click the server → Copy Server ID)
4
- DEV_GUILD_ID=
@@ -1,4 +0,0 @@
1
- node_modules
2
- dist
3
- .env
4
- .chapterjs
@@ -1,23 +0,0 @@
1
- {
2
- "name": "chapter-bot",
3
- "description": "A minimal bot, ready to grow",
4
- "private": true,
5
- "type": "module",
6
- "scripts": {
7
- "dev": "chapterjs dev",
8
- "start": "chapterjs start",
9
- "build": "chapterjs build",
10
- "typecheck": "tsc -b",
11
- "postinstall": "chapterjs sync"
12
- },
13
- "dependencies": {
14
- "chapterjs": "latest"
15
- },
16
- "devDependencies": {
17
- "@types/node": "^24.0.0",
18
- "typescript": "^7.0.2"
19
- },
20
- "engines": {
21
- "node": ">=24"
22
- }
23
- }
Binary file
@@ -1,23 +0,0 @@
1
- import { asset, command } from 'chapterjs';
2
-
3
- // /hello joins your voice channel and plays public/hello.ogg. An Opus file
4
- // (.ogg, .webm) plays as it is; an .mp3 or a link needs ffmpeg installed.
5
- export default command({
6
- name: 'hello',
7
- async run({ interaction, member, t }) {
8
- const channel = member.voiceChannel;
9
- if (!channel) {
10
- await interaction.reply({ content: t('joinVoice'), ephemeral: true });
11
- return;
12
- }
13
- // Answer first: a command has 3 seconds, joining takes a moment.
14
- await interaction.reply(t('hello', { channel: channel.name }));
15
- const voice = await channel.join();
16
- // Whatever happens while it plays, the bot leaves after.
17
- try {
18
- await voice.play(asset('hello.ogg'));
19
- } finally {
20
- await voice.leave();
21
- }
22
- },
23
- });
@@ -1,55 +0,0 @@
1
- import { button, command } from 'chapterjs';
2
-
3
- // A command is declared with command() and exported: `name` is what people
4
- // type after the /. Where the file is and what it is called are up to you:
5
- // this one could live anywhere in src/.
6
- export default command({
7
- name: 'ping',
8
- // Only the person sees the answer, so `t` speaks their language.
9
- ephemeral: true,
10
- async run({ interaction, t }) {
11
- // The button below, with the data it carries, and a text of
12
- // src/messages/ in the language of who will read the answer.
13
- await interaction.reply({
14
- content: t('pong'),
15
- components: [again({ count: 1 })],
16
- });
17
- },
18
- });
19
-
20
- // A button is declared with button() and exported: the name of the export
21
- // (here `again`) is what tells which one was clicked, you never write an
22
- // id. `data` is what the button carries from one click to the next.
23
- export const again = button({
24
- label: 'Again',
25
- data: { count: 'number' },
26
- async run({ interaction, data, t }) {
27
- const count = data.count + 1;
28
- // Changes the message the button is on, instead of sending a new one.
29
- await interaction.update({
30
- content: t('again', { count }),
31
- components: [again({ count })],
32
- });
33
- },
34
- });
35
-
36
- // A command can ask for options, typed for you in `run`, suggest values
37
- // while the person types, and make each person wait between two uses:
38
- //
39
- // export const hello = command({
40
- // name: 'hello',
41
- // description: 'Says hello to someone',
42
- // cooldown: '10s',
43
- // options: {
44
- // who: { type: 'user', description: 'Who to greet', required: true },
45
- // greeting: { type: 'string', description: 'How to greet' },
46
- // },
47
- // autocomplete: {
48
- // // Called while the person types in "greeting", with what they typed.
49
- // greeting: ({ value }) =>
50
- // ['Hello', 'Hi', 'Hey'].filter(word => word.startsWith(value)),
51
- // },
52
- // async run({ interaction, options }) {
53
- // await interaction.reply(`${options.greeting ?? 'Hello'} ${options.who}!`);
54
- // },
55
- // });
@@ -1,41 +0,0 @@
1
- import { command } from 'chapterjs';
2
-
3
- // /play plays a song in your voice channel: a file you send with the
4
- // command, or a link to an audio file or a stream. play() takes both.
5
- export default command({
6
- name: 'play',
7
- options: {
8
- file: { type: 'attachment' },
9
- url: { type: 'string' },
10
- },
11
- async run({ interaction, member, options, t }) {
12
- const { file, url } = options;
13
- const song = file ?? url;
14
- if (
15
- !song ||
16
- (file && !file.contentType?.startsWith('audio/')) ||
17
- (url && !/^https?:\/\//.test(url))
18
- ) {
19
- await interaction.reply({ content: t('nothingToPlay'), ephemeral: true });
20
- return;
21
- }
22
- const channel = member.voiceChannel;
23
- if (!channel) {
24
- await interaction.reply({ content: t('joinVoice'), ephemeral: true });
25
- return;
26
- }
27
- const name = file?.filename ?? url ?? '';
28
- await interaction.reply(
29
- t('playing', { song: name, channel: channel.name })
30
- );
31
- // Joining the channel the bot is already in changes nothing, and a new
32
- // play() replaces what was playing.
33
- const voice = await channel.join();
34
- // Whatever happens while it plays, the bot leaves after.
35
- try {
36
- await voice.play(song);
37
- } finally {
38
- await voice.leave();
39
- }
40
- },
41
- });
@@ -1,34 +0,0 @@
1
- import { event } from 'chapterjs';
2
-
3
- // An event is declared with event() and exported: `name` is the event, and
4
- // it types what `run` receives. This one runs once, when your bot is
5
- // connected. A file may declare several events, commands or buttons.
6
- export default event({
7
- name: 'ready',
8
- run({ user, guilds }) {
9
- console.log(`${user.username} is online in ${guilds.size} server(s)`);
10
- },
11
- });
12
-
13
- // Something to do by itself, every so often or at given times, is a task:
14
- // the name of its export is the name of the task.
15
- //
16
- // import { task } from 'chapterjs';
17
- //
18
- // export const report = task({
19
- // cron: '0 9 * * 1', // every Monday at 9:00
20
- // async run({ guilds }) {
21
- // console.log(`A new week in ${guilds.size} server(s)`);
22
- // },
23
- // });
24
- //
25
- // To react to something else, declare another event. For example, one that
26
- // answers "Pong!" to "!ping" (reading messages needs the Message Content
27
- // intent: `chapterjs dev` tells you how to enable it):
28
- //
29
- // export const pong = event({
30
- // name: 'messageCreate',
31
- // async run({ message }) {
32
- // if (message.content === '!ping') await message.reply('Pong!');
33
- // },
34
- // });
@@ -1,14 +0,0 @@
1
- import { event } from 'chapterjs';
2
-
3
- // Runs every time someone adds a reaction to a message of your server.
4
- // Reactions of bots, yours included, are left out: when yours waves back
5
- // below, this does not run again. Other events work the same way:
6
- // voiceJoin, pollVoteAdd, banAdd... (see the list of events in the docs).
7
- export default event({
8
- name: 'reactionAdd',
9
- async run({ emoji, channel, messageId }) {
10
- if (emoji.name !== '👋') return;
11
- const message = await channel.fetchMessage(messageId);
12
- await message.react('👋');
13
- },
14
- });
@@ -1,36 +0,0 @@
1
- import { language } from 'chapterjs';
2
-
3
- // A language is declared with language(): `locale` says which one. Each
4
- // function of your project receives `t`, which picks the language of who
5
- // will read the message: t('pong'), t('again', { count }).
6
- export default language({
7
- locale: 'en-US',
8
- // The language used when the one of a person or of a server is not here.
9
- default: true,
10
- texts: {
11
- pong: 'Pong! in en-US',
12
- again: 'Pong in en-US ! ×{count}',
13
- joinVoice: 'Join a voice channel first, then ask again.',
14
- hello: 'Coming to {channel}!',
15
- nothingToPlay:
16
- 'Send an audio file (.mp3, .ogg...) or a link starting with https://.',
17
- playing: 'Playing {song} in {channel}.',
18
- },
19
- // What Discord shows of each command, by its name: its description, and
20
- // the ones of its options. A command described here has none in its file.
21
- commands: {
22
- ping: {
23
- description: 'Replies with Pong!',
24
- },
25
- hello: {
26
- description: 'Says hello in your voice channel',
27
- },
28
- play: {
29
- description: 'Plays a song in your voice channel',
30
- options: {
31
- file: { description: 'A song to play' },
32
- url: { description: 'Or the link of a song or a stream' },
33
- },
34
- },
35
- },
36
- });
@@ -1,37 +0,0 @@
1
- import { language } from 'chapterjs';
2
-
3
- // Every language has the texts of the default one, with the same
4
- // {placeholders}: a missing one is reported when the file loads.
5
- export default language({
6
- locale: 'fr',
7
- texts: {
8
- pong: 'Pong! en fr',
9
- again: 'Pong en fr ! ×{count}',
10
- joinVoice: 'Rejoignez d’abord un salon vocal, puis redemandez.',
11
- hello: 'J’arrive dans {channel} !',
12
- nothingToPlay:
13
- 'Envoyez un fichier audio (.mp3, .ogg...) ou un lien qui commence par https://.',
14
- playing: 'Lecture de {song} dans {channel}.',
15
- },
16
- // What the bot answers by itself can be translated too, phrase by phrase:
17
- //
18
- // framework: {
19
- // command: 'commande',
20
- // failed: "Quelque chose s'est mal passé avec cette {what}.",
21
- // },
22
- commands: {
23
- ping: {
24
- description: 'Répond avec Pong !',
25
- },
26
- hello: {
27
- description: 'Dit bonjour dans votre salon vocal',
28
- },
29
- play: {
30
- description: 'Joue une musique dans votre salon vocal',
31
- options: {
32
- file: { description: 'Une musique à jouer' },
33
- url: { description: 'Ou le lien d’une musique ou d’un flux' },
34
- },
35
- },
36
- },
37
- });
@@ -1,8 +0,0 @@
1
- import { presence } from 'chapterjs';
2
-
3
- // What your bot shows under its name: its status, and what it is doing.
4
- // Save this file and chapterjs dev changes it at once.
5
- export default presence({
6
- status: 'online',
7
- activity: { type: 'playing', name: 'with ChapterJS' },
8
- });
@@ -1,20 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "module": "Preserve",
5
- "moduleResolution": "Bundler",
6
- "allowImportingTsExtensions": true,
7
- "erasableSyntaxOnly": true,
8
- "verbatimModuleSyntax": true,
9
- "noEmit": true,
10
- "strict": true,
11
- "skipLibCheck": true,
12
- "types": ["node"]
13
- },
14
- "files": [],
15
- "references": [
16
- {
17
- "path": "./.chapterjs"
18
- }
19
- ]
20
- }