feat: astro guide (#8714)
12
.github/labeler.yml
vendored
@@ -14,6 +14,10 @@
|
|||||||
- packages/docgen/*
|
- packages/docgen/*
|
||||||
- packages/docgen/**/*
|
- packages/docgen/**/*
|
||||||
|
|
||||||
|
'packages:guide':
|
||||||
|
- packages/guide/*
|
||||||
|
- packages/guide/**/*
|
||||||
|
|
||||||
'packages:proxy':
|
'packages:proxy':
|
||||||
- packages/proxy/*
|
- packages/proxy/*
|
||||||
- packages/proxy/**/*
|
- packages/proxy/**/*
|
||||||
@@ -26,6 +30,10 @@
|
|||||||
- packages/rest/*
|
- packages/rest/*
|
||||||
- packages/rest/**/*
|
- packages/rest/**/*
|
||||||
|
|
||||||
|
'packages:util':
|
||||||
|
- packages/util/*
|
||||||
|
- packages/util/**/*
|
||||||
|
|
||||||
'packages:voice':
|
'packages:voice':
|
||||||
- packages/voice/*
|
- packages/voice/*
|
||||||
- packages/voice/**/*
|
- packages/voice/**/*
|
||||||
@@ -37,7 +45,3 @@
|
|||||||
'packages:ws':
|
'packages:ws':
|
||||||
- packages/ws/*
|
- packages/ws/*
|
||||||
- packages/ws/**/*
|
- packages/ws/**/*
|
||||||
|
|
||||||
'packages:util':
|
|
||||||
- packages/util/*
|
|
||||||
- packages/util/**/*
|
|
||||||
|
|||||||
6
.github/labels.yml
vendored
@@ -54,20 +54,22 @@
|
|||||||
color: 'fbca04'
|
color: 'fbca04'
|
||||||
- name: 'packages:docgen'
|
- name: 'packages:docgen'
|
||||||
color: 'fbca04'
|
color: 'fbca04'
|
||||||
|
- name: 'packages:guide'
|
||||||
|
color: 'fbca04'
|
||||||
- name: 'packages:proxy'
|
- name: 'packages:proxy'
|
||||||
color: 'fbca04'
|
color: 'fbca04'
|
||||||
- name: 'packages:proxy-container'
|
- name: 'packages:proxy-container'
|
||||||
color: 'fbca04'
|
color: 'fbca04'
|
||||||
- name: 'packages:rest'
|
- name: 'packages:rest'
|
||||||
color: 'fbca04'
|
color: 'fbca04'
|
||||||
|
- name: 'packages:util'
|
||||||
|
color: 'fbca04'
|
||||||
- name: 'packages:voice'
|
- name: 'packages:voice'
|
||||||
color: 'fbca04'
|
color: 'fbca04'
|
||||||
- name: 'packages:website'
|
- name: 'packages:website'
|
||||||
color: 'fbca04'
|
color: 'fbca04'
|
||||||
- name: 'packages:ws'
|
- name: 'packages:ws'
|
||||||
color: 'fbca04'
|
color: 'fbca04'
|
||||||
- name: 'packages:util'
|
|
||||||
color: 'fbca04'
|
|
||||||
- name: 'performance'
|
- name: 'performance'
|
||||||
color: '80c042'
|
color: '80c042'
|
||||||
- name: 'permissions'
|
- name: 'permissions'
|
||||||
|
|||||||
3
.vscode/extensions.json
vendored
@@ -8,6 +8,7 @@
|
|||||||
"eamodio.gitlens",
|
"eamodio.gitlens",
|
||||||
"christian-kohler.npm-intellisense",
|
"christian-kohler.npm-intellisense",
|
||||||
"christian-kohler.path-intellisense",
|
"christian-kohler.path-intellisense",
|
||||||
"antfu.unocss"
|
"antfu.unocss",
|
||||||
|
"astro-build.astro-vscode"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
5
.vscode/settings.json
vendored
@@ -1,11 +1,16 @@
|
|||||||
{
|
{
|
||||||
"eslint.workingDirectories": [{ "pattern": "./packages/*" }],
|
"eslint.workingDirectories": [{ "pattern": "./packages/*" }],
|
||||||
|
"eslint.validate": ["javascript", "javascriptreact", "astro", "typescript", "typescriptreact"],
|
||||||
|
"prettier.documentSelectors": ["**/*.astro"],
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll": true,
|
"source.fixAll": true,
|
||||||
"source.organizeImports": false
|
"source.organizeImports": false
|
||||||
},
|
},
|
||||||
|
"files.associations": {
|
||||||
|
"*.mdx": "markdown"
|
||||||
|
},
|
||||||
"unocss.root": "./packages/website",
|
"unocss.root": "./packages/website",
|
||||||
"typescript.tsdk": "node_modules/typescript/lib"
|
"typescript.tsdk": "node_modules/typescript/lib"
|
||||||
}
|
}
|
||||||
|
|||||||
36
packages/guide/.eslintrc.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"extends": ["../../.eslintrc.json", "neon/react", "neon/prettier"],
|
||||||
|
"settings": {
|
||||||
|
"react": {
|
||||||
|
"version": "detect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"extraFileExtensions": [".astro"]
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.astro"],
|
||||||
|
"plugins": ["astro"],
|
||||||
|
"env": {
|
||||||
|
"astro/astro": true
|
||||||
|
},
|
||||||
|
"parser": "astro-eslint-parser",
|
||||||
|
"rules": {
|
||||||
|
"deprecation/deprecation": 0,
|
||||||
|
"react/jsx-key": 0,
|
||||||
|
"react/no-unknown-property": 0,
|
||||||
|
"react/self-closing-comp": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["**/*.astro/*.js", "*.astro/*.js"],
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"react/react-in-jsx-scope": 0,
|
||||||
|
"react/jsx-filename-extension": [1, { "extensions": [".tsx", ".astro"] }]
|
||||||
|
}
|
||||||
|
}
|
||||||
30
packages/guide/.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Packages
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
|
||||||
|
# Env
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Dist
|
||||||
|
dist/
|
||||||
|
typings/
|
||||||
|
.cache/
|
||||||
|
build/
|
||||||
|
api/
|
||||||
|
src/styles/unocss.css
|
||||||
|
.next/
|
||||||
|
|
||||||
|
# Miscellaneous
|
||||||
|
.tmp/
|
||||||
|
coverage/
|
||||||
|
.vercel
|
||||||
|
public/searchIndex
|
||||||
1
packages/guide/.lintstagedrc.cjs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports = require('../../.lintstagedrc.json');
|
||||||
15
packages/guide/.prettierignore
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Autogenerated
|
||||||
|
CHANGELOG.md
|
||||||
|
.turbo
|
||||||
|
dist/
|
||||||
|
docs/**/*
|
||||||
|
!docs/index.yml
|
||||||
|
!docs/README.md
|
||||||
|
coverage/
|
||||||
|
.cache
|
||||||
|
build/
|
||||||
|
src/styles/unocss.css
|
||||||
|
api/
|
||||||
|
.next/
|
||||||
|
.vercel/
|
||||||
|
.cache/
|
||||||
1
packages/guide/.prettierrc.cjs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports = require('../../.prettierrc.json');
|
||||||
43
packages/guide/astro.config.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { fileURLToPath, URL } from 'node:url';
|
||||||
|
import image from '@astrojs/image';
|
||||||
|
import mdx from '@astrojs/mdx';
|
||||||
|
import react from '@astrojs/react';
|
||||||
|
import { remarkCodeHike } from '@code-hike/mdx';
|
||||||
|
import { defineConfig } from 'astro/config';
|
||||||
|
import shikiThemeDarkPlus from 'shiki/themes/dark-plus.json' assert { type: 'json' };
|
||||||
|
import Unocss from 'unocss/astro';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
integrations: [
|
||||||
|
react(),
|
||||||
|
mdx(),
|
||||||
|
image({
|
||||||
|
serviceEntryPoint: '@astrojs/image/sharp',
|
||||||
|
}),
|
||||||
|
Unocss(),
|
||||||
|
],
|
||||||
|
markdown: {
|
||||||
|
remarkPlugins: [[remarkCodeHike, { autoImport: false, theme: shikiThemeDarkPlus, lineNumbers: true }]],
|
||||||
|
rehypePlugins: [],
|
||||||
|
extendDefaultPlugins: true,
|
||||||
|
syntaxHighlight: false,
|
||||||
|
},
|
||||||
|
vite: {
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'ariakit/button': fileURLToPath(new URL('node_modules/ariakit/esm/button/index.js', import.meta.url)),
|
||||||
|
'ariakit/disclosure': fileURLToPath(new URL('node_modules/ariakit/esm/disclosure/index.js', import.meta.url)),
|
||||||
|
'ariakit-utils/dom': fileURLToPath(new URL('node_modules/ariakit-utils/esm/dom.js', import.meta.url)),
|
||||||
|
'ariakit-utils/events': fileURLToPath(new URL('node_modules/ariakit-utils/esm/events.js', import.meta.url)),
|
||||||
|
'ariakit-utils/focus': fileURLToPath(new URL('node_modules/ariakit-utils/esm/focus.js', import.meta.url)),
|
||||||
|
'ariakit-utils/hooks': fileURLToPath(new URL('node_modules/ariakit-utils/esm/hooks.js', import.meta.url)),
|
||||||
|
'ariakit-utils/misc': fileURLToPath(new URL('node_modules/ariakit-utils/esm/misc.js', import.meta.url)),
|
||||||
|
'ariakit-utils/platform': fileURLToPath(new URL('node_modules/ariakit-utils/esm/platform.js', import.meta.url)),
|
||||||
|
'ariakit-utils/system': fileURLToPath(new URL('node_modules/ariakit-utils/esm/system.js', import.meta.url)),
|
||||||
|
'react-icons/fi': fileURLToPath(new URL('node_modules/react-icons/fi/index.esm.js', import.meta.url)),
|
||||||
|
'react-icons/vsc': fileURLToPath(new URL('node_modules/react-icons/vsc/index.esm.js', import.meta.url)),
|
||||||
|
'react-use': fileURLToPath(new URL('node_modules/react-use/esm/index.js', import.meta.url)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
86
packages/guide/package.json
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
{
|
||||||
|
"name": "@discordjs/guide",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"test": "vitest run",
|
||||||
|
"build:prod": "astro build",
|
||||||
|
"dev": "astro dev",
|
||||||
|
"preview": "astro preview",
|
||||||
|
"lint": "prettier --check . && cross-env TIMING=1 eslint src --ext mjs,js,ts,tsx,astro",
|
||||||
|
"format": "prettier --write . && cross-env TIMING=1 eslint src --ext mjs,js,ts,tsx,astro --fix"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"contributors": [
|
||||||
|
"Crawl <icrawltogo@gmail.com>"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"keywords": [
|
||||||
|
"discord",
|
||||||
|
"api",
|
||||||
|
"bot",
|
||||||
|
"client",
|
||||||
|
"node",
|
||||||
|
"discordapp",
|
||||||
|
"discordjs"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/discordjs/discord.js.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/discordjs/discord.js/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://discord.js.org",
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/image": "^0.9.1",
|
||||||
|
"@astrojs/mdx": "^0.11.4",
|
||||||
|
"@astrojs/react": "^1.1.4",
|
||||||
|
"@code-hike/mdx": "^0.7.4",
|
||||||
|
"@vscode/codicons": "^0.0.32",
|
||||||
|
"ariakit": "^2.0.0-next.41",
|
||||||
|
"astro": "^1.4.4",
|
||||||
|
"cmdk": "^0.1.20",
|
||||||
|
"meilisearch": "^0.28.0",
|
||||||
|
"react": "^17.0.2",
|
||||||
|
"react-custom-scrollbars-2": "^4.5.0",
|
||||||
|
"react-dom": "^17.0.2",
|
||||||
|
"react-icons": "^4.4.0",
|
||||||
|
"react-syntax-highlighter": "^15.5.0",
|
||||||
|
"react-use": "^17.4.0",
|
||||||
|
"rehype-ignore": "^1.0.1",
|
||||||
|
"rehype-pretty-code": "^0.3.2",
|
||||||
|
"rehype-raw": "^6.1.1",
|
||||||
|
"sharp": "^0.31.1",
|
||||||
|
"shiki": "^0.11.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@testing-library/react": "^13.4.0",
|
||||||
|
"@testing-library/user-event": "^14.4.3",
|
||||||
|
"@types/node": "^16.11.64",
|
||||||
|
"@types/react-dom": "^17.0.2",
|
||||||
|
"@types/react-syntax-highlighter": "^15.5.5",
|
||||||
|
"@unocss/cli": "^0.45.26",
|
||||||
|
"@unocss/reset": "^0.45.26",
|
||||||
|
"@vitejs/plugin-react": "^2.1.0",
|
||||||
|
"@vitest/coverage-c8": "^0.23.4",
|
||||||
|
"astro-eslint-parser": "^0.6.3",
|
||||||
|
"concurrently": "^7.4.0",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
|
"eslint": "^8.24.0",
|
||||||
|
"eslint-config-neon": "^0.1.34",
|
||||||
|
"eslint-plugin-astro": "^0.19.0",
|
||||||
|
"happy-dom": "^7.0.0",
|
||||||
|
"prettier": "^2.7.1",
|
||||||
|
"prettier-plugin-astro": "^0.5.5",
|
||||||
|
"prettier-plugin-tailwindcss": "^0.1.13",
|
||||||
|
"typescript": "^4.8.4",
|
||||||
|
"unocss": "^0.45.26",
|
||||||
|
"vercel": "^28.4.6",
|
||||||
|
"vitest": "^0.23.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.9.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
packages/guide/public/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
packages/guide/public/android-chrome-384x384.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
packages/guide/public/apple-touch-icon-120x120-precomposed.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
packages/guide/public/apple-touch-icon-120x120.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
packages/guide/public/apple-touch-icon-152x152-precomposed.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
packages/guide/public/apple-touch-icon-152x152.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
packages/guide/public/apple-touch-icon-180x180-precomposed.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
packages/guide/public/apple-touch-icon-180x180.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
packages/guide/public/apple-touch-icon-60x60-precomposed.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
packages/guide/public/apple-touch-icon-60x60.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
packages/guide/public/apple-touch-icon-76x76-precomposed.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
packages/guide/public/apple-touch-icon-76x76.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
packages/guide/public/apple-touch-icon-precomposed.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
packages/guide/public/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
12
packages/guide/public/browserconfig.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<browserconfig>
|
||||||
|
<msapplication>
|
||||||
|
<tile>
|
||||||
|
<square70x70logo src="/mstile-70x70.png"/>
|
||||||
|
<square150x150logo src="/mstile-150x150.png"/>
|
||||||
|
<square310x310logo src="/mstile-310x310.png"/>
|
||||||
|
<wide310x150logo src="/mstile-310x150.png"/>
|
||||||
|
<TileColor>#090a16</TileColor>
|
||||||
|
</tile>
|
||||||
|
</msapplication>
|
||||||
|
</browserconfig>
|
||||||
BIN
packages/guide/public/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 561 B |
BIN
packages/guide/public/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
packages/guide/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
packages/guide/public/mstile-150x150.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
packages/guide/public/mstile-310x150.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
packages/guide/public/mstile-310x310.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
packages/guide/public/mstile-70x70.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
packages/guide/public/open-graph.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
6
packages/guide/public/powered-by-vercel.svg
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
32
packages/guide/public/safari-pinned-tab.svg
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="400.000000pt" height="400.000000pt" viewBox="0 0 400.000000 400.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
<metadata>
|
||||||
|
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||||
|
</metadata>
|
||||||
|
<g transform="translate(0.000000,400.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#000000" stroke="none">
|
||||||
|
<path d="M0 2000 l0 -2000 2000 0 2000 0 0 2000 0 2000 -2000 0 -2000 0 0
|
||||||
|
-2000z m1305 795 c219 -47 373 -197 421 -411 24 -109 16 -315 -16 -399 l-23
|
||||||
|
-61 -59 -11 c-110 -21 -182 -92 -203 -202 -11 -56 -11 -56 -68 -79 -76 -30
|
||||||
|
-201 -42 -464 -42 l-223 0 0 610 0 610 283 0 c210 0 300 -4 352 -15z m890
|
||||||
|
-585 c0 -543 -2 -601 -18 -659 -56 -198 -190 -334 -365 -370 -176 -37 -349 5
|
||||||
|
-471 115 -48 44 -111 134 -111 161 0 6 19 14 43 18 23 4 73 18 110 32 l68 24
|
||||||
|
50 -45 c56 -50 98 -66 175 -66 100 0 207 81 234 178 6 23 10 251 10 625 l0
|
||||||
|
588 138 -3 137 -3 0 -595z m930 570 c93 -29 197 -84 252 -134 l44 -41 -61 -75
|
||||||
|
c-34 -41 -67 -81 -74 -88 -10 -10 -25 -4 -78 32 -95 63 -154 81 -268 81 -79 0
|
||||||
|
-103 -4 -142 -23 -62 -31 -90 -71 -96 -137 -8 -94 28 -137 168 -202 157 -73
|
||||||
|
172 -80 240 -108 236 -97 341 -215 356 -400 16 -193 -82 -363 -258 -449 -91
|
||||||
|
-45 -187 -66 -303 -66 -191 0 -390 74 -524 195 l-40 37 80 93 80 94 73 -53
|
||||||
|
c168 -121 353 -155 491 -92 72 33 108 82 113 155 9 122 -37 162 -323 286 -258
|
||||||
|
111 -350 179 -407 300 -69 148 -37 343 75 463 61 65 195 133 300 152 79 14
|
||||||
|
222 4 302 -20z m-1367 -977 c45 -34 65 -69 70 -125 4 -61 -19 -106 -77 -145
|
||||||
|
-90 -60 -226 3 -238 111 -3 24 -3 56 1 70 8 34 54 86 91 102 40 19 120 12 153
|
||||||
|
-13z"/>
|
||||||
|
<path d="M967 2543 c-4 -3 -7 -159 -7 -346 l0 -339 128 4 c101 3 136 8 172 24
|
||||||
|
106 48 162 145 168 292 10 214 -69 334 -238 361 -73 12 -213 15 -223 4z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
19
packages/guide/public/site.webmanifest
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "discord.js guide",
|
||||||
|
"short_name": "discord.js guide",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/android-chrome-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/android-chrome-384x384.png",
|
||||||
|
"sizes": "384x384",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"theme_color": "#1a1b1e",
|
||||||
|
"background_color": "#1a1b1e",
|
||||||
|
"display": "standalone"
|
||||||
|
}
|
||||||
18
packages/guide/src/components/Danger.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import type { PropsWithChildren } from 'react';
|
||||||
|
import { VscWarning } from 'react-icons/vsc';
|
||||||
|
|
||||||
|
export function Caution({ children }: PropsWithChildren<{}>) {
|
||||||
|
return (
|
||||||
|
<div className="rounded border border-blue-500 p-4">
|
||||||
|
<div className="flex flex-row place-items-center gap-4">
|
||||||
|
<span className="text-blue-500">
|
||||||
|
<VscWarning size={20} />
|
||||||
|
</span>
|
||||||
|
<div className="flex flex-col gap-2 text-sm">
|
||||||
|
<span className="font-semibold text-blue-500">Caution</span>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
3
packages/guide/src/components/DocsLink.tsx
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export function DocsLink() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
18
packages/guide/src/components/Info.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import type { PropsWithChildren } from 'react';
|
||||||
|
import { VscInfo } from 'react-icons/vsc';
|
||||||
|
|
||||||
|
export function Info({ children }: PropsWithChildren<{}>) {
|
||||||
|
return (
|
||||||
|
<div className="rounded border border-blue-500 p-4">
|
||||||
|
<div className="flex flex-row place-items-center gap-4">
|
||||||
|
<span className="text-blue-500">
|
||||||
|
<VscInfo size={20} />
|
||||||
|
</span>
|
||||||
|
<div className="flex flex-col gap-2 text-sm">
|
||||||
|
<span className="font-semibold text-blue-500">Info</span>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
70
packages/guide/src/components/Navbar.tsx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { Button } from 'ariakit/button';
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { FiCommand } from 'react-icons/fi';
|
||||||
|
import { VscColorMode, VscGithubInverted, VscMenu, VscSearch } from 'react-icons/vsc';
|
||||||
|
import { useMedia } from 'react-use';
|
||||||
|
import { Sidebar } from './Sidebar.jsx';
|
||||||
|
import type { MDXPage } from './SidebarItems.jsx';
|
||||||
|
|
||||||
|
export function Navbar({ pages }: { pages?: MDXPage[] | undefined }) {
|
||||||
|
const matches = useMedia('(min-width: 992px)', false);
|
||||||
|
const [opened, setOpened] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (matches) {
|
||||||
|
setOpened(false);
|
||||||
|
}
|
||||||
|
}, [matches]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<header className="dark:bg-dark-600 dark:border-dark-100 bg-light-600 border-light-800 fixed top-0 left-0 z-20 w-full border-b">
|
||||||
|
<div className="h-18 block px-6">
|
||||||
|
<div className="flex h-full flex-row place-content-between place-items-center">
|
||||||
|
<Button
|
||||||
|
aria-label="Menu"
|
||||||
|
className="focus:ring-width-2 focus:ring-blurple flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-0 focus:ring active:translate-y-px lg:hidden"
|
||||||
|
onClick={() => setOpened((open) => !open)}
|
||||||
|
>
|
||||||
|
<VscMenu size={24} />
|
||||||
|
</Button>
|
||||||
|
<div className="hidden md:flex md:flex-row">Placeholder</div>
|
||||||
|
<div className="flex flex-row place-items-center gap-4">
|
||||||
|
<Button
|
||||||
|
as="div"
|
||||||
|
className="dark:bg-dark-800 focus:ring-width-2 focus:ring-blurple rounded bg-white px-4 py-2.5 outline-0 focus:ring"
|
||||||
|
// onClick={() => dialog?.toggle()}
|
||||||
|
>
|
||||||
|
<div className="flex flex-row place-items-center gap-4">
|
||||||
|
<VscSearch size={18} />
|
||||||
|
<span className="opacity-65">Search...</span>
|
||||||
|
<div className="opacity-65 flex flex-row place-items-center gap-2">
|
||||||
|
<FiCommand size={18} /> K
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
aria-label="GitHub"
|
||||||
|
as="a"
|
||||||
|
className="focus:ring-width-2 focus:ring-blurple flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded rounded-full border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-0 focus:ring active:translate-y-px"
|
||||||
|
href="https://github.com/discordjs/discord.js"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<VscGithubInverted size={24} />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
aria-label="Toggle theme"
|
||||||
|
className="focus:ring-width-2 focus:ring-blurple flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded-full rounded border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-0 focus:ring active:translate-y-px"
|
||||||
|
// onClick={() => toggleTheme()}
|
||||||
|
>
|
||||||
|
<VscColorMode size={24} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<Sidebar opened={opened} pages={pages} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
3
packages/guide/src/components/ResultingCode.tsx
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export function ResultingCode() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
43
packages/guide/src/components/Section.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { Disclosure, DisclosureContent, useDisclosureState } from 'ariakit/disclosure';
|
||||||
|
import type { PropsWithChildren } from 'react';
|
||||||
|
import { VscChevronDown } from 'react-icons/vsc';
|
||||||
|
|
||||||
|
export function Section({
|
||||||
|
title,
|
||||||
|
icon,
|
||||||
|
padded = false,
|
||||||
|
dense = false,
|
||||||
|
defaultClosed = false,
|
||||||
|
children,
|
||||||
|
}: PropsWithChildren<{
|
||||||
|
defaultClosed?: boolean;
|
||||||
|
dense?: boolean;
|
||||||
|
icon?: JSX.Element;
|
||||||
|
padded?: boolean;
|
||||||
|
title: string;
|
||||||
|
}>) {
|
||||||
|
const disclosure = useDisclosureState({ defaultOpen: !defaultClosed });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<Disclosure
|
||||||
|
className="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple rounded p-3 outline-0 focus:ring"
|
||||||
|
state={disclosure}
|
||||||
|
>
|
||||||
|
<div className="flex flex-row place-content-between place-items-center">
|
||||||
|
<div className="flex flex-row place-items-center gap-3">
|
||||||
|
{icon ?? null}
|
||||||
|
<span className="font-semibold">{title}</span>
|
||||||
|
</div>
|
||||||
|
<VscChevronDown
|
||||||
|
className={`transform transition duration-150 ease-in-out ${disclosure.open ? 'rotate-180' : 'rotate-0'}`}
|
||||||
|
size={20}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Disclosure>
|
||||||
|
<DisclosureContent state={disclosure}>
|
||||||
|
{padded ? <div className={`py-5 ${dense ? 'mx-2 px-0' : 'px-4.5 mx-6.5'}`}>{children}</div> : children}
|
||||||
|
</DisclosureContent>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
24
packages/guide/src/components/Sidebar.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { Scrollbars } from 'react-custom-scrollbars-2';
|
||||||
|
import type { MDXPage } from './SidebarItems';
|
||||||
|
|
||||||
|
export function Sidebar({ pages, opened }: { opened: boolean; pages?: MDXPage[] | undefined }) {
|
||||||
|
return (
|
||||||
|
<nav
|
||||||
|
className={`h-[calc(100vh - 73px)] dark:bg-dark-600 dark:border-dark-100 border-light-800 fixed top-[73px] left-0 bottom-0 z-20 w-full border-r bg-white ${
|
||||||
|
opened ? 'block' : 'hidden'
|
||||||
|
} lg:w-76 lg:max-w-76 lg:block`}
|
||||||
|
>
|
||||||
|
<Scrollbars
|
||||||
|
autoHide
|
||||||
|
hideTracksWhenNotNeeded
|
||||||
|
renderThumbVertical={(props) => <div {...props} className="dark:bg-dark-100 bg-light-900 z-30 rounded" />}
|
||||||
|
renderTrackVertical={(props) => (
|
||||||
|
<div {...props} className="absolute top-0.5 right-0.5 bottom-0.5 z-30 w-1.5 rounded" />
|
||||||
|
)}
|
||||||
|
universal
|
||||||
|
>
|
||||||
|
{pages ?? null}
|
||||||
|
</Scrollbars>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
37
packages/guide/src/components/SidebarItems.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import type { MDXInstance } from 'astro';
|
||||||
|
import { Section } from './Section.jsx';
|
||||||
|
|
||||||
|
export type MDXPage = MDXInstance<{ category: string; title: string }>;
|
||||||
|
|
||||||
|
export function SidebarItems({ pages }: { pages: MDXPage[] }) {
|
||||||
|
const categories = pages.reduce<Record<string, MDXPage[]>>((acc, page) => {
|
||||||
|
if (acc[page.frontmatter.category]) {
|
||||||
|
acc[page.frontmatter.category]?.push(page);
|
||||||
|
} else {
|
||||||
|
acc[page.frontmatter.category] = [page];
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return Object.keys(categories).map((category, idx) => (
|
||||||
|
<Section key={idx} title={category}>
|
||||||
|
{categories[category]?.map((member, index) => (
|
||||||
|
<a
|
||||||
|
className={`dark:border-dark-100 border-light-800 focus:ring-width-2 focus:ring-blurple ml-5 flex flex-col border-l p-[5px] pl-6 outline-0 focus:rounded focus:border-0 focus:ring ${
|
||||||
|
false
|
||||||
|
? 'bg-blurple text-white'
|
||||||
|
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
|
||||||
|
}`}
|
||||||
|
href={member.url}
|
||||||
|
key={index}
|
||||||
|
title={member.frontmatter.title}
|
||||||
|
>
|
||||||
|
<div className="flex flex-row place-items-center gap-2 lg:text-sm">
|
||||||
|
<span className="truncate">{member.frontmatter.title}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
)) ?? null}
|
||||||
|
</Section>
|
||||||
|
));
|
||||||
|
}
|
||||||
89
packages/guide/src/components/SidebarLayout.astro
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
---
|
||||||
|
import { Navbar } from './Navbar.jsx';
|
||||||
|
import { SidebarItems } from './SidebarItems.jsx';
|
||||||
|
|
||||||
|
const pages = await Astro.glob<{ category: string; title: string }>('../pages/**/*.mdx');
|
||||||
|
---
|
||||||
|
|
||||||
|
<Navbar client:load>
|
||||||
|
<div class="flex flex-col gap-3 p-3 pb-32 lg:pb-12" slot="pages">
|
||||||
|
<SidebarItems client:load pages={pages} />
|
||||||
|
</div>
|
||||||
|
</Navbar>
|
||||||
|
<main class="pt-18 lg:pl-76">
|
||||||
|
<article class="dark:bg-dark-600 bg-light-600">
|
||||||
|
<div class="dark:bg-dark-800 relative z-10 min-h-[calc(100vh_-_70px)] bg-white p-6 pb-20 shadow">
|
||||||
|
<div class="prose max-w-none">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="h-76 md:h-52"></div>
|
||||||
|
<footer class="dark:bg-dark-600 h-76 lg:pl-84 bg-light-600 fixed bottom-0 left-0 right-0 md:h-52 md:pl-4 md:pr-16">
|
||||||
|
<div class="mx-auto flex max-w-6xl flex-col place-items-center gap-12 pt-12 lg:place-content-center">
|
||||||
|
<div class="flex w-full flex-col place-content-between place-items-center gap-12 md:flex-row md:gap-0">
|
||||||
|
<a
|
||||||
|
class="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||||
|
href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
title="Vercel"
|
||||||
|
>
|
||||||
|
<img alt="Vercel" src="/powered-by-vercel.svg" />
|
||||||
|
</a>
|
||||||
|
<div class="flex flex-row gap-6 md:gap-12">
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<div class="text-lg font-semibold">Community</div>
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<a
|
||||||
|
class="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||||
|
href="https://discord.gg/djs"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Discord
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||||
|
href="https://github.com/discordjs/discord.js/discussions"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
GitHub discussions
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<div class="text-lg font-semibold">Project</div>
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<a
|
||||||
|
class="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||||
|
href="https://github.com/discordjs/discord.js"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
discord.js
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||||
|
href="https://discordjs.guide"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
discord.js guide
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||||
|
href="https://discord-api-types.dev"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
discord-api-types
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
18
packages/guide/src/components/Tip.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import type { PropsWithChildren } from 'react';
|
||||||
|
import { VscFlame } from 'react-icons/vsc';
|
||||||
|
|
||||||
|
export function Tip({ children }: PropsWithChildren<{}>) {
|
||||||
|
return (
|
||||||
|
<div className="my-4 rounded border border-green-500 p-4">
|
||||||
|
<div className="flex flex-row place-items-center gap-4">
|
||||||
|
<span className="text-green-500">
|
||||||
|
<VscFlame size={20} />
|
||||||
|
</span>
|
||||||
|
<div className="flex flex-col gap-2 text-sm">
|
||||||
|
<span className="font-semibold text-green-500">Tip</span>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
60
packages/guide/src/components/discord/Message.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import type { PropsWithChildren, ReactNode } from 'react';
|
||||||
|
import { DiscordMessageAuthor, type IDiscordMessageAuthor } from './MessageAuthor.jsx';
|
||||||
|
import { DiscordMessageInteraction, type IDiscordMessageInteraction } from './MessageInteraction.jsx';
|
||||||
|
import { DiscordMessageReply, type IDiscordMessageReply } from './MessageReply.jsx';
|
||||||
|
|
||||||
|
export function DiscordMessage({
|
||||||
|
reply,
|
||||||
|
replyNode,
|
||||||
|
interaction,
|
||||||
|
interactionNode,
|
||||||
|
author,
|
||||||
|
authorNode,
|
||||||
|
followUp,
|
||||||
|
time,
|
||||||
|
children,
|
||||||
|
}: PropsWithChildren<{
|
||||||
|
author?: IDiscordMessageAuthor | undefined;
|
||||||
|
authorNode?: ReactNode | undefined;
|
||||||
|
followUp?: boolean;
|
||||||
|
interaction?: IDiscordMessageInteraction | undefined;
|
||||||
|
interactionNode?: ReactNode | undefined;
|
||||||
|
reply?: IDiscordMessageReply | undefined;
|
||||||
|
replyNode?: ReactNode | undefined;
|
||||||
|
time?: string | undefined;
|
||||||
|
}>) {
|
||||||
|
return (
|
||||||
|
<div className="relative" id="outer-message-wrapper">
|
||||||
|
<div
|
||||||
|
className={`pl-18 hover:bg-[rgb(4_4_5)]/7 group py-0.5 pr-12 leading-snug ${followUp ? '' : 'mt-4'}`}
|
||||||
|
id="message-wrapper"
|
||||||
|
>
|
||||||
|
{(reply || replyNode) && !followUp ? reply ? <DiscordMessageReply {...reply} /> : replyNode ?? null : null}
|
||||||
|
{(interaction || interactionNode) && !(reply || replyNode) && !followUp ? (
|
||||||
|
interaction ? (
|
||||||
|
<DiscordMessageInteraction {...interaction} />
|
||||||
|
) : (
|
||||||
|
interactionNode ?? null
|
||||||
|
)
|
||||||
|
) : null}
|
||||||
|
<div className="static" id="content-wrapper">
|
||||||
|
{followUp ? (
|
||||||
|
<span
|
||||||
|
className="h-5.5 absolute left-0 mr-1 hidden w-[56px] cursor-default select-none text-right text-xs leading-loose group-hover:inline-block"
|
||||||
|
id="time"
|
||||||
|
>
|
||||||
|
{time}
|
||||||
|
</span>
|
||||||
|
) : author ? (
|
||||||
|
<DiscordMessageAuthor {...author} />
|
||||||
|
) : (
|
||||||
|
authorNode
|
||||||
|
)}
|
||||||
|
<div className="[&>p]:m-0 [&>p]:leading-snug text-white" id="message-content">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
31
packages/guide/src/components/discord/MessageAuthor.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
export interface IDiscordMessageAuthor {
|
||||||
|
avatar: string;
|
||||||
|
bot?: boolean;
|
||||||
|
time: string;
|
||||||
|
username: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DiscordMessageAuthor({ avatar, username, bot, time }: IDiscordMessageAuthor) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<img
|
||||||
|
alt={`${username}'s avatar`}
|
||||||
|
className="absolute left-[16px] mt-0.5 h-10 w-10 cursor-pointer select-none rounded-full"
|
||||||
|
src={avatar}
|
||||||
|
/>
|
||||||
|
<h2 className="text-size-inherit m-0 font-medium leading-snug" id="user-info">
|
||||||
|
<span className="mr-1" id="username">
|
||||||
|
<span className="cursor-pointer text-base font-medium text-white hover:underline">{username}</span>
|
||||||
|
{bot ? (
|
||||||
|
<span className="bg-blurple vertical-top relative top-1 ml-1 rounded px-1 text-xs" id="bot">
|
||||||
|
BOT
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</span>
|
||||||
|
<span className="ml-1 cursor-default text-xs leading-snug text-[rgb(163_166_170)]" id="time">
|
||||||
|
{time}
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
21
packages/guide/src/components/discord/MessageAuthorReply.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
export interface IDiscordMessageAuthorReply {
|
||||||
|
avatar: string;
|
||||||
|
bot?: boolean;
|
||||||
|
username: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DiscordMessageAuthorReply({ avatar, bot, username }: IDiscordMessageAuthorReply) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<img alt={`${username}'s avatar`} className="mr-1 h-4 w-4 select-none rounded-full" src={avatar} />
|
||||||
|
{bot ? (
|
||||||
|
<div className="bg-blurple vertical-top mr-1 rounded px-1 text-xs" id="bot">
|
||||||
|
BOT
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<span className="mr-1 cursor-pointer select-none text-sm font-medium leading-snug text-white hover:underline">
|
||||||
|
{username}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
20
packages/guide/src/components/discord/MessageBaseReply.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import type { PropsWithChildren, ReactNode } from 'react';
|
||||||
|
import { DiscordMessageAuthorReply, type IDiscordMessageAuthorReply } from './MessageAuthorReply.jsx';
|
||||||
|
|
||||||
|
export function DiscordMessageBaseReply({
|
||||||
|
author,
|
||||||
|
authorNode,
|
||||||
|
children,
|
||||||
|
}: PropsWithChildren<{ author?: IDiscordMessageAuthorReply | undefined; authorNode?: ReactNode | undefined }>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="before:rounded-tl-1.5 relative mb-1 flex place-items-center before:absolute before:left-[-36px] before:right-full before:top-[50%] before:bottom-0 before:mr-1 before:block before:border-l-2 before:border-t-2 before:border-[rgb(79_84_92)] before:content-none"
|
||||||
|
id="reply-wrapper"
|
||||||
|
>
|
||||||
|
<div className="[&>span]:opacity-60 flex place-items-center">
|
||||||
|
{author ? <DiscordMessageAuthorReply {...author} /> : authorNode}
|
||||||
|
</div>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
38
packages/guide/src/components/discord/MessageEmbed.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import type { PropsWithChildren, ReactNode } from 'react';
|
||||||
|
import { DiscordMessageEmbedAuthor, type IDiscordMessageEmbedAuthor } from './MessageEmbedAuthor.jsx';
|
||||||
|
import { DiscordMessageEmbedFooter, type IDiscordMessageEmbedFooter } from './MessageEmbedFooter.jsx';
|
||||||
|
import { DiscordMessageEmbedTitle, type IDiscordMessageEmbedTitle } from './MessageEmbedTitle.jsx';
|
||||||
|
|
||||||
|
export interface IDiscordMessageEmbed {
|
||||||
|
author?: IDiscordMessageEmbedAuthor | undefined;
|
||||||
|
authorNode?: ReactNode | undefined;
|
||||||
|
footer?: IDiscordMessageEmbedFooter | undefined;
|
||||||
|
footerNode?: ReactNode | undefined;
|
||||||
|
title?: IDiscordMessageEmbedTitle | undefined;
|
||||||
|
titleNode?: ReactNode | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DiscordMessageEmbed({
|
||||||
|
author,
|
||||||
|
authorNode,
|
||||||
|
title,
|
||||||
|
titleNode,
|
||||||
|
children,
|
||||||
|
footer,
|
||||||
|
footerNode,
|
||||||
|
}: PropsWithChildren<IDiscordMessageEmbed>) {
|
||||||
|
return (
|
||||||
|
<div className="py-0.5" id="outer-embed-wrapper">
|
||||||
|
<div className="border-l-blurple grid max-w-max rounded border-l-4 bg-[rgb(47_49_54)]" id="embed-wrapper">
|
||||||
|
<div className="max-w-128">
|
||||||
|
<div className="pt-2 pr-4 pb-4 pl-3">
|
||||||
|
{author ? <DiscordMessageEmbedAuthor {...author} /> : authorNode ?? null}
|
||||||
|
{title ? <DiscordMessageEmbedTitle {...title} /> : titleNode ?? null}
|
||||||
|
{children ? <div className="mt-2 text-sm">{children}</div> : null}
|
||||||
|
{footer ? <DiscordMessageEmbedFooter {...footer} /> : footerNode ?? null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
13
packages/guide/src/components/discord/MessageEmbedAuthor.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export interface IDiscordMessageEmbedAuthor {
|
||||||
|
avatar: string;
|
||||||
|
username: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DiscordMessageEmbedAuthor({ avatar, username }: IDiscordMessageEmbedAuthor) {
|
||||||
|
return (
|
||||||
|
<div className="mt-2 flex place-items-center">
|
||||||
|
<img alt={`${username}'s avatar`} className="mr-2 h-6 w-6 select-none rounded-full" src={avatar} />
|
||||||
|
<span className="text-sm font-medium hover:underline">{username}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export interface IDiscordMessageEmbedFooter {
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DiscordMessageEmbedFooter({ content }: IDiscordMessageEmbedFooter) {
|
||||||
|
return <div className="mt-2 text-xs">{content}</div>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export interface IDiscordMessageEmbedTitle {
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DiscordMessageEmbedTitle({ title }: IDiscordMessageEmbedTitle) {
|
||||||
|
return <div className="mt-2 font-medium">{title}</div>;
|
||||||
|
}
|
||||||
18
packages/guide/src/components/discord/MessageInteraction.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import type { ReactNode } from 'react';
|
||||||
|
import type { IDiscordMessageAuthorReply } from './MessageAuthorReply.jsx';
|
||||||
|
import { DiscordMessageBaseReply } from './MessageBaseReply.jsx';
|
||||||
|
|
||||||
|
export interface IDiscordMessageInteraction {
|
||||||
|
author?: IDiscordMessageAuthorReply | undefined;
|
||||||
|
authorNode?: ReactNode | undefined;
|
||||||
|
command?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DiscordMessageInteraction({ author, authorNode, command }: IDiscordMessageInteraction) {
|
||||||
|
return (
|
||||||
|
<DiscordMessageBaseReply author={author} authorNode={authorNode}>
|
||||||
|
<span className="mr-1 select-none text-sm leading-snug text-white">used</span>
|
||||||
|
<div className="text-blurple cursor-pointer text-sm leading-snug hover:underline">{command}</div>
|
||||||
|
</DiscordMessageBaseReply>
|
||||||
|
);
|
||||||
|
}
|
||||||
19
packages/guide/src/components/discord/MessageReply.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import type { ReactNode } from 'react';
|
||||||
|
import type { IDiscordMessageAuthorReply } from './MessageAuthorReply.jsx';
|
||||||
|
import { DiscordMessageBaseReply } from './MessageBaseReply.jsx';
|
||||||
|
|
||||||
|
export interface IDiscordMessageReply {
|
||||||
|
author?: IDiscordMessageAuthorReply | undefined;
|
||||||
|
authorNode?: ReactNode | undefined;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DiscordMessageReply({ author, authorNode, content }: IDiscordMessageReply) {
|
||||||
|
return (
|
||||||
|
<DiscordMessageBaseReply author={author} authorNode={authorNode}>
|
||||||
|
<div className="cursor-pointer select-none text-sm leading-snug text-[rgb(163_166_170)] hover:text-white">
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
</DiscordMessageBaseReply>
|
||||||
|
);
|
||||||
|
}
|
||||||
9
packages/guide/src/components/discord/Messages.tsx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import type { PropsWithChildren } from 'react';
|
||||||
|
|
||||||
|
export function DiscordMessages({ rounded, children }: PropsWithChildren<{ rounded?: boolean }>) {
|
||||||
|
return (
|
||||||
|
<div className={`pt-0.1 bg-[rgb(54_57_63)] pb-4 ${rounded ? 'rounded' : ''}`} id="messages-wrapper">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
58
packages/guide/src/layouts/SidebarLayout.astro
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
import '@code-hike/mdx/styles.css';
|
||||||
|
import '../styles/ch.css';
|
||||||
|
import SidebarLayout from '../components/SidebarLayout.astro';
|
||||||
|
import { DESCRIPTION } from '../util/constants.js';
|
||||||
|
---
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<link href="/apple-touch-icon.png" rel="apple-touch-icon" sizes="180x180" />
|
||||||
|
<link href="/favicon-32x32.png" rel="icon" sizes="32x32" type="image/png" />
|
||||||
|
<link href="/favicon-16x16.png" rel="icon" sizes="16x16" type="image/png" />
|
||||||
|
<link href="/site.webmanifest" rel="manifest" />
|
||||||
|
<link color="#090a16" href="/safari-pinned-tab.svg" rel="mask-icon" />
|
||||||
|
<meta content="light dark" name="color-scheme" />
|
||||||
|
<meta content="discord.js" name="apple-mobile-web-app-title" />
|
||||||
|
<meta content="discord.js" name="application-name" />
|
||||||
|
<meta content="#090a16" name="msapplication-TileColor" />
|
||||||
|
<meta content={DESCRIPTION} name="description" />
|
||||||
|
<meta content="discord.js" property="og:site_name" />
|
||||||
|
<meta content="website" property="og:type" />
|
||||||
|
<meta content="discord.js guide" property="og:title" />
|
||||||
|
<meta content={DESCRIPTION} name="og:description" />
|
||||||
|
<meta content="https://discordjs.dev/open-graph.png" property="og:image" />
|
||||||
|
<meta content="summary_large_image" name="twitter:card" />
|
||||||
|
<meta content="@iCrawlToGo" name="twitter:creator" />
|
||||||
|
|
||||||
|
<title>discord.js</title>
|
||||||
|
<meta content="minimum-scale=1, initial-scale=1, width=device-width" name="viewport" />
|
||||||
|
<meta content="#5865f2" name="theme-color" />
|
||||||
|
</head>
|
||||||
|
<body class="dark:bg-dark-800 bg-white">
|
||||||
|
<script is:inline>
|
||||||
|
function setTheme(prefersDarkMode, persistedColorPreference) {
|
||||||
|
if (persistedColorPreference === 'dark' || (prefersDarkMode && persistedColorPreference !== 'light')) {
|
||||||
|
document.documentElement.classList.toggle('dark', true);
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.toggle('dark', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
const prefersDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
const persistedColorPreference = localStorage.getItem('theme') || 'auto';
|
||||||
|
setTheme(prefersDarkMode, persistedColorPreference);
|
||||||
|
|
||||||
|
const listener =
|
||||||
|
window.matchMedia &&
|
||||||
|
window
|
||||||
|
.matchMedia('(prefers-color-scheme: dark)')
|
||||||
|
.addEventListener('change', (ev) => setTheme(ev.matches, persistedColorPreference));
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<SidebarLayout>
|
||||||
|
<slot />
|
||||||
|
</SidebarLayout>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
244
packages/guide/src/pages/creating-your-bot/creating-commands.mdx
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
---
|
||||||
|
layout: '../../layouts/SidebarLayout.astro'
|
||||||
|
title: Creating commands
|
||||||
|
category: Creating your bot
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Tip } from '../../components/Tip.jsx';
|
||||||
|
import { DiscordMessages } from '../../components/discord/Messages.jsx';
|
||||||
|
import { DiscordMessage } from '../../components/discord/Message.jsx';
|
||||||
|
import { DocsLink } from '../../components/DocsLink.jsx';
|
||||||
|
import { ResultingCode } from '../../components/ResultingCode.jsx';
|
||||||
|
import { CH } from '@code-hike/mdx/components';
|
||||||
|
|
||||||
|
# Creating commands
|
||||||
|
|
||||||
|
<Tip>This page is a follow-up and bases its code on [the previous page](/creating-your-bot/).</Tip>
|
||||||
|
|
||||||
|
<DiscordMessages rounded>
|
||||||
|
<DiscordMessage
|
||||||
|
interaction={{
|
||||||
|
author: {
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||||
|
username: 'Crawl',
|
||||||
|
},
|
||||||
|
command: 'ping',
|
||||||
|
}}
|
||||||
|
author={{
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||||
|
username: 'Crawl',
|
||||||
|
time: 'Today at 21:00',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Pong!
|
||||||
|
</DiscordMessage>
|
||||||
|
</DiscordMessages>
|
||||||
|
|
||||||
|
Discord allows developers to register [slash commands](https://discord.com/developers/docs/interactions/application-commands), which provide users a first-class way of interacting directly with your application. Before being able to reply to a command, you must first register it.
|
||||||
|
|
||||||
|
## Registering commands
|
||||||
|
|
||||||
|
This section will cover only the bare minimum to get you started, but you can refer to our [in-depth page on registering slash commands](/interactions/slash-commands.md#registering-slash-commands) for further details. It covers guild commands, global commands, options, option types, and choices.
|
||||||
|
|
||||||
|
### Command deployment script
|
||||||
|
|
||||||
|
Create a `deploy-commands.js` file in your project directory. This file will be used to register and update the slash commands for your bot application.
|
||||||
|
|
||||||
|
Since commands only need to be registered once, and updated when the definition (description, options etc) is changed, it's not necessary to connect a whole client to the gateway or do this on every `ready` event. As such, a standalone script using the lighter REST manager is preferred.
|
||||||
|
|
||||||
|
Below is a deployment script you can use. Focus on these variables:
|
||||||
|
|
||||||
|
- `clientId`: Your application's client id
|
||||||
|
- `guildId`: Your development server's id
|
||||||
|
- `commands`: An array of commands to register. The [slash command builder](/popular-topics/builders.md#slash-command-builders) from `discord.js` is used to build the data for your commands
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
In order to get your application's client id, go to [Discord Developer
|
||||||
|
Portal](https://discord.com/developers/applications) and choose your application. Find the id under "Application ID"
|
||||||
|
in General Information subpage. To get guild id, open Discord and go to your settings. On the "Advanced" page, turn on
|
||||||
|
"Developer Mode". This will enable a "Copy ID" button in the context menu when you right-click on a server icon, a
|
||||||
|
user's profile, etc.
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
<CH.Code client:load>
|
||||||
|
|
||||||
|
```js deploy-commands.js mark=4,6:10
|
||||||
|
const { REST, SlashCommandBuilder, Routes } = require('discord.js');
|
||||||
|
const { clientId, guildId, token } = require('./config.json');
|
||||||
|
|
||||||
|
const commands = [
|
||||||
|
new SlashCommandBuilder().setName('ping').setDescription('Replies with pong!'),
|
||||||
|
new SlashCommandBuilder().setName('server').setDescription('Replies with server info!'),
|
||||||
|
new SlashCommandBuilder().setName('user').setDescription('Replies with user info!'),
|
||||||
|
].map((command) => command.toJSON());
|
||||||
|
|
||||||
|
const rest = new REST({ version: '10' }).setToken(token);
|
||||||
|
|
||||||
|
rest
|
||||||
|
.put(Routes.applicationGuildCommands(clientId, guildId), { body: commands })
|
||||||
|
.then((data) => console.log(`Successfully registered ${data.length} application commands.`))
|
||||||
|
.catch(console.error);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```json config.json mark=2:3
|
||||||
|
{
|
||||||
|
"clientId": "123456789012345678",
|
||||||
|
"guildId": "876543210987654321",
|
||||||
|
"token": "your-token-goes-here"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CH.Code>
|
||||||
|
|
||||||
|
Once you fill in these values, run `node deploy-commands.js` in your project directory to register your commands to a single guild. It's also possible to [register commands globally](/interactions/slash-commands.md#global-commands).
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
You only need to run `node deploy-commands.js` once. You should only run it again if you add or edit existing
|
||||||
|
commands.
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
## Replying to commands
|
||||||
|
|
||||||
|
Once you've registered your commands, you can listen for interactions via <DocsLink path="class/Client?scrollTo=e-interactionCreate" /> in your `index.js` file.
|
||||||
|
|
||||||
|
You should first check if an interaction is a chat input command via <DocsLink path="class/Interaction?scrollTo=isChatInputCommand" type="method">`.isChatInputCommand()`</DocsLink>, and then check the <DocsLink path="class/CommandInteraction?scrollTo=commandName">`.commandName`</DocsLink> property to know which command it is. You can respond to interactions with <DocsLink path="class/CommandInteraction?scrollTo=reply">`.reply()`</DocsLink>.
|
||||||
|
|
||||||
|
<CH.Code client:load>
|
||||||
|
|
||||||
|
```js mark=5:16
|
||||||
|
client.once('ready', () => {
|
||||||
|
console.log('Ready!');
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('interactionCreate', async (interaction) => {
|
||||||
|
if (!interaction.isChatInputCommand()) return;
|
||||||
|
const { commandName } = interaction;
|
||||||
|
if (commandName === 'ping') {
|
||||||
|
await interaction.reply('Pong!');
|
||||||
|
} else if (commandName === 'server') {
|
||||||
|
await interaction.reply('Server info.');
|
||||||
|
} else if (commandName === 'user') {
|
||||||
|
await interaction.reply('User info.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
client.login(token);
|
||||||
|
```
|
||||||
|
|
||||||
|
</CH.Code>
|
||||||
|
|
||||||
|
### Server info command
|
||||||
|
|
||||||
|
Note that servers are referred to as "guilds" in the Discord API and discord.js library. `interaction.guild` refers to the guild the interaction was sent in (a <DocsLink path="class/Guild" /> instance), which exposes properties such as `.name` or `.memberCount`.
|
||||||
|
|
||||||
|
<CH.Code client:load>
|
||||||
|
|
||||||
|
```js focus=7
|
||||||
|
client.on('interactionCreate', async (interaction) => {
|
||||||
|
if (!interaction.isChatInputCommand()) return;
|
||||||
|
const { commandName } = interaction;
|
||||||
|
if (commandName === 'ping') {
|
||||||
|
await interaction.reply('Pong!');
|
||||||
|
} else if (commandName === 'server') {
|
||||||
|
await interaction.reply(`Server name: ${interaction.guild.name}\nTotal members: ${interaction.guild.memberCount}`);
|
||||||
|
} else if (commandName === 'user') {
|
||||||
|
await interaction.reply('User info.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
</CH.Code>
|
||||||
|
|
||||||
|
<DiscordMessages rounded>
|
||||||
|
<DiscordMessage
|
||||||
|
interaction={{
|
||||||
|
author: {
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||||
|
username: 'Crawl',
|
||||||
|
},
|
||||||
|
command: 'server',
|
||||||
|
}}
|
||||||
|
author={{
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||||
|
username: 'Crawl',
|
||||||
|
time: 'Today at 21:00',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p>Server name: discord.js Guide</p>
|
||||||
|
<p>Total members: 2</p>
|
||||||
|
</DiscordMessage>
|
||||||
|
</DiscordMessages>
|
||||||
|
|
||||||
|
You could also display the date the server was created, or the server's verification level. You would do those in the same manner – use `interaction.guild.createdAt` or `interaction.guild.verificationLevel`, respectively.
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
Refer to the <DocsLink path="class/Guild" /> documentation for a list of all the available properties and methods!
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
### User info command
|
||||||
|
|
||||||
|
A "user" refers to a Discord user. `interaction.user` refers to the user the interaction was sent by (a <DocsLink path="class/User" /> instance), which exposes properties such as `.tag` or `.id`.
|
||||||
|
|
||||||
|
<CH.Code client:load>
|
||||||
|
|
||||||
|
```js focus=9
|
||||||
|
client.on('interactionCreate', async (interaction) => {
|
||||||
|
if (!interaction.isChatInputCommand()) return;
|
||||||
|
const { commandName } = interaction;
|
||||||
|
if (commandName === 'ping') {
|
||||||
|
await interaction.reply('Pong!');
|
||||||
|
} else if (commandName === 'server') {
|
||||||
|
await interaction.reply(`Server name: ${interaction.guild.name}\nTotal members: ${interaction.guild.memberCount}`);
|
||||||
|
} else if (commandName === 'user') {
|
||||||
|
await interaction.reply(`Your tag: ${interaction.user.tag}\nYour id: ${interaction.user.id}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
</CH.Code>
|
||||||
|
|
||||||
|
<DiscordMessages rounded>
|
||||||
|
<DiscordMessage
|
||||||
|
interaction={{
|
||||||
|
author: {
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||||
|
username: 'Crawl',
|
||||||
|
},
|
||||||
|
command: 'user',
|
||||||
|
}}
|
||||||
|
author={{
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||||
|
username: 'Crawl',
|
||||||
|
time: 'Today at 21:00',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p>Your tag: User#0001</p>
|
||||||
|
<p>Your id: 123456789012345678</p>
|
||||||
|
</DiscordMessage>
|
||||||
|
</DiscordMessages>
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
Refer to the <DocsLink path="class/User" /> documentation for a list of all the available properties and methods!
|
||||||
|
</Tip>
|
||||||
|
|
||||||
|
And there you have it!
|
||||||
|
|
||||||
|
## The problem with `if`/`else if`
|
||||||
|
|
||||||
|
If you don't plan on making more than a couple commands, then using an `if`/`else if` chain is fine; however, this isn't always the case. Using a giant `if`/`else if` chain will only hinder your development process in the long run.
|
||||||
|
|
||||||
|
Here's a small list of reasons why you shouldn't do so:
|
||||||
|
|
||||||
|
- Takes longer to find a piece of code you want;
|
||||||
|
- Easier to fall victim to [spaghetti code](https://en.wikipedia.org/wiki/Spaghetti_code);
|
||||||
|
- Difficult to maintain as it grows;
|
||||||
|
- Difficult to debug;
|
||||||
|
- Difficult to organize;
|
||||||
|
- General bad practice.
|
||||||
|
|
||||||
|
Next, we'll be diving into something called a "command handler" – code that makes handling commands easier and much more efficient. This allows you to move your commands into individual files.
|
||||||
|
|
||||||
|
## Resulting code
|
||||||
|
|
||||||
|
<ResultingCode />
|
||||||
36
packages/guide/src/pages/home.mdx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
layout: '../layouts/SidebarLayout.astro'
|
||||||
|
title: Introduction
|
||||||
|
category: Home
|
||||||
|
---
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
If you're reading this, it probably means you want to learn how to make a bot with discord.js. Awesome! You've come to the right place.
|
||||||
|
This guide will teach you things such as:
|
||||||
|
|
||||||
|
- How to get a bot [up and running](/preparations/) from scratch;
|
||||||
|
- How to properly [create](/creating-your-bot/), [organize](/creating-your-bot/command-handling.md), and expand on your commands;
|
||||||
|
- In-depth explanations and examples regarding popular topics (e.g. [reactions](/popular-topics/reactions.md), [embeds](/popular-topics/embeds.md), [canvas](/popular-topics/canvas.md));
|
||||||
|
- Working with databases (e.g. [sequelize](/sequelize/) and [keyv](/keyv/));
|
||||||
|
- Getting started with [sharding](/sharding/);
|
||||||
|
- And much more.
|
||||||
|
|
||||||
|
This guide will also cover subjects like common errors and how to solve them, keeping your code clean, setting up a proper development environment, etc.
|
||||||
|
Sounds good? Great! Let's get started, then.
|
||||||
|
|
||||||
|
## Before you begin...
|
||||||
|
|
||||||
|
Alright, making a bot is cool and all, but there are some prerequisites to it. To create a bot with discord.js, you should have a fairly decent grasp of JavaScript itself.
|
||||||
|
While you _can_ make a bot with very little JavaScript and programming knowledge, trying to do so without understanding the language first will only hinder you. You may get stuck on many uncomplicated issues, struggle with solutions to incredibly easy problems, and all-in-all end up frustrated. Sounds pretty annoying.
|
||||||
|
|
||||||
|
If you don't know JavaScript but would like to learn about it, here are a few links to help get you started:
|
||||||
|
|
||||||
|
- [Eloquent JavaScript, a free online book](http://eloquentjavascript.net/)
|
||||||
|
- [JavaScript.info, a modern javascript tutorial](https://javascript.info/)
|
||||||
|
- [Codecademy's interactive JavaScript course](https://www.codecademy.com/learn/introduction-to-javascript)
|
||||||
|
- [Nodeschool, for both JavaScript and Node.js lessons](https://nodeschool.io/)
|
||||||
|
- [MDN's JavaScript guide and full documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
|
||||||
|
- [Google, your best friend](https://google.com)
|
||||||
|
|
||||||
|
Take your pick, learn some JavaScript, and once you feel like you're confident enough to make a bot, come back and get started!
|
||||||
8
packages/guide/src/pages/index.astro
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
---
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h1>Hello, World!</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
20
packages/guide/src/pages/requesting-more-content.mdx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
layout: '../layouts/SidebarLayout.astro'
|
||||||
|
title: Requesting more content
|
||||||
|
category: Home
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Tip } from '../components/Tip.jsx';
|
||||||
|
|
||||||
|
# Requesting more content
|
||||||
|
|
||||||
|
Since this guide is made specifically for the discord.js community, we want to be sure to provide the most relevant and up-to-date content. We will, of course, make additions to the current pages and add new ones as we see fit, but fulfilling requests is how we know we're providing content you all want the most.
|
||||||
|
|
||||||
|
Requests may be as simple as "add an example to the [frequently asked questions](/popular-topics/faq.html) page", or as elaborate as "add a page regarding [sharding](/sharding/)". We'll do our best to fulfill all requests, as long as they're reasonable.
|
||||||
|
|
||||||
|
To make a request, simply head over to [the repo's issue tracker](https://github.com/discordjs/guide/issues) and [create a new issue](https://github.com/discordjs/guide/issues/new)! Title it appropriately, and let us know exactly what you mean inside the issue description. Make sure that you've looked around the site before making a request; what you want to request might already exist!
|
||||||
|
|
||||||
|
<Tip>
|
||||||
|
Remember that you can always [fork the repo](https://github.com/discordjs/guide) and [make a pull
|
||||||
|
request](https://github.com/discordjs/guide/pulls) if you want to add anything to the guide yourself!
|
||||||
|
</Tip>
|
||||||
95
packages/guide/src/pages/test.mdx
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
---
|
||||||
|
title: Test
|
||||||
|
category: Test
|
||||||
|
---
|
||||||
|
|
||||||
|
import { DiscordMessages } from '../components/discord/Messages.jsx';
|
||||||
|
import { DiscordMessage } from '../components/discord/Message.jsx';
|
||||||
|
import { DiscordMessageEmbed } from '../components/discord/MessageEmbed.jsx';
|
||||||
|
|
||||||
|
<DiscordMessages>
|
||||||
|
<DiscordMessage
|
||||||
|
reply={{
|
||||||
|
author: {
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||||
|
username: 'Crawl',
|
||||||
|
},
|
||||||
|
content: 'Test',
|
||||||
|
}}
|
||||||
|
author={{
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||||
|
username: 'Crawl',
|
||||||
|
time: 'Today at 21:00',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
1234
|
||||||
|
</DiscordMessage>
|
||||||
|
<DiscordMessage
|
||||||
|
author={{
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||||
|
username: 'Crawl',
|
||||||
|
time: 'Today at 21:00',
|
||||||
|
}}
|
||||||
|
followUp
|
||||||
|
time="21:02"
|
||||||
|
>
|
||||||
|
1234
|
||||||
|
</DiscordMessage>
|
||||||
|
</DiscordMessages>
|
||||||
|
|
||||||
|
<DiscordMessages>
|
||||||
|
<DiscordMessage
|
||||||
|
reply={{
|
||||||
|
author: {
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||||
|
username: 'Crawl',
|
||||||
|
},
|
||||||
|
content: 'Test',
|
||||||
|
}}
|
||||||
|
author={{
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||||
|
username: 'Crawl',
|
||||||
|
time: 'Today at 21:00',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
1234
|
||||||
|
</DiscordMessage>
|
||||||
|
</DiscordMessages>
|
||||||
|
|
||||||
|
<DiscordMessages>
|
||||||
|
<DiscordMessage
|
||||||
|
reply={{
|
||||||
|
author: {
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||||
|
username: 'Crawl',
|
||||||
|
},
|
||||||
|
content: 'Test',
|
||||||
|
}}
|
||||||
|
author={{
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||||
|
username: 'Crawl',
|
||||||
|
time: 'Today at 21:00',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<>
|
||||||
|
<DiscordMessageEmbed
|
||||||
|
author={{
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||||
|
username: 'Crawl',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<DiscordMessageEmbed title={{ title: 'Title' }} />
|
||||||
|
<DiscordMessageEmbed footer={{ content: 'Footer' }} />
|
||||||
|
<DiscordMessageEmbed
|
||||||
|
author={{
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||||
|
username: 'Crawl',
|
||||||
|
}}
|
||||||
|
title={{ title: 'Title' }}
|
||||||
|
footer={{ content: 'Footer' }}
|
||||||
|
>
|
||||||
|
Test
|
||||||
|
</DiscordMessageEmbed>
|
||||||
|
</>
|
||||||
|
</DiscordMessage>
|
||||||
|
</DiscordMessages>
|
||||||
74
packages/guide/src/pages/whats-new.mdx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
---
|
||||||
|
layout: '../layouts/SidebarLayout.astro'
|
||||||
|
title: What's new
|
||||||
|
category: Home
|
||||||
|
---
|
||||||
|
|
||||||
|
import { DiscordMessages } from '../components/discord/Messages.jsx';
|
||||||
|
import { DiscordMessage } from '../components/discord/Message.jsx';
|
||||||
|
|
||||||
|
# What's new
|
||||||
|
|
||||||
|
<DiscordMessages rounded>
|
||||||
|
<DiscordMessage
|
||||||
|
interaction={{
|
||||||
|
author: {
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||||
|
username: 'Crawl',
|
||||||
|
},
|
||||||
|
command: 'upgrade',
|
||||||
|
}}
|
||||||
|
author={{
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/474807795183648809/7f239a0776ff928b2182906a2b3743c9.webp?size=160',
|
||||||
|
bot: true,
|
||||||
|
username: 'Guide Bot',
|
||||||
|
time: 'Today at 21:00',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
discord.js v14 has released and the guide has been updated!
|
||||||
|
<br />
|
||||||
|
This includes additions and changes made in Discord, such as slash commands and message components.
|
||||||
|
</DiscordMessage>
|
||||||
|
</DiscordMessages>
|
||||||
|
|
||||||
|
## Site
|
||||||
|
|
||||||
|
- Upgraded to [VuePress v2](https://v2.vuepress.vuejs.org/)
|
||||||
|
- New theme made to match the [discord.js documentation site](https://discord.js.org/)
|
||||||
|
- Discord message components upgraded to [@discord-message-components/vue](https://github.com/Danktuary/discord-message-components/blob/main/packages/vue/README.md)
|
||||||
|
- Many fixes in code blocks, grammar, consistency, etc.
|
||||||
|
|
||||||
|
## Pages
|
||||||
|
|
||||||
|
All content has been updated to use discord.js v14 syntax. The v13 version of the guide can be found at [https://v13.discordjs.guide/](https://v13.discordjs.guide/).
|
||||||
|
|
||||||
|
### New
|
||||||
|
|
||||||
|
- [Updating from v13 to v14](/additional-info/changes-in-v14.md): A list of the changes from discord.js v13 to v14
|
||||||
|
- [Slash commands](/interactions/slash-commands.md): Registering, replying to slash commands and permissions
|
||||||
|
- [Buttons](/interactions/buttons.md): Building, sending, and receiving buttons
|
||||||
|
- [Select menus](/interactions/select-menus.md): Building, sending, and receiving select menus
|
||||||
|
- [Threads](/popular-topics/threads.md): Creating and managing threads
|
||||||
|
- [Builders](/popular-topics/builders.md): A collection of builders to use with your bot
|
||||||
|
|
||||||
|
### Updated
|
||||||
|
|
||||||
|
- Commando: Replaced with [Sapphire](https://sapphirejs.dev/docs/Guide/getting-started/getting-started-with-sapphire)
|
||||||
|
- [Voice](/voice/): Rewritten to use the [`@discordjs/voice`](https://github.com/discordjs/discord.js/tree/main/packages/voice) package
|
||||||
|
- [Command handling](/creating-your-bot/command-handling.md/): Updated to use slash commands
|
||||||
|
- Obsolete sections removed
|
||||||
|
- `client.on('message')` snippets updated to `client.on('interactionCreate')`
|
||||||
|
- [Message content will become a new privileged intent on August 31, 2022](https://support-dev.discord.com/hc/en-us/articles/4404772028055)
|
||||||
|
|
||||||
|
<DiscordMessages rounded>
|
||||||
|
<DiscordMessage
|
||||||
|
author={{
|
||||||
|
avatar: 'https://cdn.discordapp.com/avatars/474807795183648809/7f239a0776ff928b2182906a2b3743c9.webp?size=160',
|
||||||
|
bot: true,
|
||||||
|
username: 'Guide Bot',
|
||||||
|
time: 'Today at 21:00',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Thank you to all of those that contributed to the development of discord.js and the guide!
|
||||||
|
</DiscordMessage>
|
||||||
|
</DiscordMessages>
|
||||||
3
packages/guide/src/styles/ch.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.ch-frame-buttons {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
1
packages/guide/src/util/constants.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const DESCRIPTION = 'Imagine a guide... that explores the many possibilities for your discord.js bot.';
|
||||||
22
packages/guide/tsconfig.eslint.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
"**/*.js",
|
||||||
|
"**/*.cjs",
|
||||||
|
"**/*.mjs",
|
||||||
|
"**/*.jsx",
|
||||||
|
"**/*.astro",
|
||||||
|
"**/*.test.ts",
|
||||||
|
"**/*.test.js",
|
||||||
|
"**/*.test.mjs",
|
||||||
|
"**/*.spec.ts",
|
||||||
|
"**/*.spec.js",
|
||||||
|
"**/*.spec.mjs"
|
||||||
|
],
|
||||||
|
"exclude": []
|
||||||
|
}
|
||||||
19
packages/guide/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"baseUrl": ".",
|
||||||
|
"noEmit": true,
|
||||||
|
"allowJs": false,
|
||||||
|
"incremental": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"types": ["@astrojs/image/client"],
|
||||||
|
"paths": {
|
||||||
|
"~/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.tsx", "types.d.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
1
packages/guide/types.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
declare module '*.css';
|
||||||
65
packages/guide/unocss.config.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { defineConfig, presetTypography, presetUno, presetWebFonts } from 'unocss';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
theme: {
|
||||||
|
colors: {
|
||||||
|
blurple: {
|
||||||
|
50: '#e0e3ff',
|
||||||
|
100: '#cdd2ff',
|
||||||
|
200: '#9ea7ff',
|
||||||
|
300: '#7782fa',
|
||||||
|
DEFAULT: '#5865F2',
|
||||||
|
500: '#3d48c3',
|
||||||
|
600: '#293294',
|
||||||
|
700: '#1a2165',
|
||||||
|
800: '#0e1137',
|
||||||
|
900: '#020208',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
presets: [
|
||||||
|
presetUno({ dark: 'class' }),
|
||||||
|
presetWebFonts({
|
||||||
|
provider: 'bunny',
|
||||||
|
fonts: {
|
||||||
|
mono: ['JetBrains Mono', 'JetBrains Mono:400,600,700'],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
presetTypography({
|
||||||
|
cssExtend: {
|
||||||
|
pre: {
|
||||||
|
padding: '1em',
|
||||||
|
'line-height': '1.5',
|
||||||
|
'border-radius': '4px',
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
'font-size': '1em',
|
||||||
|
'font-weight': 'unset',
|
||||||
|
},
|
||||||
|
':where(:not(pre) > code)::before': {
|
||||||
|
content: '""',
|
||||||
|
},
|
||||||
|
':where(:not(pre) > code)::after': {
|
||||||
|
content: '""',
|
||||||
|
},
|
||||||
|
a: {
|
||||||
|
color: '#5865F2',
|
||||||
|
'text-decoration': 'none',
|
||||||
|
},
|
||||||
|
'a > img': {
|
||||||
|
display: 'inline-block',
|
||||||
|
},
|
||||||
|
h2: {
|
||||||
|
'margin-top': '1.25em',
|
||||||
|
},
|
||||||
|
h3: {
|
||||||
|
'margin-top': '0.75em',
|
||||||
|
},
|
||||||
|
// eslint-disable-next-line id-length
|
||||||
|
p: {
|
||||||
|
margin: '.5em 0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"extends": ["../../.eslintrc.json", "neon/react", "neon/next", "neon/edge"],
|
"extends": ["../../.eslintrc.json", "neon/react", "neon/next", "neon/edge", "neon/prettier"],
|
||||||
"settings": {
|
"settings": {
|
||||||
"react": {
|
"react": {
|
||||||
"version": "detect"
|
"version": "detect"
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
"dependsOn": ["^build"],
|
"dependsOn": ["^build"],
|
||||||
"outputs": ["dist/**"]
|
"outputs": ["dist/**"]
|
||||||
},
|
},
|
||||||
|
"@discordjs/guide#build:prod": {
|
||||||
|
"dependsOn": ["^build"],
|
||||||
|
"outputs": ["dist/**"]
|
||||||
|
},
|
||||||
"@discordjs/website#build:prod": {
|
"@discordjs/website#build:prod": {
|
||||||
"dependsOn": ["^build"],
|
"dependsOn": ["^build"],
|
||||||
"outputs": [".next/**"]
|
"outputs": [".next/**"]
|
||||||
|
|||||||