create-chapter 0.2.2 → 0.2.3

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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.js +4 -1
  3. package/package.json +1 -1
  4. package/templates/community/.env.example +4 -0
  5. package/templates/community/_gitignore +5 -0
  6. package/templates/community/package.json +23 -0
  7. package/templates/community/src/messages.ts +49 -0
  8. package/templates/community/src/presence.ts +6 -0
  9. package/templates/community/src/roles/roles.ts +60 -0
  10. package/templates/community/src/stats/weekly.ts +50 -0
  11. package/templates/community/src/welcome/channel.ts +19 -0
  12. package/templates/community/src/welcome/welcome.ts +37 -0
  13. package/templates/community/tsconfig.json +20 -0
  14. package/templates/default/_gitignore +1 -0
  15. package/templates/default/package.json +1 -1
  16. package/templates/default/src/events/ready.ts +14 -20
  17. package/templates/moderation/.env.example +4 -0
  18. package/templates/moderation/_gitignore +5 -0
  19. package/templates/moderation/package.json +23 -0
  20. package/templates/moderation/src/moderation/channel.ts +67 -0
  21. package/templates/moderation/src/moderation/log.ts +37 -0
  22. package/templates/moderation/src/moderation/messages.ts +158 -0
  23. package/templates/moderation/src/moderation/purge.ts +30 -0
  24. package/templates/moderation/src/moderation/timeout.ts +82 -0
  25. package/templates/moderation/src/moderation/warn.ts +61 -0
  26. package/templates/moderation/src/presence.ts +6 -0
  27. package/templates/moderation/tsconfig.json +20 -0
  28. package/templates/music/.env.example +4 -0
  29. package/templates/music/_gitignore +5 -0
  30. package/templates/music/package.json +23 -0
  31. package/templates/music/src/music/leave.ts +18 -0
  32. package/templates/music/src/music/messages.ts +83 -0
  33. package/templates/music/src/music/play.ts +120 -0
  34. package/templates/music/src/music/queue-command.ts +28 -0
  35. package/templates/music/src/music/queue.ts +61 -0
  36. package/templates/music/src/presence.ts +6 -0
  37. package/templates/music/tsconfig.json +20 -0
  38. package/templates/tickets/.env.example +4 -0
  39. package/templates/tickets/_gitignore +5 -0
  40. package/templates/tickets/package.json +23 -0
  41. package/templates/tickets/src/presence.ts +6 -0
  42. package/templates/tickets/src/tickets/close.ts +39 -0
  43. package/templates/tickets/src/tickets/messages.ts +68 -0
  44. package/templates/tickets/src/tickets/open.ts +81 -0
  45. package/templates/tickets/src/tickets/panel.ts +35 -0
  46. package/templates/tickets/tsconfig.json +20 -0
  47. package/templates/default/public/hello.ogg +0 -0
  48. package/templates/default/src/commands/hello.ts +0 -23
  49. package/templates/default/src/commands/ping.ts +0 -55
  50. package/templates/default/src/commands/play.ts +0 -41
  51. package/templates/default/src/events/wave.ts +0 -14
  52. package/templates/default/src/messages/en-US.ts +0 -36
  53. package/templates/default/src/messages/fr.ts +0 -37
  54. package/templates/default/src/presence.ts +0 -8
@@ -0,0 +1,61 @@
1
+ import type { Attachment, Guild, VoiceConnection } from 'chapterjs';
2
+
3
+ // Plain code: this file declares nothing, so the framework leaves it alone.
4
+ // It keeps the queue of each server and plays it, song after song.
5
+
6
+ export interface Song {
7
+ /** What play() takes: a file sent on Discord, or a link. */
8
+ source: Attachment | string;
9
+ title: string;
10
+ /** Who asked for it: a mention. */
11
+ requestedBy: string;
12
+ }
13
+
14
+ interface Queue {
15
+ songs: Song[];
16
+ current: Song | null;
17
+ }
18
+
19
+ const queues = new Map<string, Queue>();
20
+
21
+ /** The queue of a server, made on first use. */
22
+ export function queueOf(guild: Guild): Queue {
23
+ let queue = queues.get(guild.id);
24
+ if (!queue) {
25
+ queue = { songs: [], current: null };
26
+ queues.set(guild.id, queue);
27
+ }
28
+ return queue;
29
+ }
30
+
31
+ /**
32
+ * Plays the queue of a server until it is empty, then leaves the channel.
33
+ * Called once when the first song is added; a song added meanwhile is
34
+ * picked up by the loop. A song that can't be played (a dead link, ffmpeg
35
+ * missing) is skipped and said in the channel.
36
+ */
37
+ export async function playQueue(
38
+ guild: Guild,
39
+ voice: VoiceConnection,
40
+ say: (text: string) => Promise<unknown>
41
+ ): Promise<void> {
42
+ const queue = queueOf(guild);
43
+ if (queue.current) return;
44
+ let song: Song | undefined;
45
+ while ((song = queue.songs.shift())) {
46
+ queue.current = song;
47
+ try {
48
+ await voice.play(song.source);
49
+ } catch (error) {
50
+ await say(`⚠ ${song.title}: ${(error as Error).message}`);
51
+ }
52
+ }
53
+ queue.current = null;
54
+ // /leave may have made the bot leave already.
55
+ if (voice.connected) await voice.leave();
56
+ }
57
+
58
+ /** Empties the queue of a server: when the bot stops or leaves. */
59
+ export function clearQueue(guild: Guild): void {
60
+ queueOf(guild).songs.length = 0;
61
+ }
@@ -0,0 +1,6 @@
1
+ import { presence } from 'chapterjs';
2
+
3
+ // What the bot shows under its name.
4
+ export default presence({
5
+ activity: { type: 'listening', name: '/play' },
6
+ });
@@ -0,0 +1,20 @@
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
+ }
@@ -0,0 +1,4 @@
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=
@@ -0,0 +1,5 @@
1
+ node_modules
2
+ dist
3
+ .env
4
+ .chapterjs
5
+ data
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "ticket-bot",
3
+ "description": "Support tickets in private threads: a panel, a form, a close button",
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
+ }
@@ -0,0 +1,6 @@
1
+ import { presence } from 'chapterjs';
2
+
3
+ // What the bot shows under its name.
4
+ export default presence({
5
+ activity: { type: 'watching', name: 'the tickets' },
6
+ });
@@ -0,0 +1,39 @@
1
+ import { button } from 'chapterjs';
2
+ import { tickets } from './open';
3
+
4
+ // The button at the top of every ticket. Everyone in the thread sees it;
5
+ // the code decides who may use it: the staff, or who opened the ticket.
6
+ // `data` is what the button carries from the message to the click: here
7
+ // who opened the ticket, kept by Discord in the button itself.
8
+ export const close = button({
9
+ label: ({ t }) => t('closeButton'),
10
+ style: 'danger',
11
+ data: { userId: 'string' },
12
+ async run({ interaction, data, channel, member, guild, t }) {
13
+ if (!channel.isThread()) {
14
+ await interaction.reply({ content: t('notATicket'), ephemeral: true });
15
+ return;
16
+ }
17
+ const staff = member.permissions.has('ManageThreads');
18
+ if (!staff && member.id !== data.userId) {
19
+ await interaction.reply({ content: t('notYours'), ephemeral: true });
20
+ return;
21
+ }
22
+ // Said in the thread first: archiving it closes it for everyone.
23
+ await interaction.reply(t('closing', { user: member.toString() }));
24
+ await channel.edit(
25
+ { locked: true, archived: true },
26
+ `Closed by ${member.user.username}`
27
+ );
28
+ // The person may open another one.
29
+ await tickets.delete(data.userId);
30
+ // Who opened it is told in private, unless they closed it themselves.
31
+ // A person who refuses private messages is not an error.
32
+ if (member.id !== data.userId) {
33
+ const opener = await guild.fetchMember(data.userId).catch(() => null);
34
+ await opener
35
+ ?.send(t('closedDm', { ticket: channel.name, server: guild.name }))
36
+ .catch(() => {});
37
+ }
38
+ },
39
+ });
@@ -0,0 +1,68 @@
1
+ import { language } from 'chapterjs';
2
+
3
+ // The texts of the bot, one language() per language. `t` picks the one of
4
+ // who will read the message: the person for a private answer, the server
5
+ // otherwise. `commands` is what Discord shows of each command.
6
+ export const en = language({
7
+ locale: 'en-US',
8
+ default: true,
9
+ texts: {
10
+ panelTitle: 'Need help?',
11
+ panelText:
12
+ 'Open a ticket and the staff will answer you in a private thread.',
13
+ panelPosted: 'The panel is posted.',
14
+ openButton: 'Open a ticket',
15
+ formTitle: 'New ticket',
16
+ formSubject: 'Subject',
17
+ formCategory: 'What is it about?',
18
+ formDetails: 'Tell us more',
19
+ notHere: 'Tickets can only be opened from a text channel.',
20
+ opened: 'Thanks {user}, the staff will be with you shortly.',
21
+ created: 'Your ticket is open: {thread}',
22
+ alreadyOpen: 'You already have a ticket open: {thread}. Close it first.',
23
+ category: 'Category',
24
+ openedBy: 'Opened by',
25
+ closeButton: 'Close the ticket',
26
+ notATicket: 'This button only works inside a ticket.',
27
+ notYours: 'Only the staff, or who opened this ticket, can close it.',
28
+ closing: 'Ticket closed by {user}.',
29
+ closedDm: 'Your ticket "{ticket}" on {server} was closed.',
30
+ },
31
+ commands: {
32
+ 'ticket panel': {
33
+ description: 'Posts the message people open tickets from',
34
+ },
35
+ },
36
+ });
37
+
38
+ export const fr = language({
39
+ locale: 'fr',
40
+ texts: {
41
+ panelTitle: 'Besoin d’aide ?',
42
+ panelText: 'Ouvrez un ticket et le staff vous répondra dans un fil privé.',
43
+ panelPosted: 'Le panneau est posté.',
44
+ openButton: 'Ouvrir un ticket',
45
+ formTitle: 'Nouveau ticket',
46
+ formSubject: 'Sujet',
47
+ formCategory: 'De quoi s’agit-il ?',
48
+ formDetails: 'Dites-nous en plus',
49
+ notHere: 'Un ticket ne peut être ouvert que depuis un salon textuel.',
50
+ opened: 'Merci {user}, le staff arrive.',
51
+ created: 'Votre ticket est ouvert : {thread}',
52
+ alreadyOpen:
53
+ 'Vous avez déjà un ticket ouvert : {thread}. Fermez-le d’abord.',
54
+ category: 'Catégorie',
55
+ openedBy: 'Ouvert par',
56
+ closeButton: 'Fermer le ticket',
57
+ notATicket: 'Ce bouton ne fonctionne que dans un ticket.',
58
+ notYours:
59
+ 'Seul le staff, ou la personne qui a ouvert ce ticket, peut le fermer.',
60
+ closing: 'Ticket fermé par {user}.',
61
+ closedDm: 'Votre ticket « {ticket} » sur {server} a été fermé.',
62
+ },
63
+ commands: {
64
+ 'ticket panel': {
65
+ description: 'Poste le message depuis lequel on ouvre un ticket',
66
+ },
67
+ },
68
+ });
@@ -0,0 +1,81 @@
1
+ import { ChannelType, modal, store } from 'chapterjs';
2
+ import { close } from './close';
3
+
4
+ // The ticket each person has open: the id of its thread, by person. A
5
+ // store keeps data from one restart to the next, per server: in this
6
+ // server, `tickets.get(member.id)` is the ticket of that member here.
7
+ export const tickets = store<string>();
8
+
9
+ // The form a ticket starts with. `run` receives the fields typed from
10
+ // their kind: `subject` and `details` are strings, `category` is one of
11
+ // the three values below.
12
+ export const openTicket = modal({
13
+ title: ({ t }) => t('formTitle'),
14
+ fields: {
15
+ subject: {
16
+ type: 'text',
17
+ label: ({ t }) => t('formSubject'),
18
+ style: 'short',
19
+ maxLength: 80,
20
+ },
21
+ category: {
22
+ type: 'radio',
23
+ label: ({ t }) => t('formCategory'),
24
+ options: { Bug: 'bug', Question: 'question', Other: 'other' },
25
+ },
26
+ details: {
27
+ type: 'text',
28
+ label: ({ t }) => t('formDetails'),
29
+ style: 'paragraph',
30
+ minLength: 20,
31
+ maxLength: 1000,
32
+ },
33
+ },
34
+ async run({ interaction, fields, channel, member, t }) {
35
+ if (!channel.isText()) {
36
+ await interaction.reply({ content: t('notHere'), ephemeral: true });
37
+ return;
38
+ }
39
+ // One ticket at a time per person.
40
+ const open = await tickets.get(member.id);
41
+ if (open) {
42
+ await interaction.reply({
43
+ content: t('alreadyOpen', { thread: `<#${open}>` }),
44
+ ephemeral: true,
45
+ });
46
+ return;
47
+ }
48
+ // A private thread: only the people added to it, and the staff, see it.
49
+ const thread = await channel.startThread(
50
+ {
51
+ name: `${fields.category}-${member.displayName}`.slice(0, 100),
52
+ type: ChannelType.PrivateThread,
53
+ invitable: false,
54
+ },
55
+ `Ticket opened by ${member.user.username}`
56
+ );
57
+ await thread.addMember(member);
58
+ await tickets.set(member.id, thread.id);
59
+ await thread.send({
60
+ content: t('opened', { user: member.toString() }),
61
+ embeds: [
62
+ {
63
+ title: fields.subject,
64
+ description: fields.details,
65
+ color: 0xc026d3,
66
+ fields: [
67
+ { name: t('category'), value: fields.category, inline: true },
68
+ { name: t('openedBy'), value: member.toString(), inline: true },
69
+ ],
70
+ },
71
+ ],
72
+ // The close button carries who opened the ticket, so it knows who may
73
+ // close it besides the staff, and who to tell when it is closed.
74
+ components: [close({ userId: member.id })],
75
+ });
76
+ await interaction.reply({
77
+ content: t('created', { thread: thread.toString() }),
78
+ ephemeral: true,
79
+ });
80
+ },
81
+ });
@@ -0,0 +1,35 @@
1
+ import { button, command } from 'chapterjs';
2
+ import { openTicket } from './open';
3
+
4
+ // /ticket panel posts the message people open a ticket from: an embed and
5
+ // a button. Only the staff can post it (`permissions`); Discord hides the
6
+ // command from everyone else. `t` speaks the language of the server.
7
+ export default command({
8
+ name: 'ticket panel',
9
+ permissions: ['ManageThreads'],
10
+ async run({ interaction, channel, t }) {
11
+ await channel.send({
12
+ embeds: [
13
+ {
14
+ title: t('panelTitle'),
15
+ description: t('panelText'),
16
+ color: 0xc026d3,
17
+ },
18
+ ],
19
+ components: [open],
20
+ });
21
+ await interaction.reply({ content: t('panelPosted'), ephemeral: true });
22
+ },
23
+ });
24
+
25
+ // A button is named by its export (`open`): nothing to register, no id to
26
+ // write. Its label is a function of `t`, run when the message is sent.
27
+ export const open = button({
28
+ label: ({ t }) => t('openButton'),
29
+ style: 'primary',
30
+ emoji: '🎫',
31
+ async run({ interaction }) {
32
+ // A form must be the first answer to a click.
33
+ await interaction.showModal(openTicket);
34
+ },
35
+ });
@@ -0,0 +1,20 @@
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
+ }
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,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
- });