chore: upgrade deps (#10824)

This commit is contained in:
Noel
2025-04-05 13:18:56 +02:00
committed by GitHub
parent 432aba3df7
commit f580de8025
200 changed files with 6756 additions and 12893 deletions

48
.vscode/settings.json vendored
View File

@@ -1,32 +1,54 @@
{
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
"eslint.experimental.useFlatConfig": true,
"eslint.workingDirectories": [
{ "directory": "${workspaceFolder}" },
{ "pattern": "./apps/*/" },
{ "pattern": "./packages/*/" }
],
"eslint.useESLintClass": true,
"eslint.useFlatConfig": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "never",
"source.fixAll.eslint": "explicit",
"source.fixAll": "explicit"
"source.fixAll.eslint": "always",
"source.fixAll": "always"
},
"editor.trimAutoWhitespace": false,
"files.associations": {
"api-extractor.json": "jsonc",
"api-extractor-docs.json": "jsonc",
"tsconfig.json": "jsonc",
"tsconfig.eslint.json": "jsonc",
"tsconfig.docs.json": "jsonc"
"tsconfig.eslint.json": "jsonc"
},
"files.insertFinalNewline": true,
"files.eol": "\n",
"search.exclude": {
"**/.yarn": true,
"**/.next": true,
"**/dist": true,
"**/coverage": true,
"**/test-results": true
},
"search.followSymlinks": false,
"search.useParentIgnoreFiles": true,
"files.watcherExclude": {
"**/.next/*/**": true,
"**/.yarn/*/**": true,
"**/coverage/*/**": true,
"**/dist/*/**": true,
"**/test-results/*/**": true
},
"unocss.disable": true,
"npm.packageManager": "pnpm",
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"unocss.disable": true,
"tailwindCSS.experimental.classRegex": [
["cva\\(((?:[^()]|\\([^()]*\\))*)\\)", "[\"'`]?([^\"'`]+)[\"'`]?"],
["cx\\(((?:[^()]|\\([^()]*\\))*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"],
["class:\\s*?[\"'`]([^\"'`]*).*?,"]
],
"workbench.editor.customLabels.patterns": {
"**/app/**/page.tsx": "${dirname} (${filename}.${extname}) - Page",
"**/app/**/layout.tsx": "${dirname} (${filename}.${extname}) - Layout",
"**/app/**/template.tsx": "${dirname} (${filename}.${extname}) - Template",
"**/app/**/error.tsx": "${dirname} (${filename}.${extname}) - Error",
"**/app/**/not-found.tsx": "${dirname} (${filename}.${extname}) - Not Found",
"**/components/**/page.tsx": "${dirname} (${filename}.${extname}) - Component"
},
"deno.enable": false,
"deno.enablePaths": ["./packages/create-discord-bot/template/Deno"],
"deno.lint": false,

View File

@@ -1 +0,0 @@
METADATA_BASE_URL=http://localhost:3000

28
apps/guide/.gitignore vendored
View File

@@ -1,28 +0,0 @@
# Packages
node_modules
# Log files
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Env
.env
.env*.local
# Dist
.contentlayer
.next
public/searchIndex
src/styles/unocss.css
# Miscellaneous
.tmp
.vscode
lighthouse-results

View File

@@ -1,2 +0,0 @@
/** @type {import('lint-staged').Config} */
module.exports = require('../../.lintstagedrc.json');

View File

@@ -1,7 +0,0 @@
.contentlayer
.next
.turbo
.vscode
coverage
src/styles/unocss.css
next-env.d.ts

View File

@@ -1,2 +0,0 @@
/** @type {import('prettier').Config} */
module.exports = require('../../.prettierrc.json');

View File

@@ -1,52 +0,0 @@
<div align="center">
<br />
<p>
<a href="https://discord.js.org"><img src="https://discord.js.org/static/logo.svg" width="546" alt="discord.js" /></a>
</p>
<br />
<p>
<a href="https://discord.gg/djs"><img src="https://img.shields.io/discord/222078108977594368?color=5865F2&logo=discord&logoColor=white" alt="Discord server" /></a>
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.yml/badge.svg" alt="Build status" /></a>
</p>
<p>
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>
<a href="https://www.cloudflare.com"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-workers.png" alt="Cloudflare Workers" height="44" /></a>
</p>
</div>
## About
The official guide for discord.js, made to help you get started easily with the library.
## Links
- [Website][website] ([source][website-source])
- [Documentation][documentation]
- [Guide][guide] ([source][guide-source])
Also see the v13 to v14 [Update Guide][guide-update], which includes updated and removed items from the library.
- [discord.js Discord server][discord]
- [Discord API Discord server][discord-api]
- [GitHub][source]
- [Related libraries][related-libs]
## Contributing
Before creating an issue, please ensure that it hasn't already been reported/suggested, and double-check the
[documentation][documentation].
See [the contribution guide][contributing] if you'd like to submit a PR.
## Help
If you don't understand something in the documentation, you are experiencing problems, or you just need a gentle nudge in the right direction, please don't hesitate to join our official [discord.js Server][discord].
[website]: https://discord.js.org
[website-source]: https://github.com/discordjs/discord.js/tree/main/apps/website
[documentation]: https://discord.js.org/docs
[guide]: https://discordjs.guide/
[guide-source]: https://github.com/discordjs/guide
[guide-update]: https://discordjs.guide/additional-info/changes-in-v14.html
[discord]: https://discord.gg/djs
[discord-api]: https://discord.gg/discord-api
[source]: https://github.com/discordjs/discord.js/tree/main/apps/guide
[related-libs]: https://discord.com/developers/docs/topics/community-resources#libraries
[contributing]: https://github.com/discordjs/discord.js/blob/main/.github/CONTRIBUTING.md

View File

@@ -1,94 +0,0 @@
import { remarkCodeHike } from '@code-hike/mdx';
import { defineDocumentType, makeSource } from 'contentlayer/source-files';
import { type Node, toString } from 'hast-util-to-string';
import { h } from 'hastscript';
import { escape } from 'html-escaper';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
import rehypeSlug from 'rehype-slug';
import remarkGfm from 'remark-gfm';
import codeHikeThemeDarkPlus from './src/styles/code-hike-theme-dark-plus.json';
export const Content = defineDocumentType(() => ({
name: 'Content',
filePathPattern: `**/*.mdx`,
contentType: 'mdx',
fields: {
title: {
type: 'string',
required: true,
},
category: {
type: 'string',
required: true,
},
},
computedFields: {
slug: {
type: 'string',
// eslint-disable-next-line unicorn/prefer-string-replace-all
resolve: (doc) => doc._raw.flattenedPath.replace(/\d+-/g, ''),
},
url: {
type: 'string',
// eslint-disable-next-line unicorn/prefer-string-replace-all
resolve: (doc) => `/guide/${doc._raw.flattenedPath.replace(/\d+-/g, '')}`,
},
},
}));
const LinkIcon = h(
'svg',
{
width: '1.25rem',
height: '1.25rem',
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
strokeWidth: '2',
strokeLinecap: 'round',
strokeLinejoin: 'round',
},
h('path', {
// eslint-disable-next-line id-length
d: 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71',
}),
h('path', {
// eslint-disable-next-line id-length
d: 'M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71',
}),
);
const createSROnlyLabel = (text: any) => {
return h('span', { class: 'sr-only' }, `Section titled ${escape(text)}`);
};
export default makeSource({
contentDirPath: 'src/content',
documentTypes: [Content],
mdx: {
remarkPlugins: [remarkGfm, [remarkCodeHike, { theme: codeHikeThemeDarkPlus, lineNumbers: true }]],
rehypePlugins: [
rehypeSlug,
[
rehypeAutolinkHeadings,
{
properties: {
class:
'relative inline-flex place-items-center place-content-center outline-none text-black dark:text-white pr-2 -ml-8 opacity-0 group-hover:opacity-100',
},
behavior: 'prepend',
content: (heading: Node) => [
h(
`span.anchor-icon`,
{
ariaHidden: 'true',
},
LinkIcon,
),
createSROnlyLabel(toString(heading)),
],
},
],
],
},
});

View File

@@ -1,5 +0,0 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@@ -1,17 +0,0 @@
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable @typescript-eslint/no-require-imports */
// import { withContentlayer } from 'next-contentlayer';
const { withContentlayer } = require('next-contentlayer');
module.exports = withContentlayer({
reactStrictMode: true,
experimental: {
typedRoutes: true,
},
images: {
dangerouslyAllowSVG: true,
contentDispositionType: 'attachment',
contentSecurityPolicy: "default-src 'self'; frame-src 'none'; sandbox;",
},
poweredByHeader: false,
});

View File

@@ -1,94 +0,0 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@discordjs/guide",
"version": "0.1.0",
"description": "Imagine a guide... that explores the many possibilities for your discord.js bot",
"private": true,
"scripts": {
"build:check": "tsc --noEmit",
"build:local": "pnpm run build:prod",
"build:prod": "next build",
"build:analyze": "cross-env ANALYZE=true pnpm run build:prod",
"preview": "next start",
"dev": "next dev",
"generate:contentlayer": "contentlayer build",
"lint": "pnpm run build:check && prettier --check . && cross-env TIMING=1 eslint --format=pretty src",
"format": "pnpm run build:check && prettier --write . && cross-env TIMING=1 eslint --fix --format=pretty src",
"fmt": "pnpm run format"
},
"type": "commonjs",
"directories": {
"lib": "src"
},
"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",
"directory": "apps/guide"
},
"bugs": {
"url": "https://github.com/discordjs/discord.js/issues"
},
"homepage": "https://discord.js.org",
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"@code-hike/mdx": "^0.9.0",
"@discordjs/ui": "workspace:^",
"@react-icons/all-files": "^4.1.0",
"@vercel/analytics": "^1.4.1",
"@vercel/edge-config": "^1.4.0",
"@vercel/og": "^0.6.4",
"ariakit": "2.0.0-next.44",
"cmdk": "^1.0.4",
"contentlayer": "^0.3.4",
"next": "^14.2.22",
"next-contentlayer": "^0.3.4",
"next-themes": "^0.3.0",
"react": "^18.3.1",
"react-custom-scrollbars-2": "^4.5.0",
"react-dom": "^18.3.1",
"rehype-autolink-headings": "^6.1.1",
"rehype-slug": "^5.1.0",
"remark-gfm": "^3.0.1",
"sharp": "^0.33.5"
},
"devDependencies": {
"@types/html-escaper": "^3.0.2",
"@types/node": "^18.19.68",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@unocss/eslint-plugin": "^0.60.4",
"@unocss/postcss": "^0.60.4",
"@unocss/reset": "^0.60.4",
"@vitejs/plugin-react": "^4.3.4",
"cross-env": "^7.0.3",
"eslint": "^8.57.1",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"happy-dom": "^14.12.3",
"hast-util-to-string": "^2.0.0",
"hastscript": "^8.0.0",
"html-escaper": "^3.0.3",
"postcss": "^8.4.49",
"prettier": "^3.4.2",
"turbo": "^2.3.3",
"typescript": "~5.5.4",
"unocss": "^0.60.4",
"vercel": "^37.14.0"
},
"engines": {
"node": ">=18"
}
}

View File

@@ -1,5 +0,0 @@
module.exports = {
plugins: {
'@unocss/postcss': {},
},
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

View File

@@ -1,12 +0,0 @@
<?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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.2 KiB

View File

@@ -1,32 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,19 +0,0 @@
{
"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"
}

View File

@@ -1,26 +0,0 @@
'use client';
import { inter } from '~/util/fonts';
import { Providers } from './providers';
import '~/styles/cmdk.css';
import '~/styles/main.css';
export default function GlobalError({ error }: { readonly error: Error }) {
console.error(error);
return (
<html className={inter.variable} lang="en" suppressHydrationWarning>
<body className="bg-light-600 dark:bg-dark-600 dark:text-light-900">
<Providers>
<main className="mx-auto max-w-2xl min-h-screen">
<div className="mx-auto max-w-lg min-h-screen flex flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">500</h1>
<h2 className="text-[2rem] md:text-[3rem]">Error.</h2>
</div>
</main>
</Providers>
</body>
</html>
);
}

View File

@@ -1,12 +0,0 @@
'use client';
export default function Error({ error }: { readonly error: Error }) {
console.error(error);
return (
<div className="mx-auto max-w-lg min-h-screen flex flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">500</h1>
<h2 className="text-[2rem] md:text-[3rem]">Error.</h2>
</div>
);
}

View File

@@ -1 +0,0 @@
export { default } from '~/app/not-found';

View File

@@ -1,21 +0,0 @@
import { notFound } from 'next/navigation';
import { allContents } from 'contentlayer/generated';
import { Mdx } from '~/components/Mdx';
export async function generateStaticParams() {
return allContents.map((content) => ({ slug: [content.slug] }));
}
export default function Page({ params }: { readonly params: { slug: string[] } }) {
const content = allContents.find((content) => content.slug === params.slug?.join('/'));
if (!content) {
notFound();
}
return (
<article className="max-w-none px-5 prose">
<Mdx code={content?.body.code ?? ''} />
</article>
);
}

View File

@@ -1,25 +0,0 @@
import type { PropsWithChildren } from 'react';
import Footer from '~/components/Footer';
import Header from '~/components/Header';
import { Nav } from '~/components/Nav';
import { Providers } from './providers';
export default function Layout({ children }: PropsWithChildren) {
return (
<Providers>
<main className="mx-auto max-w-7xl px-4 lg:max-w-full">
<Header />
<div className="relative top-6 mx-auto max-w-7xl gap-6 lg:max-w-full lg:flex">
<div className="lg:sticky lg:top-23 lg:h-[calc(100vh_-_105px)]">
<Nav />
</div>
<div className="mx-auto max-w-5xl min-w-xs w-full pb-10">
{children}
<Footer />
</div>
</div>
</main>
</Providers>
);
}

View File

@@ -1,3 +0,0 @@
export default function Page() {
return null;
}

View File

@@ -1,8 +0,0 @@
'use client';
import type { PropsWithChildren } from 'react';
import { NavProvider } from '~/contexts/nav';
export function Providers({ children }: PropsWithChildren) {
return <NavProvider>{children}</NavProvider>;
}

View File

@@ -1,84 +0,0 @@
import { Analytics } from '@vercel/analytics/react';
import type { Metadata, Viewport } from 'next';
import type { PropsWithChildren } from 'react';
import { DESCRIPTION } from '~/util/constants';
import { inter, jetBrainsMono } from '~/util/fonts';
import { Providers } from './providers';
import '~/styles/cmdk.css';
import '@code-hike/mdx/styles.css';
import '~/styles/ch.css';
import '~/styles/main.css';
export const viewport: Viewport = {
themeColor: [
{ media: '(prefers-color-scheme: light)', color: '#f1f3f5' },
{ media: '(prefers-color-scheme: dark)', color: '#181818' },
],
colorScheme: 'light dark',
};
export const metadata: Metadata = {
metadataBase: new URL(
process.env.METADATA_BASE_URL ? process.env.METADATA_BASE_URL : `http://localhost:${process.env.PORT ?? 3_000}`,
),
title: 'discord.js',
description: DESCRIPTION,
icons: {
other: [
{
url: '/favicon-32x32.png',
sizes: '32x32',
type: 'image/png',
},
{
url: '/favicon-16x16.png',
sizes: '16x16',
type: 'image/png',
},
],
apple: [
'/apple-touch-icon.png',
{
url: '/safari-pinned-tab.svg',
rel: 'mask-icon',
},
],
},
manifest: '/site.webmanifest',
appleWebApp: {
title: 'discord.js',
},
applicationName: 'discord.js',
openGraph: {
siteName: 'discord.js',
type: 'website',
title: 'discord.js',
description: DESCRIPTION,
images: 'https://discordjs.dev/api/open-graph.png',
},
twitter: {
card: 'summary_large_image',
creator: '@iCrawlToGo',
},
other: {
'msapplication-TileColor': '#090a16',
},
};
export default function RootLayout({ children }: PropsWithChildren) {
return (
<html className={`${inter.variable} ${jetBrainsMono.variable}`} lang="en" suppressHydrationWarning>
<body className="bg-light-600 dark:bg-dark-600 dark:text-light-900">
<Providers>{children}</Providers>
<Analytics />
</body>
</html>
);
}

View File

@@ -1,20 +0,0 @@
export default function Loading() {
return (
<div className="mx-4 min-h-screen flex flex-col items-center justify-center gap-4">
<svg
className="h-9 w-9 animate-spin text-black dark:text-white"
fill="none"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path
className="opacity-75 dark:opacity-100"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
fill="currentColor"
/>
</svg>
<div className="text-lg font-medium">Loading...</div>
</div>
);
}

View File

@@ -1,16 +0,0 @@
import Link from 'next/link';
export default function NotFound() {
return (
<div className="mx-auto max-w-lg min-h-screen flex flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">404</h1>
<h2 className="text-[2rem] md:text-[3rem]">Not found.</h2>
<Link
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded bg-blurple px-6 text-base text-white font-semibold leading-none no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-white"
href="/guide"
>
Take me back
</Link>
</div>
);
}

View File

@@ -1,3 +0,0 @@
export default function Page() {
return null;
}

View File

@@ -1,8 +0,0 @@
'use client';
import { ThemeProvider } from 'next-themes';
import type { PropsWithChildren } from 'react';
export function Providers({ children }: PropsWithChildren) {
return <ThemeProvider attribute="class">{children}</ThemeProvider>;
}

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="212" height="44" fill="none"><rect width="212" height="44" fill="#000" rx="8"/><path fill="#fff" d="M60.438 15.227V26.5h1.406v-4.023h2.836c2.117 0 3.625-1.493 3.625-3.602 0-2.148-1.477-3.648-3.61-3.648h-4.257Zm1.406 1.25h2.484c1.633 0 2.531.851 2.531 2.398 0 1.492-.93 2.352-2.53 2.352h-2.485v-4.75Zm11.5 10.171c2.399 0 3.883-1.656 3.883-4.359 0-2.71-1.484-4.36-3.883-4.36-2.398 0-3.883 1.65-3.883 4.36 0 2.703 1.485 4.36 3.883 4.36Zm0-1.21c-1.594 0-2.492-1.157-2.492-3.149 0-2 .898-3.148 2.492-3.148 1.594 0 2.492 1.148 2.492 3.148 0 1.992-.898 3.148-2.492 3.148Zm15.954-7.36h-1.352l-1.656 6.735h-.125l-1.883-6.735h-1.29l-1.882 6.735h-.125l-1.656-6.735h-1.36l2.36 8.422h1.36l1.874-6.516h.125l1.883 6.516h1.367l2.36-8.422Zm4.523 1.04c1.336 0 2.227.984 2.258 2.476h-4.64c.101-1.492 1.039-2.477 2.382-2.477Zm2.219 5.202c-.352.742-1.086 1.14-2.172 1.14-1.43 0-2.36-1.054-2.43-2.718v-.062h6.055v-.516c0-2.617-1.383-4.234-3.656-4.234-2.313 0-3.797 1.718-3.797 4.367 0 2.664 1.46 4.351 3.797 4.351 1.844 0 3.156-.89 3.547-2.328H96.04Zm3.242 2.18h1.344v-5.219c0-1.187.93-2.047 2.211-2.047.266 0 .75.047.86.078V17.97a5.77 5.77 0 0 0-.672-.04c-1.117 0-2.086.579-2.336 1.4h-.125v-1.25h-1.281V26.5Zm8.899-7.383c1.336 0 2.227.985 2.258 2.477h-4.641c.102-1.492 1.04-2.477 2.383-2.477Zm2.219 5.203c-.352.742-1.086 1.14-2.172 1.14-1.43 0-2.359-1.054-2.43-2.718v-.062h6.055v-.516c0-2.617-1.383-4.234-3.656-4.234-2.313 0-3.797 1.718-3.797 4.367 0 2.664 1.461 4.351 3.797 4.351 1.844 0 3.156-.89 3.547-2.328H110.4Zm6.36 2.328c1.164 0 2.164-.554 2.695-1.492h.125V26.5h1.281V14.734h-1.343v4.672h-.118c-.476-.922-1.468-1.476-2.64-1.476-2.141 0-3.539 1.718-3.539 4.36 0 2.648 1.382 4.358 3.539 4.358Zm.312-7.507c1.524 0 2.477 1.218 2.477 3.148 0 1.945-.946 3.148-2.477 3.148-1.539 0-2.461-1.18-2.461-3.148 0-1.96.93-3.148 2.461-3.148Zm14.462 7.507c2.133 0 3.531-1.726 3.531-4.359 0-2.648-1.391-4.36-3.531-4.36-1.156 0-2.18.571-2.641 1.477h-.125v-4.672h-1.344V26.5h1.282v-1.344h.125c.531.938 1.531 1.492 2.703 1.492Zm-.313-7.507c1.539 0 2.453 1.18 2.453 3.148 0 1.969-.914 3.148-2.453 3.148-1.531 0-2.484-1.203-2.484-3.148s.953-3.148 2.484-3.148Zm6.04 10.406c1.492 0 2.164-.578 2.882-2.531l3.29-8.938h-1.43l-2.305 6.93h-.125l-2.312-6.93h-1.453l3.117 8.43-.157.5c-.351 1.015-.773 1.383-1.546 1.383-.188 0-.399-.008-.563-.04V29.5c.188.031.422.047.602.047Zm17.391-3.047 3.898-11.273h-2.148l-2.813 8.921h-.132l-2.836-8.921h-2.227l3.938 11.273h2.32Zm8.016-7.18c1.164 0 1.93.813 1.969 2.078h-4.024c.086-1.25.899-2.078 2.055-2.078Zm1.984 4.828c-.281.633-.945.985-1.906.985-1.273 0-2.094-.89-2.141-2.313v-.101h5.969v-.625c0-2.696-1.461-4.313-3.898-4.313-2.477 0-4.016 1.727-4.016 4.477s1.516 4.414 4.031 4.414c2.016 0 3.446-.969 3.797-2.524h-1.836Zm3.547 2.352h1.938v-4.938c0-1.195.875-1.976 2.133-1.976.328 0 .843.055.992.11v-1.798c-.18-.054-.524-.085-.805-.085-1.101 0-2.023.625-2.258 1.468h-.132v-1.328h-1.868V26.5Zm13.501-5.672c-.203-1.797-1.532-3.047-3.727-3.047-2.57 0-4.078 1.649-4.078 4.422 0 2.813 1.516 4.469 4.086 4.469 2.164 0 3.508-1.203 3.719-2.992h-1.844c-.203.89-.875 1.367-1.883 1.367-1.32 0-2.117-1.047-2.117-2.844 0-1.773.789-2.797 2.117-2.797 1.063 0 1.703.594 1.883 1.422h1.844Zm5.117-1.508c1.164 0 1.93.813 1.969 2.078h-4.024c.086-1.25.899-2.078 2.055-2.078Zm1.985 4.828c-.282.633-.946.985-1.907.985-1.273 0-2.093-.89-2.14-2.313v-.101h5.968v-.625c0-2.696-1.461-4.313-3.898-4.313-2.477 0-4.016 1.727-4.016 4.477s1.516 4.414 4.032 4.414c2.015 0 3.445-.969 3.796-2.524h-1.835Zm3.625 2.352h1.937V14.648h-1.937V26.5ZM23.325 13l9.325 16H14l9.325-16Z"/><path stroke="#5E5E5E" d="M43.5 0v44"/></svg>

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,91 +0,0 @@
import { FiExternalLink } from '@react-icons/all-files/fi/FiExternalLink';
import type { PropsWithChildren } from 'react';
import {
BASE_URL_DISCORD_API_TYPES,
DISCORD_API_TYPES_VERSION,
DISCORD_API_TYPES_VOICE_VERSION,
} from '~/util/constants';
interface DiscordAPITypesLinkOptions {
/**
* The initial documentation enum, interface, function etc.
*
* @example `'RESTJSONErrorCodes'`
*/
readonly parent?: string;
/**
* The scope of where this link lives.
*
* @remarks API does not have a scope.
*/
readonly scope?: 'gateway' | 'globals' | 'payloads' | 'rest' | 'rpc' | 'utils' | 'voice';
/**
* The symbol belonging to the parent.
*
* @example '`MaximumNumberOfGuildsReached'`
*/
readonly symbol?: string;
/**
* The type of the {@link DiscordAPITypesLinkOptions.parent}.
*
* @example `'enum'`
* @example `'interface'`
*/
readonly type?: string;
}
export function DiscordAPITypesLink({
parent,
scope,
symbol,
type,
children,
}: PropsWithChildren<DiscordAPITypesLinkOptions>) {
let url = BASE_URL_DISCORD_API_TYPES;
let text = 'discord-api-types';
if (type || parent) {
url += `/api/discord-api-types`;
switch (scope) {
case 'globals':
url += `-${scope}`;
break;
case 'gateway':
case 'payloads':
case 'rest':
url += `-${scope}/common`;
break;
case 'rpc':
case 'utils':
url += `-${scope}/${DISCORD_API_TYPES_VERSION}`;
break;
case 'voice':
url += `-${scope}/${DISCORD_API_TYPES_VOICE_VERSION}`;
break;
default:
url += `-${DISCORD_API_TYPES_VERSION}`;
}
if (type) {
url += `/${type}/${parent}`;
if (symbol) url += `#${symbol}`;
} else {
url += `#${parent}`;
}
text = `${parent}${symbol ? `#${symbol}` : ''}${type?.toUpperCase() === 'FUNCTION' ? '()' : ''}`;
}
return (
<a
className="inline-flex flex-row place-items-center gap-1"
href={url}
rel="external noopener noreferrer"
target="_blank"
>
{children ?? text}
<FiExternalLink size={18} />
</a>
);
}

View File

@@ -1,86 +0,0 @@
import { FiExternalLink } from '@react-icons/all-files/fi/FiExternalLink';
import type { PropsWithChildren } from 'react';
import { BASE_URL, BASE_URL_LEGACY, PACKAGES, VERSION } from '~/util/constants';
interface DocsLinkOptions {
/**
* Whether to apply brackets to the end of the symbol to denote a method.
*
* @remarks Functions automatically infer this.
*/
readonly brackets?: boolean;
/**
* The package.
*
* @defaultValue `'discord.js'`
*/
readonly package?: (typeof PACKAGES)[number];
/**
* The initial documentation class, function, interface etc.
*
* @example `'Client'`
*/
readonly parent?: string;
/**
* Whether to reference a static property.
*
* @remarks
* This should only be used for the https://discord.js.org domain
* as static properties are not identified in the URL.
*/
readonly static?: boolean;
/**
* The symbol belonging to the parent.
*
* @example '`login'`
*/
readonly symbol?: string;
/**
* The type of the {@link DocsLinkOptions.parent}.
*
* @example `'class'`
* @example `'Function'`
*/
readonly type?: string;
}
export function DocsLink({
package: docs = PACKAGES[0],
type,
parent,
symbol,
brackets,
static: staticReference,
children,
}: PropsWithChildren<DocsLinkOptions>) {
// In the case of no type and no parent, this will default to the entry point of the respective documentation.
let url = docs === PACKAGES[0] ? `${BASE_URL_LEGACY}/${VERSION}/general/welcome` : `${BASE_URL}/${docs}/stable`;
let text = `${docs === PACKAGES[0] ? '' : '@discordjs/'}${docs}`;
// If there is a type and parent, we need to do some parsing.
if (type && parent) {
const bracketText = brackets || type?.toUpperCase() === 'FUNCTION' ? '()' : '';
// Legacy discord.js documentation parsing.
if (docs === PACKAGES[0]) {
url = `${BASE_URL_LEGACY}/${VERSION}/${type}/${parent}`;
if (symbol) url += `?scrollTo=${symbol}`;
text = `${parent}${symbol ? (symbol.startsWith('s-') ? '.' : '#') : ''}${
// eslint-disable-next-line prefer-named-capture-group
symbol ? `${symbol.replace(/(e|s)-/, '')}` : ''
}${bracketText}`;
} else {
url += `/${parent}:${type}`;
if (symbol) url += `#${symbol}`;
text = `${parent}${symbol ? `${staticReference ? '.' : '#'}${symbol}` : ''}${bracketText}`;
}
}
return (
<a className="inline-flex flex-row place-items-center gap-1" href={url} rel="noopener noreferrer" target="_blank">
{children ?? text}
<FiExternalLink size={18} />
</a>
);
}

View File

@@ -1,98 +0,0 @@
import Image from 'next/image';
import vercelLogo from '~/assets/powered-by-vercel.svg';
import workersLogo from '~/assets/powered-by-workers.png';
export default function Footer() {
return (
<footer className="md:pl-12 md:pr-12">
<div className="flex flex-col flex-wrap place-content-center gap-6 pt-12 sm:flex-row md:gap-12">
<div className="flex flex-wrap place-content-center place-items-center gap-4">
<a
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"
rel="external noopener noreferrer"
target="_blank"
title="Vercel"
>
<Image
alt="Vercel"
blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAABLCAQAAAA1k5H2AAAAi0lEQVR42u3SMQEAAAgDoC251a3gL2SgmfBYBRAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARCAgwWEOSWBnYbKggAAAABJRU5ErkJggg=="
height={44}
placeholder="blur"
src={vercelLogo}
width={212}
/>
</a>
<a
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://www.cloudflare.com"
rel="external noopener noreferrer"
target="_blank"
title="Cloudflare Workers"
>
<Image
alt="Cloudflare"
blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAABLCAQAAAA1k5H2AAAAi0lEQVR42u3SMQEAAAgDoC251a3gL2SgmfBYBRAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARCAgwWEOSWBnYbKggAAAABJRU5ErkJggg=="
height={44}
placeholder="blur"
priority
src={workersLogo}
/>
</a>
</div>
<div className="flex flex-col place-self-center gap-6 sm:flex-row md:gap-12">
<div className="max-w-max flex flex-col gap-2">
<div className="text-lg font-semibold">Community</div>
<div className="flex flex-col gap-1">
<a
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://discord.gg/djs"
rel="external noopener noreferrer"
target="_blank"
>
Discord
</a>
<a
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://github.com/discordjs/discord.js/discussions"
rel="external noopener noreferrer"
target="_blank"
>
GitHub discussions
</a>
</div>
</div>
<div className="max-w-max flex flex-col gap-2">
<div className="text-lg font-semibold">Project</div>
<div className="flex flex-col gap-1">
<a
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://github.com/discordjs/discord.js"
rel="external noopener noreferrer"
target="_blank"
>
discord.js
</a>
<a
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://discord.js.org/docs"
rel="noopener noreferrer"
target="_blank"
>
discord.js documentation
</a>
<a
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://discord-api-types.dev"
rel="external noopener noreferrer"
target="_blank"
>
discord-api-types
</a>
</div>
</div>
</div>
</div>
</footer>
);
}

View File

@@ -1,9 +0,0 @@
import type { HTMLAttributes, PropsWithChildren } from 'react';
export function H1({ children, className, ...props }: PropsWithChildren<HTMLAttributes<HTMLHeadingElement>>) {
return (
<h1 className={`group ${className}`} {...props}>
{children}
</h1>
);
}

View File

@@ -1,9 +0,0 @@
import type { HTMLAttributes, PropsWithChildren } from 'react';
export function H2({ children, className, ...props }: PropsWithChildren<HTMLAttributes<HTMLHeadingElement>>) {
return (
<h2 className={`group ${className}`} {...props}>
{children}
</h2>
);
}

View File

@@ -1,9 +0,0 @@
import type { HTMLAttributes, PropsWithChildren } from 'react';
export function H3({ children, className, ...props }: PropsWithChildren<HTMLAttributes<HTMLHeadingElement>>) {
return (
<h3 className={`group ${className}`} {...props}>
{children}
</h3>
);
}

View File

@@ -1,9 +0,0 @@
import type { HTMLAttributes, PropsWithChildren } from 'react';
export function H4({ children, className, ...props }: PropsWithChildren<HTMLAttributes<HTMLHeadingElement>>) {
return (
<h4 className={`group ${className}`} {...props}>
{children}
</h4>
);
}

View File

@@ -1,92 +0,0 @@
'use client';
import { VscGithubInverted } from '@react-icons/all-files/vsc/VscGithubInverted';
import { VscMenu } from '@react-icons/all-files/vsc/VscMenu';
import { Button } from 'ariakit/button';
import type { Route } from 'next';
import dynamic from 'next/dynamic';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { Fragment, useMemo } from 'react';
import { useNav } from '~/contexts/nav';
const ThemeSwitcher = dynamic(async () => import('./ThemeSwitcher'));
export default function Header() {
const pathname = usePathname();
const { setOpened } = useNav();
const pathElements = useMemo(
() =>
pathname
.split('/')
.slice(1)
.map((path, idx, original) => (
<Link
className="rounded outline-none hover:underline focus:ring focus:ring-width-2 focus:ring-blurple"
href={`/${original.slice(0, idx + 1).join('/')}` as Route}
key={`${path}-${idx}`}
>
{path}
</Link>
)),
[pathname],
);
const breadcrumbs = useMemo(
() =>
pathElements.flatMap((el, idx, array) => {
if (idx === 0) {
return (
<Fragment key={`${el.key}-${idx}`}>
<div className="mx-2">/</div>
<div>{el}</div>
<div className="mx-2">/</div>
</Fragment>
);
}
if (idx !== array.length - 1) {
return (
<Fragment key={`${el.key}-${idx}`}>
<div>{el}</div>
<div className="mx-2">/</div>
</Fragment>
);
}
return <div key={`${el.key}-${idx}`}>{el}</div>;
}),
[pathElements],
);
return (
<header className="sticky top-4 z-20 border border-light-900 rounded-md bg-white/75 shadow backdrop-blur-md dark:border-dark-100 dark:bg-dark-600/75">
<div className="block h-16 px-6">
<div className="h-full flex flex-row place-content-between place-items-center gap-8">
<Button
aria-label="Menu"
className="h-6 w-6 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-none lg:hidden active:translate-y-px focus:ring focus:ring-width-2 focus:ring-blurple"
onClick={() => setOpened((open) => !open)}
>
<VscMenu size={24} />
</Button>
<div className="hidden lg:flex lg:grow lg:flex-row lg:overflow-hidden">{breadcrumbs}</div>
<div className="flex flex-row place-items-center gap-4">
<Button
aria-label="GitHub"
as="a"
className="h-6 w-6 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded rounded-full bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-blurple"
href="https://github.com/discordjs/discord.js"
rel="external noopener noreferrer"
target="_blank"
>
<VscGithubInverted size={24} />
</Button>
<ThemeSwitcher />
</div>
</div>
</div>
</header>
);
}

View File

@@ -1,34 +0,0 @@
'use client';
import { Alert, Section, DiscordMessages, DiscordMessage, DiscordMessageEmbed } from '@discordjs/ui';
import { useMDXComponent } from 'next-contentlayer/hooks';
import { DocsLink } from '~/components/DocsLink';
import { ResultingCode } from '~/components/ResultingCode';
import { DiscordAPITypesLink } from './DiscordAPITypesLink';
import { H1 } from './H1';
import { H2 } from './H2';
import { H3 } from './H3';
import { H4 } from './H4';
export function Mdx({ code }: { readonly code: string }) {
const Component = useMDXComponent(code);
return (
<Component
components={{
Alert,
Section,
DiscordMessages,
DiscordMessage,
DiscordMessageEmbed,
DiscordAPITypesLink,
DocsLink,
ResultingCode,
h1: H1,
h2: H2,
h3: H3,
h4: H4,
}}
/>
);
}

View File

@@ -1,32 +0,0 @@
'use client';
import { Scrollbars } from 'react-custom-scrollbars-2';
import { useNav } from '~/contexts/nav';
import { Sidebar } from './Sidebar';
export function Nav() {
const { opened } = useNav();
return (
<nav
className={`dark:bg-dark-600/75 dark:border-dark-100 border-light-900 top-22 fixed bottom-4 left-4 right-4 z-20 mx-auto max-w-5xl rounded-md border bg-white/75 shadow backdrop-blur-md ${
opened ? 'block' : 'hidden'
} lg:min-w-xs lg:sticky lg:block lg:h-full lg:w-full lg:max-w-xs`}
>
<Scrollbars
autoHide
className="[&>div]:overscroll-none"
hideTracksWhenNotNeeded
renderThumbVertical={(props) => <div {...props} className="z-30 rounded bg-light-900 dark:bg-dark-100" />}
renderTrackVertical={(props) => (
<div {...props} className="absolute bottom-0.5 right-0.5 top-0.5 z-30 w-1.5 rounded" />
)}
universal
>
<div className="flex flex-col gap-4 p-3">
<Sidebar />
</div>
</Scrollbars>
</nav>
);
}

View File

@@ -1,69 +0,0 @@
import { useMemo, useState } from 'react';
import { Scrollbars } from 'react-custom-scrollbars-2';
const LINK_HEIGHT = 30;
const INDICATOR_SIZE = 10;
const INDICATOR_OFFSET = (LINK_HEIGHT - INDICATOR_SIZE) / 2;
export function Outline({ headings }: { readonly headings: any[] }) {
// eslint-disable-next-line react/hook-use-state
const [active /* setActive */] = useState(0);
const headingItems = useMemo(
() =>
headings.map((heading, idx) => (
<a
className={`dark:border-dark-100 border-light-800 pl-6.5 focus:ring-width-2 focus:ring-blurple ml-[10px] border-l p-[5px] text-sm outline-none focus:rounded focus:border-0 focus:ring ${
idx === active
? 'bg-blurple text-white'
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
}`}
href={`#${heading.slug}`}
key={heading.slug}
style={{ paddingLeft: `${heading.depth * 14}px` }}
title={heading.text}
>
<span className="line-clamp-1">{heading.text}</span>
</a>
)),
[headings, active],
);
// useEffect(() => {
// const idx = headings.findIndex((heading) => heading.slug === state.hash?.slice(1));
// if (idx >= 0) {
// setActive(idx);
// }
// }, [state, headings]);
return (
<Scrollbars
autoHide
hideTracksWhenNotNeeded
renderThumbVertical={(props) => <div {...props} className="z-30 rounded bg-light-900 dark:bg-dark-100" />}
renderTrackVertical={(props) => (
<div {...props} className="absolute bottom-0.5 right-0.5 top-0.5 z-30 w-1.5 rounded" />
)}
universal
>
<div className="flex flex-col break-all p-3 pb-8">
<div className="ml-2 mt-4 flex flex-row gap-2">
{/* <VscListSelection size={25} /> */}
<span className="font-semibold">Contents</span>
</div>
<div className="ml-2 mt-4 flex flex-col gap-2">
<div className="relative flex flex-col">
<div
className="absolute h-[10px] w-[10px] border-2 border-black rounded-full bg-blurple dark:border-white"
style={{
left: INDICATOR_SIZE / 2 + 0.5,
transform: `translateY(${active * LINK_HEIGHT + INDICATOR_OFFSET}px)`,
}}
/>
{headingItems}
</div>
</div>
</div>
</Scrollbars>
);
}

View File

@@ -1,21 +0,0 @@
export function PageButton({
url,
title,
direction,
}: {
readonly direction: 'next' | 'prev';
readonly title: string;
readonly url: string;
}) {
return (
<a
className="flex flex-row flex-col transform-gpu cursor-pointer select-none appearance-none place-items-center gap-2 rounded bg-light-600 px-4 py-3 leading-none no-underline outline-none active:translate-y-px active:bg-light-800 dark:bg-dark-600 hover:bg-light-700 focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-400 dark:hover:bg-dark-500"
href={url}
>
<h3 className="text-md font-semibold">{title}</h3>
<p className={`${direction === 'next' ? 'ml-auto' : 'mr-auto'} text-sm text-gray-600 dark:text-gray-400`}>
{direction === 'next' ? 'Next Page' : 'Previous Page'}
</p>
</a>
);
}

View File

@@ -1,3 +0,0 @@
export function ResultingCode() {
return null;
}

View File

@@ -1,8 +0,0 @@
'use client';
import { Section as DJSSection, type SectionOptions } from '@discordjs/ui';
import type { PropsWithChildren } from 'react';
export function Section(options: PropsWithChildren<SectionOptions>) {
return <DJSSection {...options} />;
}

View File

@@ -1,63 +0,0 @@
'use client';
import type { Route } from 'next';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { allContents } from 'contentlayer/generated';
import { useNav } from '~/contexts/nav';
import { Section } from './Section';
const items = allContents.map((content) => ({
title: content.title,
category: content.category,
slug: content.slug,
href: content.url,
}));
function transformItemsByCategory(allContents: typeof items) {
return allContents.reduce<Record<string, typeof items>>((accumulator: any, content) => {
if (!accumulator[content.category]) {
accumulator[content.category] = [];
}
accumulator[content.category].push(content);
return accumulator;
}, {});
}
const itemsByCategory = transformItemsByCategory(items);
export function Sidebar() {
const pathname = usePathname();
const { setOpened } = useNav();
return (
<div className="flex flex-col gap-4">
{Object.keys(itemsByCategory).map((category, idx) => (
<Section
buttonClassName="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-400 dark:hover:bg-dark-300 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple rounded p-3 outline-none focus:ring z-10"
key={`${category}-${idx}`}
title={category}
>
{itemsByCategory[category]?.map((member, index) => (
<Link
className={`dark:border-dark-100 border-light-800 focus:ring-width-2 focus:ring-blurple ml-5 flex flex-col border-l first:mt-1 p-[5px] pl-6 outline-none focus:rounded focus:border-0 focus:ring ${
decodeURIComponent(pathname ?? '') === member.href
? 'bg-blurple text-white'
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
}`}
href={member.href as Route}
key={`${member.title}-${index}`}
onClick={() => setOpened(false)}
title={member.title}
>
<div className="flex flex-row place-items-center gap-2 lg:text-sm">
<span className="truncate">{member.title}</span>
</div>
</Link>
))}
</Section>
))}
</div>
);
}

View File

@@ -1,20 +0,0 @@
'use client';
import { VscColorMode } from '@react-icons/all-files/vsc/VscColorMode';
import { Button } from 'ariakit/button';
import { useTheme } from 'next-themes';
export default function ThemeSwitcher() {
const { resolvedTheme, setTheme } = useTheme();
const toggleTheme = () => setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
return (
<Button
aria-label="Toggle theme"
className="h-6 w-6 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded rounded-full bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-blurple"
onClick={() => toggleTheme()}
>
<VscColorMode size={24} />
</Button>
);
}

View File

@@ -1,32 +0,0 @@
---
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](../getting-started/starting-out) from scratch;
- In-depth explanations regarding features and concepts of the API (e.g. [intents](../topics/intents), [threads](../topics/threads), [webhooks](../topics/webhooks));
- 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.
## 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://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/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!

View File

@@ -1,49 +0,0 @@
---
title: What's new
category: Home
---
# What's new
<DiscordMessages rounded>
<DiscordMessage
interaction={{
author: {
avatar: '/assets/old-guide.png',
username: 'discord.js',
},
command: '/upgrade',
}}
author={{
avatar: '/assets/discordjs.png',
bot: true,
username: 'Guide Bot',
time: 'Today at 21:00',
}}
>
This website is new! We will no longer be updating the old guide website.
</DiscordMessage>
</DiscordMessages>
## Site
We have moved from VuePress to [Next.js](https://nextjs.org/)! The source can be found [here](https://github.com/discordjs/discord.js/tree/main/apps/guide).
## Pages
- Pages have been revamped to account for our new [create-discord-bot](https://github.com/discordjs/discord.js/tree/main/packages/create-discord-bot) command-line interface.
- Popular topic are now simply "topics" that detail usage of a particular concept of the API.
- Focus is primarily on discord.js, so irrelevant topics have been removed. It may be better to visit the documentation of the package you are using to learn how to use them.
<DiscordMessages rounded>
<DiscordMessage
author={{
avatar: '/assets/discordjs.png',
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>

View File

@@ -1,198 +0,0 @@
---
title: How to contribute
category: Home
---
# How to contribute
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](../topics/frequently-asked-questions) page", or as elaborate as "add a page regarding [sharding](../topics/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 repository's issue tracker](https://github.com/discordjs/discord.js/issues) and [create a new issue](https://github.com/discordjs/discord.js/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!
<Alert title="Tip" type="info">
Remember that you can always [fork the repository](https://github.com/discordjs/discord.js/fork) and [make a pull
request](https://github.com/discordjs/discord.js/pulls) if you want to add anything to the guide yourself!
</Alert>
We'll also get into some of the more advanced features this guide uses below. We recommended you have a look at the [source](https://github.com/discordjs/discord.js/blob/main/apps/guide/src/content/01-home/03-how-to-contribute.mdx) of this page to see exactly how they work.
## Components
Throughout the guide, you'll see some components from the _`@discordjs/ui`_ package:
- _`Alert`_
- _`Section`_
- _`DiscordMessages`_, _`DiscordMessage`_, and _`DiscordMessageEmbed`_
Check the source of this page to see them in action!
### Alert
This component may take a _`title`_ and a _`type`_ of _`'danger' | 'info' | 'success' | 'warning'`_.
This uses _`title="Alert" type="info"`_:
<Alert title="Alert" type="info">
Use these appropriately!
</Alert>
### Section
<Section title="Expand me!" padding defaultClosed background gutter>
Well, hello there!
Whenever some text does not need to be in the main body, you can put it here.
- _`title`_: The title that'll appear.
- _`padding`_: Adds padding.
- _`dense`_: When _`padding`_ is specified, _`dense`_ could make it appear, well, dense.
- _`defaultClosed`_ Whether the section is closed by default. This one was.
- _`background`_ Adds background to the content.
- _`gutter`_: This adds a very small appealing space between the expansion of the section and its content.
</Section>
### DiscordMessages, DiscordMessage, and DiscordMessageEmbed
<DiscordMessages>
<DiscordMessage
author={{
avatar: '/assets/discordjs.png',
bot: true,
time: 'Today at 21:00',
username: 'Guide Bot',
}}
>
A _`DiscordMessage`_ must be within _`DiscordMessages`_.
</DiscordMessage>
<DiscordMessage
author={{
avatar: '/assets/discordjs.png',
bot: true,
time: 'Today at 21:01',
username: 'Guide Bot',
}}
reply={{
author: {
avatar: '/assets/discordjs.png',
bot: true,
username: 'Guide Bot',
},
content: 'A _`DiscordMessage`_ must be within _`DiscordMessages`_.',
}}
time="21:02"
>
It's much better to see the source code of this page to replicate and learn!
</DiscordMessage>
<DiscordMessage
author={{
avatar: '/assets/discordjs.png',
bot: true,
time: 'Today at 21:02',
username: 'Guide Bot',
}}
>
This message depicts the use of embeds.
<>
<DiscordMessageEmbed
author={{
avatar: '/assets/discordjs.png',
username: 'Guide Bot',
}}
footer={{ content: 'Sometimes, titles just have to be.' }}
title={{ title: 'An amazing title' }}
>
This is a description. You can put a description here. It must be descriptive!
</DiscordMessageEmbed>
<DiscordMessageEmbed
author={{
avatar: '/assets/discordjs.png',
username: 'Guide Bot',
}}
footer={{ content: "When one amazing title just wasn't enough." }}
title={{ title: 'Another amazing title' }}
>
Multiple embeds!
</DiscordMessageEmbed>
</>
</DiscordMessage>
<DiscordMessage
author={{
avatar: '/assets/discordjs.png',
bot: true,
time: 'Today at 21:03',
username: 'Guide Bot',
}}
interaction={{
author: {
avatar: '/assets/discordjs.png',
bot: true,
username: 'Guide Bot',
},
command: '/interaction',
}}
>
Interactions are supported! I definitely used a command.
</DiscordMessage>
<DiscordMessage
author={{
avatar: '/assets/discordjs.png',
bot: true,
color: 'text-red-500',
time: 'Today at 21:04',
username: 'Guide Bot',
}}
reply={{
author: {
avatar: '/assets/snek-bot.jpeg',
bot: true,
verified: true,
color: 'text-blue-500',
username: 'Snek Bot',
},
content: 'You can also have verified bots, like me!',
}}
>
Display colors are supported as well!
</DiscordMessage>
</DiscordMessages>
## Code blocks
We use [Code Hike](https://codehike.org). Here are some example code blocks, which should be easy to grasp and learn upon reading the source code of this page:
<CH.Code>
```ts
const HELLO = 'hello' as const;
console.log(HELLO);
// "ts" is the language of the code block.
```
</CH.Code>
<CH.Code>
```ts fileName
const FILE_NAME = 'fileName' as const;
if (FILE_NAME.includes(' ')) throw new Error('Spaces cannot be used in file names.');
```
```ts anotherFileName
const FILE_NAME_2 = 'anotherFileName' as const;
// Putting code blocks together makes them appear in tabs, just like in your editor.
```
---
```ts requiredName
const FILE_NAME_3 = 'requiredName' as const;
if (!FILE_NAME) throw new Error('There must be a file name to use panels!');
// The --- divider was used to create a panel.
```
</CH.Code>
For more information, be sure to check out the [documentation](https://codehike.org/docs/ch-code).

View File

@@ -1,64 +0,0 @@
---
title: Starting out
category: Getting started
---
# Starting out
Our [create-discord-bot](https://github.com/discordjs/discord.js/tree/main/packages/create-discord-bot) command-line interface sets up a basic Discord bot to help you get started on your journey.
## Creating your bot
To use discord.js, you'll need to install [Node.js](https://nodejs.org), [Deno](https://deno.com), or [Bun](https://bun.sh). discord.js v14 requires Node.js v16.11.0 or higher, but the long-term support (LTS) version is always recommended. For the purposes of this guide, we will be using Node.js.
<Alert title="Tip" type="info">
To check if you already have Node.js installed, run _`node --version`_ in your terminal. If it outputs _`v16.11.0`_ or
higher, then you're good to go!
</Alert>
### Windows
- Download from the [Node.js website](https://nodejs.org).
- Use [fnm](https://github.com/Schniz/fnm).
- Use [Volta](https://volta.sh).
### macOS
- Download from the [Node.js website](https://nodejs.org/).
- Use [fnm](https://github.com/Schniz/fnm).
- Use [Homebrew](https://formulae.brew.sh/formula/node).
- Use [nvm](https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updating).
- Use [Volta](https://volta.sh).
### Linux
- Visit [this page](https://nodejs.org/en/download/package-manager) to determine how you should install Node.js.
- Use [fnm](https://github.com/Schniz/fnm).
- Use [nvm](https://github.com/nvm-sh/nvm).
- Use [Volta](https://volta.sh).
After installing Node.js, you'll be able to create a new application from your desired package manager. If you're starting out fresh, installing Node.js will also install npm, a package manager for Node.js.
<CH.Code lineNumbers={false} showCopyButton={true}>
```sh npm
npm create discord-bot
```
```sh yarn
yarn create discord-bot
```
```sh pnpm
pnpm create discord-bot
```
```sh bun
bun create discord-bot
```
</CH.Code>
You'll be asked the directory to create the application in, as well as whether TypeScript should be used. Dependencies will automatically be installed for you. After this, you've just got your startup Discord bot template _nearly_ ready!
In the next section, we will explain how to create an application to interact with Discord's API.

View File

@@ -1,64 +0,0 @@
---
title: Setting up an application
category: Getting started
---
# Setting up an application
You'll need to create an application on Discord's developer portal so your bot has a token to interact with Discord's API.
## Creating the application
Follow these steps:
1. Open the [Discord developer portal](https://discord.com/developers/applications). You'll need to be logged in.
2. Click on the "New Application" button.
3. Enter a name and confirm the pop-up window by clicking the "Create" button.
- You'll need to agree to the [Developer Terms of Service](https://discord.com/developers/docs/policies-and-agreements/terms-of-service) and [Developer Policy](https://discord.com/developers/docs/policies-and-agreements/developer-policy).
You should see a page like this:
![Successfully created application](/assets/create-app.png)
You can edit your application's name, description, and avatar here. Copy the application id and paste it in the .env file after _`APPLICATION_ID=`_.
Once you've saved your changes, move on by selecting the "Bot" tab in the left pane.
## Your bot's token
<Alert title="Important" type="danger">
This section is critical, so pay close attention. It explains what your bot token is, as well as the security aspects
of it.
</Alert>
On the bot tab, you'll see a section like this:
![Bot application](/assets/bot-user.png)
In this panel, you can give your bot a snazzy avatar, set its username, and make it public or private. Your bot's token will be revealed when you press the "Reset Token" button and confirm. Once you've done this, copy it and paste it in the .env file after _`DISCORD_TOKEN=`_.
If you happen to lose this token at some point, you will need to come back to this page and reset it, which will reveal the new token, invalidating all old ones.
### Bot token explanation
A token is essentially your bot's password; it's what your bot uses to login to Discord. With that said, **it is vital that you do not ever share this token with anybody, purposely or accidentally**. If someone does manage to get a hold of your bot's token, they can use your bot as if it were theirs—this means they can perform malicious acts with it.
Tokens look like this: _`NzkyNzE1NDU0MTk2MDg4ODQy.X-hvzA.Ovy4MCQywSkoMRRclStW4xAYK7I`_ (don't worry, we immediately reset this token before even posting it here!). If it's any shorter and looks more like this: _`kxbsDRU5UfAaiO7ar9GFMHSlmTwYaIYn`_, you copied your client secret instead. Make sure to copy the token if you want your bot to work!
### Token leak scenario
Let's imagine that you have a bot on over 1,000 servers, and it took you many, many months of coding and patience to get it on that amount. Your bot's token gets leaked somewhere, and now someone else has it. That person can:
- Spam every server your bot is on;
- DM spam as many users as possible;
- Delete as many channels as possible;
- Kick or ban as many server members as possible;
- Make your bot leave all of the servers it has joined;
All that and much, much more. Sounds pretty terrible, right? So make sure to keep your bot's token as safe as possible!
<Alert title="Compromised tokens" type="danger">
If your bot token has been compromised by committing it to a public repository, posting it in discord.js support etc.
or otherwise see your bot's token in danger, return to this page and press "Reset Token". This will invalidate all old
tokens belonging to your bot. Keep in mind that you will need to update your bot's token where you used it before.
</Alert>

View File

@@ -1,48 +0,0 @@
---
title: Adding your bot to a server
category: Getting started
---
# Adding your bot to a server
After you [set up an application](./setting-up-an-application), you'll notice it's not in any servers yet. So, how does that work?
Before you're able to see your bot in a server, you will need to add it by using an invite link.
## Bot invite links
The basic version of one such link looks like this:
<CH.Code lineNumbers={false}>
```
https://discord.com/api/oauth2/authorize?client_id=123456789012345678&permissions=0&scope=bot
```
</CH.Code>
The structure of the URL is quite simple:
- _`https://discord.com/api/oauth2/authorize`_ is Discord's standard structure for authorizing an OAuth2 application (such as your bot application) for entry to a Discord server.
- _`client_id=...`_ is to specify _which_ application you want to authorize. You'll need to replace this part with your client's id to create a valid invite link.
- _`permissions=...`_ describes the permissions that your bot will request to be granted by default upon joining the server you are adding it to.
- _`scope=bot`_ specifies that you want to add this application as a Discord bot with the ability to create slash commands.
<Alert title="Warning" type="warning">
If you get an error message saying "Bot requires a code grant", head over to your application's settings and disable
the "Requires OAuth2 Code Grant" option. You shouldn't enable this option unless you know why you need to.
</Alert>
## Creating and using your invite link
To create an invite link, head back to the [developer portal](https://discord.com/developers/applications), click on your bot application, and open the OAuth2 page.
In the sidebar, you'll find the URL generator. Select the _`bot`_ option. Once you select the _`bot`_ option, a list of permissions will appear, allowing you to configure the permissions your bot needs.
Grab the link via the "Copy" button and send it in a channel in Discord. Click on the link you just sent which should reveal this:
![Bot Authorization page](/assets/bot-auth-page.png)
Choose the server you want to add the bot to and click "Authorize". Congratulations! You've successfully added your bot to your Discord server.
At this point, you should have a Discord bot you created with [create-discord-bot](https://github.com/discordjs/discord.js/tree/main/packages/create-discord-bot) with your .env file populated and your Discord bot in a server. You are now ready to do what you like.

View File

@@ -1,496 +0,0 @@
---
title: Frequently asked questions
category: Topics
---
# Frequently asked questions
## Legend
- _`client`_ is a placeholder for the <DocsLink type="class" parent="Client" /> object:
_`const client = new Client({ intents: [GatewayIntentBits.Guilds] });`_.
- _`interaction`_ is a placeholder for the <DocsLink type="class" parent="BaseInteraction" />:
_`client.on(Events.InteractionCreate, interaction => { ... });`_.
- _`guild`_ is a placeholder for the <DocsLink type="class" parent="Guild" /> object:
_`interaction.guild`_ or _`client.guilds.cache.get('id')`_
- _`voiceChannel`_ is a placeholder for the <DocsLink type="class" parent="VoiceChannel" />:
_`interaction.member.voice.channel`_.
For a more detailed explanation of the notations commonly used in this guide, the docs, and the support server, see [here](/additional-info/notation.md).
## Administrative
### How do I ban a user?
<CH.Code>
```js
const user = interaction.options.getUser('target');
await guild.members.ban(user);
```
</CH.Code>
### How do I unban a user?
<CH.Code>
```js
const user = interaction.options.getUser('target');
await guild.members.unban(user);
```
</CH.Code>
<Alert title="Tip" type="info">
Discord validates and resolves user ids for users not on the server in user slash command options. To retrieve and use
the full structure from the resulting interaction, you can use the{' '}
<DocsLink type="class" parent="CommandInteractionOptionResolver" symbol="getUser" brackets /> method.
</Alert>
### How do I kick a guild member?
<CH.Code>
```js
const member = interaction.options.getMember('target');
await member.kick();
```
</CH.Code>
### How do I timeout a guild member?
<CH.Code>
```js
const member = interaction.options.getMember('target');
await member.timeout(60_000); // Timeout for one minute
```
</CH.Code>
<Alert title="Tip" type="info">
Timeout durations are measured by the millisecond. The maximum timeout duration you can set is 28 days. To remove a
timeout set on a member, pass _`null`_ instead of a timeout duration.
</Alert>
### How do I add a role to a guild member?
<CH.Code>
```js
const role = interaction.options.getRole('role');
const member = interaction.options.getMember('target');
await member.roles.add(role);
```
</CH.Code>
### How do I check if a guild member has a specific role?
<CH.Code>
```js
const role = interaction.options.getRole('role');
const member = interaction.options.getMember('target');
if (member.roles.cache.has(role.id) {
// ...
}
```
</CH.Code>
### How do I limit a command to a single user?
<CH.Code>
```js
if (interaction.user.id === 'id') {
// ...
}
```
</CH.Code>
## Bot Configuration and Utility
### How do I set my bot's username?
<CH.Code>
```js
await client.user.setUsername('username');
```
</CH.Code>
### How do I set my bot's avatar?
<CH.Code>
```js
await client.user.setAvatar('URL or path');
```
</CH.Code>
### How do I set my playing status?
<CH.Code>
```js
client.user.setActivity('activity');
```
</CH.Code>
### How do I set my status to "Watching/Listening to/Competing in ..."?
<CH.Code>
```js
import { ActivityType } from 'discord.js';
client.user.setActivity('activity', { type: ActivityType.Watching });
client.user.setActivity('activity', { type: ActivityType.Listening });
client.user.setActivity('activity', { type: ActivityType.Competing });
```
</CH.Code>
<Alert title="Tip" type="info">
If you would like to set your activity upon startup, you can use the{' '}
<DocsLink type="typedef" parent="ClientOptions" /> object to set the appropriate
<DocsLink type="typedef" parent="PresenceData" />.
</Alert>
### How do I make my bot display online/idle/dnd/invisible?
<CH.Code>
```js
client.user.setStatus('online');
client.user.setStatus('idle');
client.user.setStatus('dnd');
client.user.setStatus('invisible');
```
</CH.Code>
### How do I set both status and activity in one go?
<CH.Code>
```js
client.user.setPresence({ activities: [{ name: 'activity' }], status: 'idle' });
```
</CH.Code>
## Miscellaneous
### How do I send a message to a specific channel?
<CH.Code>
```js
const channel = client.channels.cache.get('id');
await channel.send('content');
```
</CH.Code>
### How do I create a post in a forum channel?
<Alert title="Tip" type="info">
Currently, the only way to get tag ids is programmatically through{' '}
<DocsLink type="class" parent="ForumChannel" symbol="availableTags" />.
</Alert>
<CH.Code>
```js
const channel = client.channels.cache.get('id');
await channel.threads.create({
name: 'Post name',
message: { content: 'Message content' },
appliedTags: ['tagId', 'anotherTagId'],
});
```
</CH.Code>
### How do I DM a specific user?
<CH.Code>
```js
await client.users.send('id', 'content');
```
</CH.Code>
<Alert title="Tip" type="info">
If you want to send a direct message to the user who sent the interaction, you can use _`interaction.user.send()`_.
</Alert>
### How do I mention a specific user in a message?
<CH.Code>
```js
const user = interaction.options.getUser('target');
await interaction.reply(`Hi, ${user}.`);
await interaction.followUp(`Hi, <@${user.id}>.`);
```
</CH.Code>
<Alert title="Tip" type="info">
Mentions in embeds may resolve correctly in embed titles, descriptions and field values but will never notify the
user. Other areas do not support mentions at all.
</Alert>
### How do I control which users and/or roles are mentioned in a message?
Controlling which mentions will send a ping is done via the _`allowedMentions`_ option, which replaces _`disableMentions`_.
This can be set as a default in <DocsLink type="typedef" parent="ClientOptions" />, and controlled per-message sent by your bot.
<CH.Code>
```js
new Client({ allowedMentions: { parse: ['users', 'roles'] } });
```
</CH.Code>
Even more control can be achieved by listing specific _`users`_ or _`roles`_ to be mentioned by id, e.g.:
<CH.Code>
```js
await channel.send({
content: '<@123456789012345678> <@987654321098765432> <@&102938475665748392>',
allowedMentions: { users: ['123456789012345678'], roles: ['102938475665748392'] },
});
```
</CH.Code>
### How do I prompt the user for additional input?
<CH.Code>
```js
await interaction.reply('Please enter more input.');
const filter = (m) => interaction.user.id === m.author.id;
try {
const messages = await interaction.channel.awaitMessages({ filter, time: 60000, max: 1, errors: ['time'] });
await interaction.followUp(`You've entered: ${messages.first().content}`);
} catch {
await interaction.followUp('You did not enter any input!');
}
```
</CH.Code>
<Alert title="Tip" type="info">
If you want to learn more about this syntax or other types of collectors, check out [this dedicated guide page for
collectors](/popular-topics/collectors.md)!
</Alert>
### How do I block a user from using my bot?
<CH.Code>
```js
const blockedUsers = ['id1', 'id2'];
client.on(Events.InteractionCreate, (interaction) => {
if (blockedUsers.includes(interaction.user.id)) return;
});
```
</CH.Code>
<Alert title="Tip" type="info">
You do not need to have a constant local variable like _`blockedUsers`_ above. If you have a database system that you
use to store ids of blocked users, you can query the database instead.
</Alert>
<CH.Code>
```js
client.on(Events.InteractionCreate, async (interaction) => {
const blockedUsers = await database.query('SELECT user_id FROM blocked_users;');
if (blockedUsers.includes(interaction.user.id)) return;
});
```
</CH.Code>
Note that this is just a showcase of how you could do such a check.
### How do I react to the message my bot sent?
<CH.Code>
```js
const sentMessage = await interaction.channel.send('My message to react to.');
// Unicode emoji
await sentMessage.react('👍');
// Custom emoji
await sentMessage.react('123456789012345678');
await sentMessage.react('<emoji:123456789012345678>');
await sentMessage.react('<a:emoji:123456789012345678>');
await sentMessage.react('emoji:123456789012345678');
await sentMessage.react('a:emoji:123456789012345678');
```
</CH.Code>
<Alert title="Tip" type="info">
If you want to learn more about reactions, check out [this dedicated guide on
reactions](/popular-topics/reactions.md)!
</Alert>
### How do I restart my bot with a command?
<CH.Code>
```js
process.exit();
```
</CH.Code>
<Alert title="Warning" type="warning">
_`process.exit()`_ will only kill your Node process, but when using [PM2](https://pm2.keymetrics.io/), it will restart
the process whenever it gets killed. You can read our guide on PM2 [here](/improving-dev-environment/pm2.md).
</Alert>
### What is the difference between a User and a GuildMember?
A User represents a global Discord user, and a GuildMember represents a Discord user on a specific server. That means only GuildMembers can have permissions, roles, and nicknames, for example, because all of these things are server-bound information that could be different on each server that the user is in.
### How do I find all online members of a guild?
<CH.Code>
```js
// First use guild.members.fetch to make sure all members are cached
const fetchedMembers = await guild.members.fetch({ withPresences: true });
const totalOnline = fetchedMembers.filter((member) => member.presence?.status === 'online');
// Now you have a collection with all online member objects in the totalOnline variable
console.log(`There are currently ${totalOnline.size} members online in this guild!`);
```
</CH.Code>
<Alert title="Warning" type="warning">
This only works correctly if you have the _`GuildPresences`_ intent enabled for your application and client. If you
want to learn more about intents, check out [this dedicated guide on intents](/popular-topics/intents.md)!
</Alert>
### How do I check which role was added/removed and for which member?
<CH.Code>
```js
// Start by declaring a guildMemberUpdate listener
// This code should be placed outside of any other listener callbacks to prevent listener nesting
client.on(Events.GuildMemberUpdate, (oldMember, newMember) => {
// If the role(s) are present on the old member object but no longer on the new one (i.e role(s) were removed)
const removedRoles = oldMember.roles.cache.filter((role) => !newMember.roles.cache.has(role.id));
if (removedRoles.size > 0) {
console.log(`The roles ${removedRoles.map((r) => r.name)} were removed from ${oldMember.displayName}.`);
}
// If the role(s) are present on the new member object but are not on the old one (i.e role(s) were added)
const addedRoles = newMember.roles.cache.filter((role) => !oldMember.roles.cache.has(role.id));
if (addedRoles.size > 0) {
console.log(`The roles ${addedRoles.map((r) => r.name)} were added to ${oldMember.displayName}.`);
}
});
```
</CH.Code>
### How do I check the bot's ping?
There are two common measurements for bot pings. The first, **websocket heartbeat**, is the average interval of a regularly sent signal indicating the healthy operation of the websocket connection the library receives events over:
<CH.Code>
```js
await interaction.reply(`Websocket heartbeat: ${client.ws.ping}ms.`);
```
</CH.Code>
<Alert title="Tip" type="info">
If you're using [sharding](/sharding/), a specific shard's heartbeat can be found on the WebSocketShard instance,
accessible at _`client.ws.shards.get(id).ping`_.
</Alert>
The second, **Roundtrip Latency**, describes the amount of time a full API roundtrip (from the creation of the command message to the creation of the response message) takes. You then edit the response to the respective value to avoid needing to send yet another message:
<CH.Code>
```js
const sent = await interaction.reply({ content: 'Pinging...', fetchReply: true });
await interaction.editReply(`Roundtrip latency: ${sent.createdTimestamp - interaction.createdTimestamp}ms`);
```
</CH.Code>
### Why do some emojis behave weirdly?
If you've tried using [the usual method of retrieving unicode emojis](./reactions.md#unicode-emojis), you may have noticed that some characters don't provide the expected results. Here's a short snippet that'll help with that issue. You can toss this into a file of its own and use it anywhere you need! Alternatively feel free to simply copy-paste the characters from below:
<CH.Code>
```js index.js
import { emojiCharacters } from './emojiCharacters.js';
console.log(emojiCharacters.a); // 🇦
console.log(emojiCharacters[10]); // 🔟
console.log(emojiCharacters['!']); // ❗
```
{/* prettier-ignore */}
```js emojiCharacters.js
export const emojiCharacters = {
a: '🇦', b: '🇧', c: '🇨', d: '🇩',
e: '🇪', f: '🇫', g: '🇬', h: '🇭',
i: '🇮', j: '🇯', k: '🇰', l: '🇱',
m: '🇲', n: '🇳', o: '🇴', p: '🇵',
q: '🇶', r: '🇷', s: '🇸', t: '🇹',
u: '🇺', v: '🇻', w: '🇼', x: '🇽',
y: '🇾', z: '🇿', 0: '0⃣', 1: '1⃣',
2: '2⃣', 3: '3⃣', 4: '4⃣', 5: '5⃣',
6: '6⃣', 7: '7⃣', 8: '8⃣', 9: '9⃣',
10: '🔟', '#': '#️⃣', '*': '*️⃣',
'!': '❗', '?': '❓',
};
```
</CH.Code>
<Alert title="Tip" type="info">
You can use the <kbd>⌃ Control</kbd> <kbd>⌘ Command</kbd> <kbd>Space</kbd> keyboard shortcut to open up an emoji picker that can be used for quick, easy access to all the Unicode emojis available to you.
On Windows, the shortcut is <kbd>⊞</kbd> <kbd>.</kbd>.
</Alert>

View File

@@ -1,165 +0,0 @@
---
title: Audit logs
category: Topics
---
# Audit logs
## A Quick Background
Audit logs are an excellent moderation tool offered by Discord to know what happened in a server and usually by whom. Making use of audit logs requires the _`ViewAuditLog`_ permission. Audit logs may be fetched on a server, or they may be received via the gateway event <DocsLink type="class" parent="Client" symbol="e-guildAuditLogEntryCreate"/> which requires the _`GuildModeration`_ intent.
There are quite a few cases where you may use audit logs. This guide will limit itself to the most common use cases. Feel free to consult the [relevant Discord API page](https://discord.com/developers/docs/resources/audit-log) for more information.
Keep in mind that these examples explore a straightforward case and are by no means exhaustive. Their purpose is to teach you how audit logs work, and expansion of these examples is likely needed to suit your specific use case.
## Fetching Audit Logs
Let's start by glancing at the <DocsLink type="class" parent="Guild" symbol="fetchAuditLogs" brackets /> method and how to work with it. Like many discord.js methods, it returns a [Promise](../additional-info/understanding-async-await) containing the <DocsLink type="class" parent="GuildAuditLogs"/> object. This object has one property, _`entries`_, which holds a [Collection](../additional-info/collections) of <DocsLink type="class" parent="GuildAuditLogsEntry"/> objects, and consequently, the information you want to retrieve.
Here is the most basic fetch to look at some entries.
<CH.Code>
```js
const fetchedLogs = await guild.fetchAuditLogs();
const firstEntry = fetchedLogs.entries.first();
```
</CH.Code>
Simple, right? Now, let's look at utilizing its options:
<CH.Code>
```js
import { AuditLogEvent } from 'discord.js';
const fetchedLogs = await guild.fetchAuditLogs({
type: AuditLogEvent.InviteCreate,
limit: 1,
});
const firstEntry = fetchedLogs.entries.first();
```
</CH.Code>
This will return the first entry where an invite was created. You used _`limit: 1`_ here to specify only one entry.
## Receiving Audit Logs
Audit logs may be received via the gateway event <DocsLink type="class" parent="Client" symbol="e-guildAuditLogEntryCreate"/>.
This is the best way to receive audit logs if you want to monitor them. As soon as an audit log entry is created,
your application will receive an instance of this event. A common use case is to find out _who_ did the action that
caused the audit log event to happen.
### Who deleted a message?
One of the most common use cases for audit logs is understanding who deleted a message in a Discord server. If a user deleted another user's message, you can find out who did that as soon as you receive the corresponding audit log event.
<Alert title="Tip" type="info">
Messages deleted by their author or bots (excluding bulk deletes) do not generate audit log entries.
</Alert>
<CH.Code>
```js JavaScript
import { AuditLogEvent, Events } from 'discord.js';
client.on(Events.GuildAuditLogEntryCreate, async (auditLog) => {
// Define your variables.
// The extra information here will be the channel.
const { action, extra: channel, executorId, targetId } = auditLog;
// Check only for deleted messages.
if (action !== AuditLogEvent.MessageDelete) return;
// Ensure the executor is cached.
const executor = await client.users.fetch(executorId);
// Ensure the author whose message was deleted is cached.
const target = await client.users.fetch(targetId);
// Log the output.
console.log(`A message by ${target.tag} was deleted by ${executor.tag} in ${channel}.`);
});
```
```ts TypeScript
import { AuditLogEvent, Events } from 'discord.js';
client.on(Events.GuildAuditLogEntryCreate, async (auditLog) => {
// Define your variables.
// The extra information here will be the channel.
const { action, extra: channel, executorId, targetId } = auditLog;
// Check only for deleted messages.
if (action !== AuditLogEvent.MessageDelete) return;
// Ensure the executor is cached. The id definitely exists.
const executor = await client.users.fetch(executorId!);
// Ensure the author whose message was deleted is cached. The id definitely exists.
const target = await client.users.fetch(targetId!);
// Log the output.
console.log(`A message by ${target.tag} was deleted by ${executor.tag} in ${channel}.`);
});
```
</CH.Code>
With this, you now have a very simple logger telling you who deleted a message authored by another person.
### Who kicked a user?
This is very similar to the example above.
<CH.Code>
```js JavaScript
import { AuditLogEvent, Events } from 'discord.js';
client.on(Events.GuildAuditLogEntryCreate, async (auditLog) => {
// Define your variables.
const { action, executorId, targetId } = auditLog;
// Check only for kicked users.
if (action !== AuditLogEvent.MemberKick) return;
// Ensure the executor is cached.
const executor = await client.users.fetch(executorId);
// Ensure the kicked guild member is cached.
const kickedUser = await client.users.fetch(targetId);
// Now you can log the output!
console.log(`${kickedUser.tag} was kicked by ${executor.tag}.`);
});
```
```ts TypeScript
import { AuditLogEvent, Events } from 'discord.js';
client.on(Events.GuildAuditLogEntryCreate, async (auditLog) => {
// Define your variables.
const { action, executorId, targetId } = auditLog;
// Check only for kicked users.
if (action !== AuditLogEvent.MemberKick) return;
// Ensure the executor is cached. The id definitely exists.
const executor = await client.users.fetch(executorId!);
// Ensure the kicked guild member is cached. The id definitely exists.
const kickedUser = await client.users.fetch(targetId!);
// Now you can log the output!
console.log(`${kickedUser.tag} was kicked by ${executor.tag}.`);
});
```
</CH.Code>
If you want to check who banned a user, it's the same example as above except the _`action`_ should be <DiscordAPITypesLink type="enum" parent="AuditLogEvent" symbol="MemberBanAdd" />. You can check the rest of the possible actions on this page.

View File

@@ -1,223 +0,0 @@
---
title: Collectors
category: Topics
---
# Collectors
## Message collectors
{/* prettier-ignore */}
<DocsLink type="class" parent="Collector">Collectors</DocsLink> are useful to enable your bot to obtain _additional_ input after the first command was sent. An example would be initiating a quiz, where the bot will "await" a correct response from somebody.
### Basic message collector
Let's take a look at a basic message collector:
<CH.Code>
```js
const collectorFilter = (message) => message.content.includes('discord');
const collector = interaction.channel.createMessageCollector({ filter: collectorFilter, time: 15_000 });
collector.on('collect', (message) => {
console.log(`Collected ${message.content}`);
});
collector.on('end', (collected) => {
console.log(`Collected ${collected.size} messages`);
});
```
</CH.Code>
You can provide a _`filter`_ key to the object parameter of <DocsLink type="class" parent="TextChannel" symbol="createMessageCollector" brackets />. The value to this key should be a function that returns a boolean value to indicate if this message should be collected or not. To check for multiple conditions in your filter you can connect them using [logical operators](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Expressions_and_Operators#logical_operators). If you don't provide a filter all messages in the channel the collector was started on will be collected.
Note that the above example uses [implicit return](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions#function_body) for the filter function and passes it to the options object using the [object property shorthand](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Object_initializer#property_definitions) notation.
If a message passes through the filter, it will trigger the <DocsLink type="class" parent="Collector" symbol="e-collect" /> event for the _`collector`_ you've created. This message is then passed into the event listener as _`collected`_ and the provided function is executed. In the above example, you simply log the message. Once the collector finishes collecting based on the provided end conditions the <DocsLink type="class" parent="Collector" symbol="e-end" /> event emits.
You can control when a collector ends by supplying additional option keys when creating a collector:
- _`time`_: Amount of time in milliseconds the collector should run for
- _`max`_: Number of messages to successfully pass the filter
- _`maxProcessed`_: Number of messages encountered (no matter the filter result)
The benefit of using an event-based collector over _`awaitMessages()`_ (its promise-based counterpart) is that you can do something directly after each message is collected, rather than just after the collector ended. You can also stop the collector manually by calling <DocsLink type="class" parent="Collector" symbol="stop" brackets />.
### Await messages
Using <DocsLink type="class" parent="TextChannel" symbol="awaitMessages" brackets /> can be easier if you understand [Promises](../additional-info/understanding-async-await), and it allows you to have cleaner code overall. It is essentially identical to <DocsLink type="class" parent="TextChannel" symbol="createMessageCollector" brackets />, except promisified. However, the drawback of using this method is that you cannot do things before the Promise is resolved or rejected, either by an error or completion. However, it should do for most purposes, such as awaiting the correct response in a quiz. Instead of taking their example, let's set up a basic quiz command using the _`.awaitMessages()`_ feature.
First, you'll need some questions and answers to choose from, so here's a basic set:
<CH.Code>
```json
[
{
"question": "What color is the sky?",
"answers": ["blue"]
},
{
"question": "How many letters are there in the alphabet?",
"answers": ["26", "twenty-six", "twenty six", "twentysix"]
}
]
```
</CH.Code>
The provided set allows for responder error with an array of answers permitted. Ideally, it would be best to place this in a JSON file, which you can call _`quiz.json`_ for simplicity.
<CH.Code>
```js
import quiz from './quiz.json' assert { type: 'json' };
// ...
const item = quiz[Math.floor(Math.random() * quiz.length)];
const collectorFilter = (response) => {
return item.answers.some((answer) => answer.toLowerCase() === response.content.toLowerCase());
};
await interaction.reply({ content: item.question });
try {
const collected = await interaction.channel.awaitMessages({
filter: collectorFilter,
max: 1,
time: 30_000,
errors: ['time'],
});
await interaction.followUp(`${collected.first().author} got the correct answer!`);
} catch {
await interaction.followUp('Looks like nobody got the answer this time.');
}
```
</CH.Code>
<Alert title="Tip" type="info">
If you don't understand how _`.some()`_ works, you can read about it in more detail
[here](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/some).
</Alert>
In this filter, you iterate through the answers to find what you want. You would like to ignore the case because simple typos can happen, so you convert each answer to its lowercase form and check if it's equal to the response in lowercase form as well. In the options section, you only want to allow one answer to pass through, hence the _`max: 1`_ setting.
The filter looks for messages that match one of the answers in the array of possible answers to pass through the collector. The _`max`_ option (the second parameter) specifies that only a maximum of one message can go through the filter successfully before the Promise successfully resolves. The _`errors`_ section specifies that time will cause it to error out, which will cause the Promise to reject if one correct answer is not received within the time limit of one minute. As you can see, there is no _`collect`_ event, so you are limited in that regard.
## Reaction collectors
### Basic reaction collector
These work quite similarly to message collectors, except that you apply them on a message rather than a channel. This example uses the <DocsLink type="class" parent="Message" symbol="createReactionCollector" brackets /> method. The filter will check for the 👍 emojiin the default skin tone specifically, so be wary of that. It will also check that the person who reacted shares the same id as the author of the original message that the collector was assigned to.
```js
const collectorFilter = (reaction, user) => {
return reaction.emoji.name === '👍' && user.id === message.author.id;
};
const collector = message.createReactionCollector({ filter: collectorFilter, time: 15_000 });
collector.on('collect', (reaction, user) => {
console.log(`Collected ${reaction.emoji.name} from ${user.tag}`);
});
collector.on('end', (collected) => {
console.log(`Collected ${collected.size} items`);
});
```
### Await reactions
<DocsLink type="class" parent="Message" symbol="awaitReactions" brackets /> works almost the same as a reaction
collector, except it is Promise-based. The same differences apply as with channel collectors.
```js
const collectorFilter = (reaction, user) => {
return reaction.emoji.name === '👍' && user.id === message.author.id;
};
try {
const collected = await message.awaitReactions({ filter: collectorFilter, max: 1, time: 60_000, errors: ['time'] });
console.log(collected.size);
} catch (collected) {
console.log(`After a minute, the user did not react.`);
}
```
## Interaction collectors
The third type of collector allows you to collect interactions; such as when users activate a slash command or click on a button in a message.
### Basic message component collector
Collecting interactions from message components works similarly to reaction collectors. In the following example, you will check that the interaction came from a button, and that the user clicking the button is the same user that initiated the command.
One important difference to note with interaction collectors is that Discord expects a response to _all_ interactions within 3 seconds - even ones that you don't want to collect. For this reason, you may wish to _`.deferUpdate()`_ all interactions in your filter, or not use a filter at all and handle this behavior in the _`collect`_ event.
```js
import { ComponentType } from 'discord.js';
const collector = message.createMessageComponentCollector({ componentType: ComponentType.Button, time: 15_000 });
collector.on('collect', (i) => {
if (i.user.id === interaction.user.id) {
await i.reply(`${i.user.id} clicked on the ${i.customId} button.`);
} else {
await i.reply({ content: `These buttons aren't for you!`, ephemeral: true });
}
});
collector.on('end', (collected) => {
console.log(`Collected ${collected.size} interactions.`);
});
```
### Await message component
As before, this works similarly to the message component collector, except it is Promise-based.
Unlike other Promise-based collectors, this method will only ever collect one interaction that passes the filter. If no interactions are collected before the time runs out, the Promise will reject. This behavior aligns with Discord's requirement that actions should immediately receive a response. In this example, you will use _`.deferUpdate()`_ on all interactions in the filter.
```js
import { ComponentType } from 'discord.js';
const collectorFilter = (i) => {
i.deferUpdate();
return i.user.id === interaction.user.id;
};
try {
const interaction = await message.awaitMessageComponent({
filter: collectorFilter,
componentType: ComponentType.StringSelect,
time: 60_000,
});
await interaction.editReply(`You selected ${interaction.values.join(', ')}!`);
} catch (error) {
console.log('No interactions were collected.');
}
```
### Await modal submit
If you want to wait for the submission of a modal within the context of another command or button execution, you may find the promisified collector <DocsLink type="class" parent="CommandInteraction" symbol="awaitModalSubmit" brackets /> useful.
As Discord does not inform you if the user dismisses the modal, supplying a maximum _`time`_ to wait for is crucial:
```js
try {
const interaction = await initialInteraction.awaitModalSubmit({ time: 60_000, filter });
await interaction.editReply('Thank you for your submission!');
} catch (error) {
console.log('No modal submit interaction was collected');
}
```
For more information on working with modals, see the [modals section of this guide](../interactions/modals).

View File

@@ -1,95 +0,0 @@
---
title: Formatters
category: Topics
---
# Formatters
discord.js provides the <DocsLink package="formatters" /> package which contains a variety of utilities you can use when writing your Discord bot.
## Basic Markdown
These functions format strings into all the different markdown styles supported by Discord.
<CH.Code>
```js
import { bold, italic, strikethrough, underscore, spoiler, quote, blockQuote } from 'discord.js';
const string = 'Hello!';
const boldString = bold(string);
const italicString = italic(string);
const strikethroughString = strikethrough(string);
const underscoreString = underscore(string);
const spoilerString = spoiler(string);
const quoteString = quote(string);
const blockquoteString = blockQuote(string);
```
</CH.Code>
## Links
There are also two functions to format hyperlinks. _`hyperlink()`_ will format the URL into a masked markdown link, and _`hideLinkEmbed()`_ will wrap the URL in _`<>`_, preventing it from embedding.
<CH.Code>
```js
import { hyperlink, hideLinkEmbed } from 'discord.js';
const url = 'https://discord.js.org/';
const link = hyperlink('discord.js', url);
const hiddenEmbed = hideLinkEmbed(url);
```
</CH.Code>
## Code blocks
You can use _`inlineCode()`_ and _`codeBlock()`_ to turn a string into an inline code block or a regular code block with or without syntax highlighting.
<CH.Code>
```js
import { inlineCode, codeBlock } from 'discord.js';
const jsString = 'const value = true;';
const inline = inlineCode(jsString);
const codeblock = codeBlock(jsString);
const highlighted = codeBlock('js', jsString);
```
</CH.Code>
## Timestamps
With _`time()`_, you can format Unix timestamps and dates into a Discord time string.
<CH.Code>
```js
import { time, TimestampStyles } from 'discord.js';
const date = new Date();
const timeString = time(date);
const relative = time(date, TimestampStyles.RelativeTime);
```
</CH.Code>
## Mentions
_`userMention()`_, _`channelMention()`_, and _`roleMention()`_ all exist to format Snowflakes into mentions.
<CH.Code>
```js
import { channelMention, roleMention, userMention } from 'discord.js';
const id = '123456789012345678';
const channel = channelMention(id);
const role = roleMention(id);
const user = userMention(id);
```
</CH.Code>

View File

@@ -1,68 +0,0 @@
---
title: Intents
category: Topics
---
# Intents
Intents are an important part of establishing a WebSocket connection, as they define behavior regarding gateway events and impact received data via the REST API.
## Usage
```js
import { Client, GatewayIntentBits } from 'discord.js';
const client = new Client({
intents: [GatewayIntentBits.Guilds],
});
```
This is the most basic usage of intents for discord.js. By specifying _`GatewayIntentBits.Guilds`_, your bot will receive gateway events regarding guilds. This includes receiving initial information about guilds it is in at startup, such as role data.
You can find the full list of _`GatewayIntentBits`_ <DiscordAPITypesLink type="enum" parent="GatewayIntentBits">on the documentation</DiscordAPITypesLink> and an explanation of what each intent does [on Discord's API documentation](https://discord.com/developers/docs/topics/gateway#list-of-intents).
## Considerations
In discord.js, some intents require an extra bit of consideration.
### _`GatewayIntentBits.Guilds`_
discord.js relies heavily on caching in the library. We recommend you set at least the _`GatewayIntentBits.Guilds`_ intent to avoid these pitfalls.
### _`GatewayIntentBits.GuildMembers`_
Fetching members in a guild via <DocsLink type="class" parent="GuildMemberManager" symbol="fetch" brackets /> requests them over the gateway. As such, this intent is required and you may receive a timeout error if this intent is not specified.
<Alert title="Info" type="info">
This is a privileged intent. Read on for more information.
</Alert>
### _`GatewayIntentBits.DirectMessages`_
This intent is required to receive direct messages. In discord.js however, you **must** specify partials as well. See the partials topic on how this is done.
### _`GatewayIntentBits.MessageContent`_
Unlike other intents, this only populates user-generated fields. See [Discord's documentation](https://discord.com/developers/docs/topics/gateway#message-content-intent) on what exactly this intent unveils.
It is a common mistake to not see the message content in a message—this is usually because this intent is not specified.
<Alert title="Info" type="info">
This is a privileged intent. Read on for more information.
</Alert>
## Privileged intents
Some gateway events are considered privileged. Currently, these are:
- _`GatewayIntentBits.GuildPresences`_
- _`GatewayIntentBits.GuildMembers`_
- _`GatewayIntentBits.MessageContent`_
To use these intents, you will need to enable them in the developer portal. If your bot is in over 75 guilds, you will need to verify it and request usage of your desired intents.
Carefully think if you need these intents. They are opt-in so users across the platform can enjoy a higher level of privacy. Presences can expose some personal information, such as the games being played and overall online time. You might find that it isn't necessary for your bot to have this level of information about all guild members at all times.
### Disallowed intents
Should you receive an error stating you are using disallowed intents, please review your developer dashboard settings for all privileged intents you use. Check the Discord API documentation for up-to-date information.

View File

@@ -1,202 +0,0 @@
---
title: Threads
category: Topics
---
# Threads
Threads can be thought of as temporary sub-channels inside an existing channel to help better organize conversations in a busy channel.
## Thread related gateway events
<Alert title="Tip" type="info">
You can use the <DocsLink type="class" parent="BaseChannel" symbol="isThread" brackets /> type guard to make sure a
channel is a <DocsLink type="class" parent="ThreadChannel" />!
</Alert>
Threads introduce a number of new gateway events, which are listed below:
- <DocsLink type="class" parent="Client" symbol="e-threadCreate" />: Emitted whenever a thread is created or when the
client user is added to a thread.
- <DocsLink type="class" parent="Client" symbol="e-threadDelete" />: Emitted whenever a thread is deleted.
- <DocsLink type="class" parent="Client" symbol="e-threadUpdate" />: Emitted whenever a thread is updated (e.g. name
change, archive state change, locked state change).
- <DocsLink type="class" parent="Client" symbol="e-threadListSync" />: Emitted whenever the client user gains access to
a text or announcement channel that contains threads.
- <DocsLink type="class" parent="Client" symbol="e-threadMembersUpdate" />: Emitted whenever members are added or
removed from a thread. Requires <code>GuildMembers</code> privileged intent.
- <DocsLink type="class" parent="Client" symbol="e-threadMemberUpdate" />: Emitted whenever the client user's thread
member is updated.
## Creating and deleting threads
Threads are created and deleted using the <DocsLink type="class" parent="GuildTextThreadManager" /> of a text or announcement channel.
To create a thread, you call the <DocsLink type="class" parent="GuildTextThreadManager" symbol="create" brackets /> method:
<CH.Code>
```js
import { ThreadAutoArchiveDuration } from 'discord.js';
const thread = await channel.threads.create({
name: 'food-talk',
autoArchiveDuration: ThreadAutoArchiveDuration.OneHour,
reason: 'Needed a separate thread for food',
});
console.log(`Created thread: ${thread.name}`);
```
</CH.Code>
They can also be created from an existing message with the <DocsLink type="class" parent="Message" symbol="startThread" brackets /> method, but will be "orphaned" if that message is deleted.
<CH.Code>
```js focus=3[22:42]
import { ThreadAutoArchiveDuration } from 'discord.js';
const thread = await message.startThread({
name: 'food-talk',
autoArchiveDuration: ThreadAutoArchiveDuration.OneHour,
reason: 'Needed a separate thread for food',
});
console.log(`Created thread: ${thread.name}`);
```
</CH.Code>
The created thread and the message it originated from will share the same id. The type of thread created matches the parent channel's type.
To delete a thread, use the <DocsLink type="class" parent="ThreadChannel" symbol="delete" brackets /> method:
<CH.Code>
```js focus=2
const thread = channel.threads.cache.find((x) => x.name === 'food-talk');
if (thread.manageable) await thread.delete();
```
</CH.Code>
## Joining and leaving threads
To subscribe your client to a thread, use the <DocsLink type="class" parent="ThreadChannel" symbol="join" brackets /> method:
<CH.Code>
```js focus=2
const thread = channel.threads.cache.find((x) => x.name === 'food-talk');
if (thread.joinable) await thread.join();
```
</CH.Code>
And to leave one, use the <DocsLink type="class" parent="ThreadChannel" symbol="leave" brackets /> method:
<CH.Code>
```js focus=2
const thread = channel.threads.cache.find((x) => x.name === 'food-talk');
await thread.leave();
```
</CH.Code>
## Archiving, unarchiving, and locking threads
A thread can be either active or archived. Changing a thread from archived to active is referred to as unarchiving the thread. Threads that have _`locked`_ set to _`true`_ can only be unarchived by a member with the _`ManageThreads`_ permission.
Threads are automatically archived after inactivity. "Activity" is defined as sending a message, unarchiving a thread, or changing the auto-archive time.
To archive or unarchive a thread, use the <DocsLink type="class" parent="ThreadChannel" symbol="setArchived" brackets /> method and pass in a boolean parameter:
<CH.Code>
```js focus=2,3
const thread = channel.threads.cache.find((x) => x.name === 'food-talk');
await thread.setArchived(true); // Archived.
await thread.setArchived(false); // Unarchived.
```
</CH.Code>
This same principle applies to locking and unlocking a thread via the <DocsLink type="class" parent="ThreadChannel" symbol="setLocked" brackets /> method:
<CH.Code>
```js focus=2,3
const thread = channel.threads.cache.find((x) => x.name === 'food-talk');
await thread.setLocked(true); // Locked.
await thread.setLocked(false); // Unlocked.
```
</CH.Code>
## Private threads
Public threads are viewable by everyone who can view the parent channel of the thread. Private threads, however, are only viewable to those who are invited or have the _`ManageThreads`_ permission. Private threads can only be created on text channels.
To create a private thread, use the <DocsLink type="class" parent="GuildTextThreadManager" symbol="create" brackets /> method and pass in _`ChannelType.PrivateThread`_ as the _`type`_:
<CH.Code>
```js focus=1[10:21],6
import { ChannelType, ThreadAutoArchiveDuration } from 'discord.js';
const thread = await channel.threads.create({
name: 'mod-talk',
autoArchiveDuration: ThreadAutoArchiveDuration.OneHour,
type: ChannelType.PrivateThread,
reason: 'Needed a separate thread for moderation',
});
console.log(`Created thread: ${thread.name}`);
```
</CH.Code>
## Adding and removing members
You can add members to a thread with the <DocsLink type="class" parent="ThreadMemberManager" symbol="add" brackets /> method. The thread must be unarchived and you must be able to send messages in it.
<CH.Code>
```js focus=2
const thread = channel.threads.cache.find((x) => x.name === 'food-talk');
await thread.members.add('12345678901234567');
```
</CH.Code>
You can remove members from a thread with the <DocsLink type="class" parent="ThreadMemberManager" symbol="remove" brackets /> method. The thread must be unarchived and you must have the _`ManageThreads`_ permission unless the thread is private and you are the owner of it.
<CH.Code>
```js focus=2
const thread = channel.threads.cache.find((x) => x.name === 'food-talk');
await thread.members.remove('12345678901234567');
```
</CH.Code>
## Sending messages to threads with webhooks
It is possible for a webhook built on the parent channel to send messages to the channel's threads. For the purpose of this example, it is assumed a single webhook already exists for that channel. If you wish to learn more about webhooks, see our [webhook guide](./webhooks).
<CH.Code>
```js focus=4:7
const webhooks = await channel.fetchWebhooks();
const webhook = webhooks.first();
await webhook.send({
content: "Look ma! I'm in a thread!",
threadId: '123456789012345678',
});
```
</CH.Code>
And that's it! Now you know all there is to know on working with threads using discord.js!

View File

@@ -1,227 +0,0 @@
---
title: Webhooks
category: Topics
---
# Webhooks
Webhooks can send messages to a text channel without having to log in as a bot. They can also fetch, edit, and delete their own messages. There are a variety of methods in discord.js to interact with webhooks. In this section, you will learn how to create, fetch, edit, and use webhooks.
## What is a webhook
Webhooks are a utility used to send messages to text channels without needing a Discord application. Webhooks are useful for allowing something to send messages without requiring a Discord application. You can also directly edit or delete messages you sent through the webhook. There are two structures to make use of this functionality: <DocsLink type="class" parent="Webhook" /> and <DocsLink type="class" parent="WebhookClient" />. _`WebhookClient`_ is an extended version of a _`Webhook`_, which allows you to send messages through it without needing a bot client.
<Alert title="Tip" type="info">
If you would like to read about using webhooks through the API without discord.js, you can read about them
[here](https://discord.com/developers/docs/resources/webhook).
</Alert>
## Detecting webhook messages
Bots receive webhook messages in a text channel as usual. You can detect if a webhook sent the message by checking if the _`Message.webhookId`_ is not _`null`_. In this example, we return if a webhook sent the message.
<CH.Code>
```js
if (message.webhookId) return;
```
</CH.Code>
If you would like to get the webhook object that sent the message, you can use <DocsLink type="class" parent="Message" symbol="fetchWebhook" brackets />.
## Fetching webhooks
<Alert title="Tip" type="info">
Webhook fetching will always make use of collections and promises. If you do not understand either concept, revise
them, and then come back to this section. You can read about collections [here](../additional-info/collections), and
promises [here](../additional-info/understanding-async-await) and
[here](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Using_promises).
</Alert>
### Fetching all webhooks of a guild
If you would like to get all webhooks of a guild, you can use the <DocsLink type="class" parent="Guild" symbol="fetchWebhooks" brackets /> method. This will return a promise which will resolve into a collection of webhooks.
### Fetching webhooks of a channel
Webhooks belonging to a channel can be fetched using the <DocsLink type="class" parent="TextChannel" symbol="fetchWebhooks" brackets /> method. This will return a promise which will resolve into a collection of webhooks. A collection will be returned even if the channel contains a single webhook. If you are certain the channel contains a single webhook, you can use the <DocsLink package="collection" type="Class" parent="Collection" symbol="first" brackets /> method on the collection to get the webhook.
### Fetching a single webhook
#### Using client
You can fetch a specific webhook using its _`id`_ with the <DocsLink type="class" parent="Client" symbol="fetchWebhook" brackets /> method. You can obtain the webhook id by looking at its URL: the number after _`https://discord.com/api/webhooks/`_ is the _`id`_ and the part after that is the _`token`_.
#### Using the WebhookClient constructor
If you are not using a bot client, you can get a webhook by creating a new instance of _`WebhookClient`_ and passing the _`id`_ and _`token`_ into the constructor. These credentials do not require you to have a bot application, but it also offers limited information instead of fetching it using an authorized client.
<CH.Code>
```js
const webhookClient = new WebhookClient({ id: 'id', token: 'token' });
```
</CH.Code>
You can also pass in just a _`url`_:
<CH.Code>
```js
const webhookClient = new WebhookClient({ url: 'https://discord.com/api/webhooks/id/token' });
```
</CH.Code>
## Creating webhooks
### Creating webhooks through server settings
You can create webhooks directly through the Discord client. Go to Server Settings, and you will see an _`Integrations`_ tab.
![Integrations tab](/assets/integrations-tab.png)
If you already have created a webhook, the webhooks tab will look like this; you will need to click the _`View Webhooks`_ button.
![Integrations tab](/assets/integrations-view-tab.png)
Once you are there, click on the _`Create Webhook`_ / _`New Webhook`_ button; this will create a webhook. From here, you can edit the channel, the name, and the avatar. Copy the link, the first part is the id, and the second is the token.
![Creating a Webhook](/assets/webhook.png)
### Creating webhooks with discord.js
Webhooks can be created with the <DocsLink type="class" parent="TextChannel" symbol="createWebhook" brackets /> method.
<CH.Code>
```js
channel
.createWebhook({ name: 'Username', avatar: 'https://guide.discordjs.dev/assets/discordjs.png' })
.then((webhook) => console.log(`Created webhook ${webhook}`))
.catch(console.error);
```
</CH.Code>
## Editing webhooks
You can edit Webhooks and WebhookClients to change their name, avatar, and channel using <DocsLink type="class" parent="Webhook" symbol="edit" brackets />.
<CH.Code>
```js
webhook
.edit({ name: 'Username', avatar: 'https://guide.discordjs.dev/assets/discordjs.png', channel: '123456789012345678' })
.then((webhook) => console.log(`Edited webhook ${webhook}`))
.catch(console.error);
```
</CH.Code>
## Using webhooks
Webhooks can send messages to text channels, as well as fetch, edit, and delete their own. These methods are the same for both _`Webhook`_ and _`WebhookClient`_.
### Sending messages
Webhooks, like bots, can send up to 10 embeds per message. They can also send attachments and normal content. The <DocsLink type="class" parent="Webhook" symbol="send" brackets /> method is very similar to the method used for sending a message to a text channel. Webhooks can also choose how the username and avatar will appear when they send the message.
Example using a _`WebhookClient`_:
<CH.Code>
```js
import { EmbedBuilder, WebhookClient } from 'discord.js';
import config from './config.json' assert { type: 'json' };
const { webhookId, webhookToken } = config;
const webhookClient = new WebhookClient({ id: webhookId, token: webhookToken });
const embed = new EmbedBuilder().setTitle('Some Title').setColor(0x00ffff);
await webhookClient.send({
content: 'Webhook test',
username: 'some-username',
avatarURL: 'https://guide.discordjs.dev/assets/discordjs.png',
embeds: [embed],
});
```
</CH.Code>
Try to find a webhook your bot knows the token for. This makes sure your bot can execute the webhook later on.
<CH.Code>
```js
import { Client, EmbedBuilder, Events, GatewayIntentBits } from 'discord.js';
import config from './config.json' assert { type: 'json' };
const { token } = config;
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
const embed = new EmbedBuilder().setTitle('Some Title').setColor(0x00ffff);
client.once(Events.ClientReady, async () => {
const channel = client.channels.cache.get('123456789012345678');
try {
const webhooks = await channel.fetchWebhooks();
const webhook = webhooks.find((wh) => wh.token);
if (!webhook) return console.log('No webhook was found that I can use!');
await webhook.send({
content: 'Webhook test',
username: 'some-username',
avatarURL: 'https://guide.discordjs.dev/assets/discordjs.png',
embeds: [embed],
});
} catch (error) {
console.error('Error trying to send a message: ', error);
}
});
client.login(token);
```
</CH.Code>
### Fetching messages
You can use <DocsLink type="class" parent="Webhook" symbol="fetchMessage" brackets /> to fetch messages previously sent by the Webhook.
<CH.Code>
```js
const message = await webhookClient.fetchMessage('123456789012345678');
```
</CH.Code>
### Editing messages
You can use <DocsLink type="class" parent="Webhook" symbol="editMessage" brackets /> to edit messages previously sent by the Webhook.
<CH.Code>
```js
const message = await webhook.editMessage('123456789012345678', {
content: 'Edited!',
embeds: [embed],
});
```
</CH.Code>
### Deleting messages
You can use <DocsLink type="class" parent="Webhook" symbol="deleteMessage" brackets /> to delete messages previously sent by the webhook.
<CH.Code>
```js
await webhookClient.deleteMessage('123456789012345678');
```
</CH.Code>

View File

@@ -1,249 +0,0 @@
---
title: Understanding async/await
category: Additional info
---
# Understanding async/await
If you aren't very familiar with ECMAScript 2017, you may not know about async/await. It's a useful way to handle Promises in a hoisted manner. It's also slightly faster and increases overall readability.
## How do Promises work?
Before we can get into async/await, you should know what Promises are and how they work because async/await is just a way to handle Promises. If you know what Promises are and how to deal with them, you can skip this part.
Promises are a way to handle asynchronous tasks in JavaScript; they are the newer alternative to callbacks. A Promise has many similarities to a progress bar; they represent an unfinished and ongoing process. An excellent example of this is a request to a server (e.g., discord.js sends requests to Discord's API).
A Promise can have three states; pending, resolved, and rejected
The **pending** state means that the Promise still is ongoing and neither resolved nor rejected.
The **resolved** state means that the Promise is done and executed without any errors.
The **rejected** state means that the Promise encountered an error and could not execute correctly.
One important thing to know is that a Promise can only have one state simultaneously; it can never be pending and resolved, rejected and resolved, or pending and rejected. You may be asking, "How would that look in code?". Here is a small example:
<Alert title="Tip" type="success">
This example uses ES6 code. If you do not know what that is, you should read up on that
[here](/additional-info/es6-syntax.md).
</Alert>
<CH.Code>
```js
function deleteMessages(amount) {
return new Promise((resolve) => {
if (amount > 10) throw new Error("You can't delete more than 10 Messages at a time.");
setTimeout(() => resolve('Deleted 10 messages.'), 2000);
});
}
deleteMessages(5)
.then((value) => {
// `deleteMessages` is complete and has not encountered any errors
// the resolved value will be the string "Deleted 10 messages"
})
.catch((error) => {
// `deleteMessages` encountered an error
// the error will be an Error Object
});
```
</CH.Code>
In this scenario, the _`deleteMessages`_ function returns a Promise. The _`.then()`_ method will trigger if the Promise resolves, and the _`.catch()`_ method if the Promise rejects. In the _`deleteMessages`_ function, the Promise is resolved after 2 seconds with the string "Deleted 10 messages.", so the _`.catch()`_ method will never be executed. You can also pass the _`.catch()`_ function as the second parameter of _`.then()`_.
## How to implement async/await
### Theory
The following information is essential to know before working with async/await. You can only use the _`await`_ keyword inside a function declared as _`async`_ (you put the _`async`_ keyword before the _`function`_ keyword or before the parameters when using a callback function).
A simple example would be:
<CH.Code>
```js
async function declaredAsAsync() {
// ...
}
```
</CH.Code>
or
<CH.Code>
```js
const declaredAsAsync = async () => {
// ...
};
```
</CH.Code>
You can use that as well if you use the arrow function as an event listener.
<CH.Code>
```js
client.on('event', async (first, last) => {
// ...
});
```
</CH.Code>
An important thing to know is that a function declared as _`async`_ will always return a Promise. In addition to this, if you return something, the Promise will resolve with that value, and if you throw an error, it will reject the Promise with that error.
### Execution with discord.js code
Now that you know how Promises work and what they are used for, let's look at an example that handles multiple Promises. Let's say you want to react with letters (regional indicators) in a specific order. For this example, here's a basic template for a discord.js bot with some ES6 adjustments.
<CH.Code>
```js
import { Client, Events, GatewayIntentBits } from 'discord.js';
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.once(Events.ClientReady, () => {
console.log('I am ready!');
});
client.on(Events.InteractionCreate, (interaction) => {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === 'react') {
// ...
}
});
client.login('your-token-goes-here');
```
</CH.Code>
If you don't know how Node.js asynchronous execution works, you would probably try something like this:
<CH.Code>
```js mark=4:7
client.on('interactionCreate', (interaction) => {
// ...
if (commandName === 'react') {
const message = interaction.reply({ content: 'Reacting!', fetchReply: true });
message.react('🇦');
message.react('🇧');
message.react('🇨');
}
});
```
</CH.Code>
But since all of these methods are started at the same time, it would just be a race to which server request finished first, so there would be no guarantee that it would react at all (if the message isn't fetched) or in the order you wanted it to. In order to make sure it reacts after the message is sent and in order (a, b, c), you'd need to use the _`.then()`_ callback from the Promises that these methods return. The code would look like this:
<CH.Code>
```js mark=4:12
client.on('interactionCreate', (interaction) => {
// ...
if (commandName === 'react') {
interaction.reply({ content: 'Reacting!', fetchReply: true }).then((message) => {
message
.react('🇦')
.then(() => message.react('🇧'))
.then(() => message.react('🇨'))
.catch((error) => {
// handle failure of any Promise rejection inside here
});
});
}
});
```
</CH.Code>
In this piece of code, the Promises are [chain resolved](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise/then#Chaining) with each other, and if one of the Promises gets rejected, the function passed to _`.catch()`_ gets called. Here's the same code but with async/await:
<CH.Code>
```js mark=1,4:7
client.on('interactionCreate', async (interaction) => {
// ...
if (commandName === 'react') {
const message = await interaction.reply({ content: 'Reacting!', fetchReply: true });
await message.react('🇦');
await message.react('🇧');
await message.react('🇨');
}
});
```
</CH.Code>
It's mostly the same code, but how would you catch Promise rejections now since _`.catch()`_ isn't there anymore? That is also a useful feature with async/await; the error will be thrown if you await it so that you can wrap the awaited Promises inside a try/catch, and you're good to go.
<CH.Code>
```js mark=1,4:11
client.on('interactionCreate', async (interaction) => {
if (commandName === 'react') {
try {
const message = await interaction.reply({ content: 'Reacting!', fetchReply: true });
await message.react('🇦');
await message.react('🇧');
await message.react('🇨');
} catch (error) {
// handle failure of any Promise rejection inside here
}
}
});
```
</CH.Code>
This code looks clean and is also easy to read.
So you may be asking, "How would I get the value the Promise resolved with?".
Let's look at an example where you want to delete a sent reply.
<CH.Code>
```js mark=3:10
client.on('interactionCreate', (interaction) => {
// ...
if (commandName === 'delete') {
interaction
.reply({ content: 'This message will be deleted.', fetchReply: true })
.then((replyMessage) => setTimeout(() => replyMessage.delete(), 10000))
.catch((error) => {
// handle error
});
}
});
```
</CH.Code>
The return value of a _`.reply()`_ with the _`fetchReply`_ option set to _`true`_ is a Promise which resolves with the reply when it has been sent, but how would the same code with async/await look?
<CH.Code>
```js mark=1,4:10
client.on('interactionCreate', async (interaction) => {
if (commandName === 'delete') {
try {
const replyMessage = await interaction.reply({ content: 'This message will be deleted.', fetchReply: true });
setTimeout(() => replyMessage.delete(), 10000);
} catch (error) {
// handle error
}
}
});
```
</CH.Code>
With async/await, you can assign the awaited function to a variable representing the returned value. Now you know how you use async/await.

View File

@@ -1,123 +0,0 @@
---
title: Collections
category: Additional info
---
# Collections
discord.js comes with a utility class known as _`Collection`_.
It extends JavaScript's native _`Map`_ class, so it has all the _`Map`_ features and more!
<Alert title="Warning" type="warning">
If you're not familiar with _`Map`_, read [MDN's page on
it](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map) before continuing. You should be
familiar with _`Array`_ [methods](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) as
well. We will also use some ES6 features, so read up [here](/additional-info/es6-syntax.md) if you do not know what
they are.
</Alert>
A _`Map`_ allows for an association between unique keys and their values.
For example, how can you transform every value or filter the entries in a _`Map`_ easily?
This is the point of the _`Collection`_ class!
## Array-like Methods
Many of the methods on _`Collection`_ correspond to their namesake in _`Array`_. One of them is _`find`_:
<CH.Code>
```js
// Assume we have an array of users and a collection of the same users.
array.find((u) => u.discriminator === '1000');
collection.find((u) => u.discriminator === '1000');
```
</CH.Code>
The interface of the callback function is very similar between the two.
For arrays, callbacks usually pass the parameters _`(value, index, array)`_, where _`value`_ is the value iterated to,
_`index`_ is the current index, and _`array`_ is the array. For collections, you would have _`(value, key, collection)`_.
Here, _`value`_ is the same, but _`key`_ is the key of the value, and _`collection`_ is the collection itself instead.
Methods that follow this philosophy of staying close to the _`Array`_ interface are as follows:
- _`find`_
- _`filter`_ - Note that this returns a _`Collection`_ rather than an _`Array`_.
- _`map`_ - Yet this returns an _`Array`_ of values instead of a _`Collection`_!
- _`every`_
- _`some`_
- _`reduce`_
- _`concat`_
- _`sort`_
## Converting to Array
Since _`Collection`_ extends _`Map`_, it is an [iterable](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols), and can be converted to an _`Array`_ through either _`Array.from()`_ or spread syntax (_`...collection`_).
<CH.Code>
```js
// For values.
Array.from(collection.values());
[...collection.values()];
// For keys.
Array.from(collection.keys());
[...collection.keys()];
// For [key, value] pairs.
Array.from(collection);
[...collection];
```
</CH.Code>
<Alert title="Warning" type="warning">
Many people convert Collections to Arrays way too much! This can lead to unnecessary and confusing code. Before you
use _`Array.from()`_ or similar, ask yourself if whatever you are trying to do can't be done with the given _`Map`_ or
_`Collection`_ methods or with a for-of loop.
</Alert>
## Extra Utilities
Some methods are not from _`Array`_ and are instead entirely new to standard JavaScript.
<CH.Code>
```js
// A random value.
collection.random();
// The first value.
collection.first();
// The first 5 values.
collection.first(5);
// Similar to `first`, but from the end.
collection.last();
collection.last(2);
// Removes anything that meets the condition from the collection.
// Sort of like `filter`, but in-place.
collection.sweep((user) => user.username === 'Bob');
```
</CH.Code>
A more complicated method is _`partition`_, which splits a single Collection into two new Collections based on the provided function.
You can think of it as two \_`filter`\_ methods, but done at the same time:
<CH.Code>
```js
// `bots` is a Collection of users where their `bot` property was true.
// `humans` is a Collection where the property was false instead!
const [bots, humans] = collection.partition((u) => u.bot);
// Both return true.
bots.every((b) => b.bot);
humans.every((h) => !h.bot);
```
</CH.Code>

View File

@@ -1,834 +0,0 @@
---
title: Updating to v14
category: Additional info
---
# Updating to v14
## Before you start
v14 requires Node 16.11 or higher to use, so make sure you're up to date. To check your Node.js version, use _`node --version`_ in your terminal or command prompt, and if it's not high enough, update it! There are many resources online to help you with this step based on your host system.
### Various packages are now included in v14
If you previously had _`@discordjs/builders`_, _`@discordjs/formatters`_, _`@discordjs/rest`_, or _`discord-api-types`_ manually installed, it's _highly_ recommended that you uninstall the packages to avoid package version conflicts.
<CH.Code>
```sh npm
npm uninstall @discordjs/builders @discordjs/formatters @discordjs/rest discord-api-types
```
```sh yarn
yarn remove @discordjs/builders @discordjs/formatters @discordjs/rest discord-api-types
```
```sh pnpm
pnpm remove @discordjs/builders @discordjs/formatters @discordjs/rest discord-api-types
```
```sh bun
bun remove @discordjs/builders @discordjs/formatters @discordjs/rest discord-api-types
```
</CH.Code>
## Breaking Changes
### API version
discord.js v14 makes the switch to Discord API v10!
### Common Breakages
### Enum Values
Any areas that used to accept a _`string`_ or _`number`_ type for an enum parameter will now only accept a _`number`_.
In addition, the old enums exported by discord.js v13 and lower are replaced with new enums from <DiscordAPITypesLink />.
#### New enum differences
Most of the difference between enums from discord.js and discord-api-types can be summarized as so:
1. Enums are singular, i.e., _`ApplicationCommandOptionTypes`_ -> _`ApplicationCommandOptionType`_
2. Enums that are prefixed with _`Message`_ no longer have the _`Message`_ prefix, i.e., _`MessageButtonStyles`_ -> _`ButtonStyle`_
3. Enum values are _`PascalCase`_ rather than `SCREAMING_SNAKE_CASE`, i.e., `.CHAT_INPUT` -> `.ChatInput`
<Alert title="Magic Numbers Warning" type="danger">
You might be inclined to a raw _`number`_ (most commonly referred to as [magic
numbers](https://en.wikipedia.org/wiki/Magic_number_(programming))) instead of enum values. This is highly
discouraged. Enums provide more readability and are more resistant to changes in the API. Magic numbers can obscure
the meaning of your code in many ways. Check out this [blog
post](https://blog.webdevsimplified.com/2020-02/magic-numbers) if you want more context on as to why they shouldn't be
used.
</Alert>
#### Common enum breakages
Areas like _`Client`_ initialization, JSON slash commands and JSON message components will likely need to be modified to accommodate these changes:
##### Common Client Initialization Changes
<CH.Code>
```diff
- import { Client, Intents } = from 'discord.js';
+ import { Client, GatewayIntentBits, Partials } = from 'discord.js';
- const client = new Client({ intents: [Intents.FLAGS.GUILDS], partials: ['CHANNEL'] });
+ const client = new Client({ intents: [GatewayIntentBits.Guilds], partials: [Partials.Channel] });
```
</CH.Code>
##### Common Application Command Data changes
<CH.Code>
```diff
+ import { ApplicationCommandType, ApplicationCommandOptionType } = from 'discord.js';
const command = {
name: 'ping',
- type: 'CHAT_INPUT',
+ type: ApplicationCommandType.ChatInput,
options: [{
name: 'option',
description: 'A sample option',
- type: 'STRING',
+ type: ApplicationCommandOptionType.String,
}],
};
```
</CH.Code>
##### Common Button Data changes
<CH.Code>
```diff
+ import { ButtonStyle } = from 'discord.js';
const button = {
label: 'test',
- style: 'PRIMARY',
+ style: ButtonStyle.Primary,
customId: '1234'
}
```
</CH.Code>
### Removal of method-based type guards
#### Channels
Some channel type guard methods that narrowed to one channel type have been removed. Instead compare the _`type`_ property against a <DiscordAPITypesLink type="enum" parent="ChannelType" /> enum member to narrow channels.
<CH.Code>
```diff
- channel.isText();
+ channel.type === ChannelType.GuildText;
- channel.isVoice();
+ channel.type === ChannelType.GuildVoice;
- channel.isDM();
+ channel.type === ChannelType.DM;
```
</CH.Code>
### Builders
Builders are no longer returned by the API like they were previously. For example, you send the API an <DocsLink type="class" parent="EmbedBuilder"/> but you receive an <DocsLink type="class" parent="Embed"/> of the same data. This may affect how your code handles received structures such as components. Refer to [message component changes section](#messagecomponent) for more details.
Added <DocsLink package="builders" type="Function" parent="disableValidators" /> and <DocsLink package="builders" type="Function" parent="enableValidators" /> as top-level exports which disable or enable validation (enabled by default).
### Consolidation of create & edit parameters
Various _`create()`_ and _`edit()`_ methods on managers and objects have had their parameters consolidated. The changes are below:
- <DocsLink type="class" parent="Guild" symbol="edit" brackets /> now takes _`reason`_ in the _`data`_ parameter
- <DocsLink type="class" parent="GuildChannel" symbol="edit" brackets /> now takes _`reason`_ in the _`data`_ parameter
- <DocsLink type="class" parent="GuildEmoji" symbol="edit" brackets /> now takes _`reason`_ in the _`data`_ parameter
- <DocsLink type="class" parent="Role" symbol="edit" brackets /> now takes _`reason`_ in the _`data`_ parameter
- <DocsLink type="class" parent="Sticker" symbol="edit" brackets /> now takes _`reason`_ in the _`data`_ parameter
- <DocsLink type="class" parent="ThreadChannel" symbol="edit" brackets /> now takes _`reason`_ in the _`data`_ parameter
- <DocsLink type="class" parent="GuildChannelManager" symbol="create" brackets /> now takes _`name`_ in the _`options`_
parameter
- <DocsLink type="class" parent="GuildChannelManager" symbol="createWebhook" brackets /> (and other text-based channels)
now takes _`channel`_ and _`name`_ in the _`options`_ parameter
- <DocsLink type="class" parent="GuildChannelManager" symbol="edit" brackets /> now takes _`reason`_ as a part of
_`data`_
- <DocsLink type="class" parent="GuildEmojiManager" symbol="edit" brackets /> now takes _`reason`_ as a part of _`data`_
- <DocsLink type="class" parent="GuildManager" symbol="create" brackets /> now takes _`name`_ as a part of _`options`_
- <DocsLink type="class" parent="GuildMemberManager" symbol="edit" brackets /> now takes _`reason`_ as a part of
_`data`_
- <DocsLink type="class" parent="GuildMember" symbol="edit" brackets /> now takes _`reason`_ as a part of _`data`_
- <DocsLink type="class" parent="GuildStickerManager" symbol="edit" brackets /> now takes _`reason`_ as a part of
_`data`_
- <DocsLink type="class" parent="RoleManager" symbol="edit" brackets /> now takes _`reason`_ as a part of _`options`_
- <DocsLink type="class" parent="Webhook" symbol="edit" brackets /> now takes _`reason`_ as a part of _`options`_
- <DocsLink type="class" parent="GuildEmojiManager" symbol="create" brackets /> now takes _`attachment`_ and _`name`_ as
a part of _`options`_
- <DocsLink type="class" parent="GuildStickerManager" symbol="create" brackets /> now takes _`file`_, _`name`_, and
_`tags`_ as a part of _`options`_
### Activity
The following properties have been removed as they are not supported by the API:
- _`Activity#id`_
- _`Activity#platform`_
- _`Activity#sessionId`_
- _`Activity#syncId`_
### Application
_`Application#fetchAssets()`_ has been removed as it is no longer supported by the API.
### BitField
- BitField constituents now have a _`BitField`_ suffix to avoid naming conflicts with the enum names:
```diff
- new Permissions();
+ new PermissionsBitField();
- new MessageFlags();
+ new MessageFlagsBitField();
- new ThreadMemberFlags();
+ new ThreadMemberFlagsBitField();
- new UserFlags();
+ new UserFlagsBitField();
- new SystemChannelFlags();
+ new SystemChannelFlagsBitField();
- new ApplicationFlags();
+ new ApplicationFlagsBitField();
- new Intents();
+ new IntentsBitField();
- new ActivityFlags();
+ new ActivityFlagsBitField();
```
- _`#FLAGS`_ has been renamed to _`#Flags`_
### CDN
The methods that return CDN URLs have changed. Here is an example on a `User`:
<CH.Code>
```diff
- const url = user.displayAvatarURL({ dynamic: true, format: "png", size: 1024 });
+ const url = user.displayAvatarURL({ extension: "png", size: 1024 });
```
</CH.Code>
Dynamic URLs use <DocsLink package="rest" type="Interface" parent="ImageURLOptions"/> and static URLs use <DocsLink package="rest" type="Interface" parent="BaseImageURLOptions"/>. Since dynamic URLs are returned by default, this option has been renamed to _`forceStatic`_ which forces the return of a static URL. Additionally, _`format`_ has been renamed to _`extension`_.
### CategoryChannel
<DocsLink type="class" parent="CategoryChannel" symbol="children" /> is no longer a _`Collection`_ of channels the
category contains. It is now a <DocsLink type="class" parent="CategoryChannelChildManager" />. This also means
_`CategoryChannel#createChannel()`_ has been moved to the <DocsLink type="class" parent="CategoryChannelChildManager" />
.
### Channel
The following type guards have been removed:
- _`Channel#isText()`_
- _`Channel#isVoice()`_
- _`Channel#isDirectory()`_
- _`Channel#isDM()`_
- _`Channel#isGroupDM()`_
- _`Channel#isCategory()`_
- _`Channel#isNews()`_
Refer to [this section](#channels) for more context.
The base channel class is now <DocsLink type="class" parent="BaseChannel"/>.
### Client
The _`restWsBridgeTimeout`_ client option has been removed.
### CommandInteractionOptionResolver
<DocsLink type="class" parent="CommandInteractionOptionResolver" symbol="getMember" brackets /> no longer has a
parameter for _`required`_.[^1]
### Constants
- Many constant objects and key arrays are now top-level exports. For example:
<CH.Code>
```diff
- import { Constants } = from 'discord.js';
- const { Colors } = Constants;
+ import { Colors } = from 'discord.js';
```
</CH.Code>
- The refactored constants structures have _`PascalCase`_ member names as opposed to _`SCREAMING_SNAKE_CASE`_ member names.
- Many of the exported constants structures have been replaced and renamed:
<CH.Code>
```diff
- Opcodes
+ GatewayOpcodes
- WSEvents
+ GatewayDispatchEvents
- WSCodes
+ GatewayCloseCodes
- InviteScopes
+ OAuth2Scopes
```
</CH.Code>
### Events
The _`message`_ and _`interaction`_ events are now removed. Use <DocsLink type="class" parent="Client" symbol="e-messageCreate"/> and <DocsLink type="class" parent="Client" symbol="e-interactionCreate"/> instead.
_`Client#applicationCommandCreate`_, _`Client#applicationCommandDelete`_, and _`Client#applicationCommandUpdate`_ have all been removed.[^2]
The <DocsLink type="class" parent="Client" symbol="e-threadMembersUpdate"/> event now emits the users that were added, the users that were removed, and the thread respectively.
### GuildBanManager
Developers should utilise _`deleteMessageSeconds`_ instead of _`days`_ and _`deleteMessageDays`_:
<CH.Code>
```diff
<GuildBanManager>.create('123456789', {
- days: 3
- deleteMessageDays: 3
+ deleteMessageSeconds: 3 * 24 * 60 * 60
});
```
</CH.Code>
_`deleteMessageDays`_ and _`days`_ are both deprecated and will be removed in the future.
### Guild
<DocsLink type="class" parent="Guild" symbol="setRolePositions" brackets /> and <DocsLink
type="class"
parent="Guild"
symbol="setChannelPositions"
brackets
/> have been removed. Use <DocsLink type="class" parent="RoleManager" symbol="setPositions" brackets /> and <DocsLink
type="class"
parent="GuildChannelManager"
symbol="setPositions"
brackets
/> instead respectively.
<DocsLink type="class" parent="Guild" symbol="maximumPresences" /> no longer has a default value of 25,000.
_`Guild#me`_ has been moved to <DocsLink type="class" parent="GuildMemberManager" symbol="me" />.[^3]
### GuildAuditLogs & GuildAuditLogsEntry
_`GuildAuditLogs.build()`_ has been removed as it has been deemed defunct. There is no alternative.
The following properties & methods have been moved to the <DocsLink type="class" parent="GuildAuditLogsEntry" /> class:
- `GuildAuditLogs.Targets`
- `GuildAuditLogs.actionType()`
- `GuildAuditLogs.targetType()`
### GuildMember
<DocsLink type="class" parent="GuildMember" symbol="pending" /> is now nullable to account for partial guild
members.[^4]
### IntegrationApplication
_`IntegrationApplication#summary`_ has been removed as it is no longer supported by the API.
### Interaction
Whenever an interaction is replied to and one fetches the reply, it could possibly give an <DiscordAPITypesLink type="interface" parent="APIMessage" /> if the guild was not cached. However, interaction replies now always return a discord.js <DocsLink type="class" parent="Message"/> object with _`fetchReply`_ as _`true`_.
The base interaction class is now <DocsLink type="class" parent="BaseInteraction"/>.
### Invite
<DocsLink type="class" parent="Invite" symbol="inviter" /> is now a getter and resolves structures from the cache.
### MessageAttachment
- _`MessageAttachment`_ has now been renamed to <DocsLink type="class" parent="AttachmentBuilder" />.
<CH.Code>
```diff
- new MessageAttachment(buffer, 'image.png');
+ new AttachmentBuilder(buffer, { name: 'image.png' });
```
</CH.Code>
### MessageComponent
- MessageComponents have been renamed as well. They no longer have the _`Message`_ prefix, and now have a _`Builder`_ suffix:
<CH.Code>
```diff
- const button = new MessageButton();
+ const button = new ButtonBuilder();
- const selectMenu = new MessageSelectMenu();
+ const selectMenu = new StringSelectMenuBuilder();
- const actionRow = new MessageActionRow();
+ const actionRow = new ActionRowBuilder();
- const textInput = new TextInputComponent();
+ const textInput = new TextInputBuilder();
```
</CH.Code>
- Components received from the API are no longer directly mutable. If you wish to mutate a component from the API, use _`ComponentBuilder#from()`_. For example, if you want to make a button mutable:
<CH.Code>
```diff
- const editedButton = receivedButton.setDisabled(true);
+ import { ButtonBuilder } = from 'discord.js';
+ const editedButton = ButtonBuilder.from(receivedButton).setDisabled(true);
```
</CH.Code>
### MessageManager
The second parameter of <DocsLink type="class" parent="MessageManager" symbol="fetch" brackets /> has been removed. The <DocsLink type="typedef" parent="BaseFetchOptions" /> the second parameter once was is now merged into the first parameter.
<CH.Code>
```diff
- messageManager.fetch('1234567890', { cache: false, force: true });
+ messageManager.fetch({ message: '1234567890', cache: false, force: true });
```
</CH.Code>
### MessageSelectMenu
- _`MessageSelectMenu`_ has been renamed to <DocsLink type="class" parent="StringSelectMenuBuilder" />.
- _`StringSelectMenuBuilder#addOption()`_ has been removed. Use <DocsLink type="class" parent="StringSelectMenuBuilder" symbol="addOptions" brackets /> instead.
### MessageEmbed
- _`MessageEmbed`_ has now been renamed to <DocsLink type="class" parent="EmbedBuilder" />.
- <DocsLink package="builders" type="Class" parent="EmbedBuilder" symbol="setAuthor" brackets /> now accepts a sole <DocsLink
package="builders"
type="TypeAlias"
parent="EmbedAuthorOptions"
/> object.
- <DocsLink package="builders" type="Class" parent="EmbedBuilder" symbol="setFooter" brackets /> now accepts a sole <DocsLink
package="builders"
type="TypeAlias"
parent="EmbedFooterOptions"
/> object.
- _`EmbedBuilder#addField()`_ has been removed. Use <DocsLink package="builders" type="Class" parent="EmbedBuilder" symbol="addFields" brackets/> instead.
<CH.Code>
```diff
- new MessageEmbed().addField('Inline field title', 'Some value here', true);
+ new EmbedBuilder().addFields([
+ { name: 'one', value: 'one', inline: true },
+ { name: 'two', value: 'two', inline: true },
+]);
```
</CH.Code>
### Modal
- _`Modal`_ has been renamed <DocsLink type="class" parent="ModalBuilder" />.
<CH.Code>
```diff
- const modal = new Modal();
+ const modal = new ModalBuilder();
```
</CH.Code>
### PartialTypes
The _`PartialTypes`_ string array has been removed. Use the <DocsLink type="typedef" parent="Partials" /> enum instead.
In addition to this, there is now a new partial: _`Partials.ThreadMember`_.
### Permissions
The thread permissions _`USE_PUBLIC_THREADS`_ and _`USE_PRIVATE_THREADS`_ have been removed as they are deprecated in the API. Use _`CREATE_PUBLIC_THREADS`_ and _`CREATE_PRIVATE_THREADS`_ respectively.
_`ManageEmojisAndStickers`_ has been deprecated due to API changes. Its replacement is _`ManageGuildExpressions`_.[^7]
### PermissionOverwritesManager
Overwrites are now keyed by the _`PascalCase`_ permission key rather than the _`SCREAMING_SNAKE_CASE`_ permission key.
### REST Events
#### apiRequest
This REST event has been removed as discord.js now uses [Undici](https://github.com/nodejs/undici) as the underlying request handler. You must now use a [Diagnostics Channel](https://undici.nodejs.org/#/docs/api/DiagnosticsChannel). Here is a simple example:
<CH.Code>
```js JavaScript
import diagnosticsChannel from 'node:diagnostics_channel';
diagnosticsChannel.channel('undici:request:create').subscribe(({ request }) => {
const { request } = data;
console.log(request.method); // Log the method
console.log(request.path); // Log the path
console.log(request.headers); // Log the headers
console.log(request); // Or just log everything!
});
```
```ts TypeScript
import diagnosticsChannel from 'node:diagnostics_channel';
import { type DiagnosticsChannel } from 'undici';
diagnosticsChannel.channel('undici:request:create').subscribe((data) => {
const { request } = data as DiagnosticsChannel.RequestCreateMessage;
console.log(request.method); // Log the method
console.log(request.path); // Log the path
console.log(request.headers); // Log the headers
console.log(request); // Or just log everything!
});
```
</CH.Code>
You can find further examples at the [Undici Diagnostics Channel documentation](https://undici.nodejs.org/#/docs/api/DiagnosticsChannel).
#### apiResponse
This REST event has been renamed to _`response`_ and moved to <DocsLink type="class" parent="Client" symbol="rest"/>:
<CH.Code>
```diff
- client.on('apiResponse', ...);
+ client.rest.on('response', ...);
```
</CH.Code>
#### invalidRequestWarning
This REST event has been moved to <DocsLink type="class" parent="Client" symbol="rest"/>:
<CH.Code>
```diff
- client.on('invalidRequestWarning', ...);
+ client.rest.on('invalidRequestWarning', ...);
```
</CH.Code>
#### rateLimit
This REST event has been renamed to _`rateLimited`_ and moved to <DocsLink type="class" parent="Client" symbol="rest"/>:
<CH.Code>
```diff
- client.on('rateLimit', ...);
+ client.rest.on('rateLimited', ...);
```
</CH.Code>
### RoleManager
_`Role.comparePositions()`_ has been removed. Use <DocsLink type="class" parent="RoleManager" symbol="comparePositions" brackets/> instead.
### Sticker
<DocsLink type="class" parent="Sticker" symbol="tags" /> is now a nullable string (_`string | null`_). Previously, it
was a nullable array of strings (_`string[] | null`_).[^5]
### ThreadChannel
The _`MAX`_ helper used in _`ThreadAutoArchiveDuration`_ has been removed. Discord has since allowed any guild to use any auto archive time which makes this helper redundant.
### ThreadMemberManager
The second parameter of <DocsLink type="class" parent="ThreadMemberManager" symbol="fetch" brackets /> has been removed. The <DocsLink type="class" parent="BaseFetchOptions" /> the second parameter once was is now merged into the first parameter. In addition, the boolean helper to specify _`cache`_ has been removed.
Usage is now as follows:
<CH.Code>
```diff
// The second parameter is merged into the first parameter.
- threadMemberManager.fetch('1234567890', { cache: false, force: true });
+ threadMemberManager.fetch({ member: '1234567890', cache: false, force: true });
// The lone boolean has been removed. One must be explicit here.
- threadMemberManager.fetch(false);
+ threadMemberManager.fetch({ cache: false });
```
</CH.Code>
### Util
_`Util.removeMentions()`_ has been removed. To control mentions, you should use _`allowedMentions`_ on <DocsLink type="typedef" parent="BaseMessageOptions" /> instead.
_`Util.splitMessage()`_ has been removed. This utility method is something the developer themselves should do.
_`Util.resolveAutoArchiveMaxLimit()`_ has been removed. Discord has since allowed any guild to use any auto archive time which makes this method redundant.
Other functions in _`Util`_ have been moved to top-level exports so you can directly import them from discord.js.
<CH.Code>
```diff
- import { Util } from 'discord.js';
- Util.escapeMarkdown(message);
+ import { escapeMarkdown } from 'discord.js';
+ escapeMarkdown(message);
```
</CH.Code>
### .deleted fields have been removed
You can no longer use the _`deleted`_ property to check if a structure was deleted.[^6]
### VoiceChannel
_`VoiceChannel#editable`_ has been removed. You should use <DocsLink type="class" parent="GuildChannel" symbol="manageable"/> instead.
### VoiceRegion
_`VoiceRegion#vip`_ has been removed as it is no longer part of the API.
### Webhook
The second parameter of <DocsLink type="class" parent="Webhook" symbol="fetchMessage" brackets/> no longer allows a boolean to be passed. The _`cache`_ option in <DocsLink type="typedef" parent="WebhookFetchMessageOptions"/> should be used instead.
## Features
### ApplicationCommand
NFSW commands are supported.
### Attachment
Added support for voice message metadata fields.
### AutocompleteInteraction
<DocsLink type="class" parent="AutocompleteInteraction" symbol="commandGuildId" /> has been added which is the id of the
guild the invoked application command is registered to.
### BaseChannel
Added support for <DocsLink type="class" parent="BaseChannel" symbol="flags" />.
Store channels have been removed as they are no longer part of the API.
<DocsLink type="class" parent="BaseChannel" symbol="url" /> has been added which is a link to a channel, just like in
the client.
Additionally, new typeguards have been added:
- <DocsLink type="class" parent="BaseChannel" symbol="isDMBased" brackets />
- <DocsLink type="class" parent="BaseChannel" symbol="isTextBased" brackets />
- <DocsLink type="class" parent="BaseChannel" symbol="isVoiceBased" brackets />
### BaseInteraction
Added <DocsLink type="class" parent="BaseInteraction" symbol="isRepliable" brackets /> to check whether a given interaction can be replied to.
### ClientApplication
Added support for role connection metadata.
### Collection
- Added <DocsLink package="collection" type="Class" parent="Collection" symbol="merge" brackets/> and <DocsLink package="collection" type="Class" parent="Collection" symbol="combineEntries" brackets/>.
- Added <DocsLink package="collection" type="TypeAlias" parent="ReadonlyCollection"/> which indicates an immutable _`Collection`_.
### Collector
A new <DocsLink type="class" parent="Collector" symbol="e-ignore"/> event has been added which is emitted whenever an element is not collected by the collector.
Component collector options now use the <DiscordAPITypesLink type="enum" parent="ComponentType" /> enum values:
<CH.Code>
```diff
+ import { ComponentType } from 'discord.js';
const collector = interaction.channel.createMessageComponentCollector({
filter,
- componentType: 'BUTTON',
+ componentType: ComponentType.Button,
time: 20000
});
```
</CH.Code>
### CommandInteraction
<DocsLink type="class" parent="CommandInteraction" symbol="commandGuildId" /> has been added which is the id of the
guild the invoked application command is registered to.
### CommandInteractionOptionResolver
<DocsLink type="class" parent="CommandInteractionOptionResolver" symbol="getChannel" brackets /> now has a third
parameter which narrows the channel type.
### Events
Added support for <DocsLink type="class" parent="Client" symbol="e-guildAuditLogEntryCreate" /> event.
### ForumChannel
Added support for forum channels.
Added support for <DocsLink type="class" parent="ForumChannel" symbol="defaultForumLayout" />.
### Guild
Added support for <DocsLink type="class" parent="Guild" symbol="setMFALevel" brackets /> which sets the guild's MFA level.
Added support for <DocsLink type="class" parent="Guild" symbol="maxVideoChannelUsers"/>. which indicates the maximum number of video channel users.
Added support for <DocsLink type="class" parent="Guild" symbol="maxStageVideoChannelUsers" />. which indicates the maximum number of video channel users for stage channels.
Added support for <DocsLink type="class" parent="Guild" symbol="disableInvites" brackets />. which disables the guild's invites.
Added support for the _`after`_ parameter in <DocsLink type="class" parent="Guild" symbol="fetchAuditLogs" brackets />.
### GuildChannelManager
_`videoQualityMode`_ may be used whilst creating a channel to initially set the camera video quality mode.
### GuildEmojiManager
Added <DocsLink type="class" parent="GuildEmojiManager" symbol="delete" brackets /> and <DocsLink type="class" parent="GuildEmojiManager" symbol="edit" brackets /> for managing existing guild emojis.
### GuildForumThreadManager
Added <DocsLink type="class" parent="GuildForumThreadManager" /> as manager for threads in forum channels.
### GuildMember
Added support for <DocsLink type="class" parent="GuildMember" symbol="flags"/>.
### GuildMembersChunk
This object now supports the _`notFound`_ property.
### GuildMemberManager
Added <DocsLink type="class" parent="GuildMemberManager" symbol="fetchMe" brackets /> to fetch the client user in the guild.
Added <DocsLink type="class" parent="GuildMemberManager" symbol="addRole" brackets /> and <DocsLink type="class" parent="GuildMemberManager" symbol="removeRole" brackets />. These methods allow a single addition or removal of a role respectively to a guild member, even if uncached.
### GuildTextThreadManager
Added <DocsLink type="class" parent="GuildTextThreadManager" /> as manager for threads in text channels and announcement channels.
### Message
<DocsLink type="class" parent="Message" symbol="position" /> has been added as an approximate position in a thread.
Added support for role subscription data.
### MessageReaction
Added <DocsLink type="class" parent="MessageReaction" symbol="react" brackets /> to make the client user react with the reaction the class belongs to.
### Role
Added support for role subscriptions.
Added support for _`Role#tags#guildConnections`_.
### StageChannel
Stage channels now allow messages to be sent in them, much like voice channels.
### Sticker
Added support for GIF stickers.
### ThreadMemberManager
The new _`withMember`_ options returns the associated guild member with the thread member.
When fetching multiple thread members alongside _`withMember`_, paginated results will be returned. The _`after`_ and _`limit`_ option are supported in this scenario.
### Webhook
Added <DocsLink type="class" parent="Webhook" symbol="applicationId" />.
Added the _`threadName`_ property in <DocsLink type="typedef" parent="WebhookMessageCreateOptions"/> which allows a webhook to create a post in a forum channel.
### WebSocketManager
discord.js uses <DocsLink package="ws" /> internally.
[^1]: https://github.com/discordjs/discord.js/pull/7188
[^2]: https://github.com/discordjs/discord.js/pull/6492
[^3]: https://github.com/discordjs/discord.js/pull/7669
[^4]: https://github.com/discordjs/discord.js/issues/6546
[^5]: https://github.com/discordjs/discord.js/pull/8010
[^6]: https://github.com/discordjs/discord.js/issues/7091
[^7]: https://github.com/discord/discord-api-docs/pull/6017

View File

@@ -1,27 +0,0 @@
'use client';
import {
type PropsWithChildren,
type Dispatch,
type SetStateAction,
createContext,
useContext,
useState,
useMemo,
} from 'react';
export const NavContext = createContext<{ opened: boolean; setOpened: Dispatch<SetStateAction<boolean>> }>({
opened: false,
setOpened: (_) => {},
});
export const NavProvider = ({ children }: PropsWithChildren) => {
const [opened, setOpened] = useState(false);
const value = useMemo(() => ({ opened, setOpened }), [opened]);
return <NavContext.Provider value={value}>{children}</NavContext.Provider>;
};
export function useNav() {
return useContext(NavContext);
}

View File

@@ -1,9 +0,0 @@
import { NextResponse, type NextRequest } from 'next/server';
export default async function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/guide/home/introduction', request.url));
}
export const config = {
matcher: ['/', '/guide'],
};

View File

@@ -1,3 +0,0 @@
[data-backdrop] {
background-color: rgb(0 0 0 / 35%);
}

View File

@@ -1,566 +0,0 @@
{
"$schema": "vscode://schemas/color-theme",
"name": "dark-plus",
"tokenColors": [
{
"settings": {
"foreground": "#D4D4D4"
}
},
{
"scope": ["meta.embedded", "source.groovy.embedded", "string meta.image.inline.markdown"],
"settings": {
"foreground": "#D4D4D4"
}
},
{
"scope": "emphasis",
"settings": {
"fontStyle": "italic"
}
},
{
"scope": "strong",
"settings": {
"fontStyle": "bold"
}
},
{
"scope": "header",
"settings": {
"foreground": "#000080"
}
},
{
"scope": "comment",
"settings": {
"foreground": "#6A9955"
}
},
{
"scope": "constant.language",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": [
"constant.numeric",
"variable.other.enummember",
"keyword.operator.plus.exponent",
"keyword.operator.minus.exponent"
],
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": "constant.regexp",
"settings": {
"foreground": "#646695"
}
},
{
"scope": "entity.name.tag",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "entity.name.tag.css",
"settings": {
"foreground": "#d7ba7d"
}
},
{
"scope": "entity.other.attribute-name",
"settings": {
"foreground": "#9cdcfe"
}
},
{
"scope": [
"entity.other.attribute-name.class.css",
"entity.other.attribute-name.class.mixin.css",
"entity.other.attribute-name.id.css",
"entity.other.attribute-name.parent-selector.css",
"entity.other.attribute-name.pseudo-class.css",
"entity.other.attribute-name.pseudo-element.css",
"source.css.less entity.other.attribute-name.id",
"entity.other.attribute-name.scss"
],
"settings": {
"foreground": "#d7ba7d"
}
},
{
"scope": "invalid",
"settings": {
"foreground": "#f44747"
}
},
{
"scope": "markup.underline",
"settings": {
"fontStyle": "underline"
}
},
{
"scope": "markup.bold",
"settings": {
"fontStyle": "bold",
"foreground": "#569cd6"
}
},
{
"scope": "markup.heading",
"settings": {
"fontStyle": "bold",
"foreground": "#569cd6"
}
},
{
"scope": "markup.italic",
"settings": {
"fontStyle": "italic"
}
},
{
"scope": "markup.strikethrough",
"settings": {
"fontStyle": "strikethrough"
}
},
{
"scope": "markup.inserted",
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": "markup.deleted",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "markup.changed",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "punctuation.definition.quote.begin.markdown",
"settings": {
"foreground": "#6A9955"
}
},
{
"scope": "punctuation.definition.list.begin.markdown",
"settings": {
"foreground": "#6796e6"
}
},
{
"scope": "markup.inline.raw",
"settings": {
"foreground": "#ce9178"
}
},
{
"name": "brackets of XML/HTML tags",
"scope": "punctuation.definition.tag",
"settings": {
"foreground": "#808080"
}
},
{
"scope": ["meta.preprocessor", "entity.name.function.preprocessor"],
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "meta.preprocessor.string",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "meta.preprocessor.numeric",
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": "meta.structure.dictionary.key.python",
"settings": {
"foreground": "#9cdcfe"
}
},
{
"scope": "meta.diff.header",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "storage",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "storage.type",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": ["storage.modifier", "keyword.operator.noexcept"],
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": ["string", "meta.embedded.assembly"],
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "string.tag",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "string.value",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "string.regexp",
"settings": {
"foreground": "#d16969"
}
},
{
"name": "String interpolation",
"scope": [
"punctuation.definition.template-expression.begin",
"punctuation.definition.template-expression.end",
"punctuation.section.embedded"
],
"settings": {
"foreground": "#569cd6"
}
},
{
"name": "Reset JavaScript string interpolation expression",
"scope": ["meta.template.expression"],
"settings": {
"foreground": "#d4d4d4"
}
},
{
"scope": [
"support.type.vendored.property-name",
"support.type.property-name",
"variable.css",
"variable.scss",
"variable.other.less",
"source.coffee.embedded"
],
"settings": {
"foreground": "#9cdcfe"
}
},
{
"scope": "keyword",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "keyword.control",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "keyword.operator",
"settings": {
"foreground": "#d4d4d4"
}
},
{
"scope": [
"keyword.operator.new",
"keyword.operator.expression",
"keyword.operator.cast",
"keyword.operator.sizeof",
"keyword.operator.alignof",
"keyword.operator.typeid",
"keyword.operator.alignas",
"keyword.operator.instanceof",
"keyword.operator.logical.python",
"keyword.operator.wordlike"
],
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "keyword.other.unit",
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"],
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "support.function.git-rebase",
"settings": {
"foreground": "#9cdcfe"
}
},
{
"scope": "constant.sha.git-rebase",
"settings": {
"foreground": "#b5cea8"
}
},
{
"name": "coloring of the Java import and package identifiers",
"scope": ["storage.modifier.import.java", "variable.language.wildcard.java", "storage.modifier.package.java"],
"settings": {
"foreground": "#d4d4d4"
}
},
{
"name": "this.self",
"scope": "variable.language",
"settings": {
"foreground": "#569cd6"
}
},
{
"name": "Function declarations",
"scope": [
"entity.name.function",
"support.function",
"support.constant.handlebars",
"source.powershell variable.other.member",
"entity.name.operator.custom-literal"
],
"settings": {
"foreground": "#DCDCAA"
}
},
{
"name": "Types declaration and references",
"scope": [
"support.class",
"support.type",
"entity.name.type",
"entity.name.namespace",
"entity.other.attribute",
"entity.name.scope-resolution",
"entity.name.class",
"storage.type.numeric.go",
"storage.type.byte.go",
"storage.type.boolean.go",
"storage.type.string.go",
"storage.type.uintptr.go",
"storage.type.error.go",
"storage.type.rune.go",
"storage.type.cs",
"storage.type.generic.cs",
"storage.type.modifier.cs",
"storage.type.variable.cs",
"storage.type.annotation.java",
"storage.type.generic.java",
"storage.type.java",
"storage.type.object.array.java",
"storage.type.primitive.array.java",
"storage.type.primitive.java",
"storage.type.token.java",
"storage.type.groovy",
"storage.type.annotation.groovy",
"storage.type.parameters.groovy",
"storage.type.generic.groovy",
"storage.type.object.array.groovy",
"storage.type.primitive.array.groovy",
"storage.type.primitive.groovy"
],
"settings": {
"foreground": "#4EC9B0"
}
},
{
"name": "Types declaration and references, TS grammar specific",
"scope": [
"meta.type.cast.expr",
"meta.type.new.expr",
"support.constant.math",
"support.constant.dom",
"support.constant.json",
"entity.other.inherited-class"
],
"settings": {
"foreground": "#4EC9B0"
}
},
{
"name": "Control flow / Special keywords",
"scope": [
"keyword.control",
"source.cpp keyword.operator.new",
"keyword.operator.delete",
"keyword.other.using",
"keyword.other.operator",
"entity.name.operator"
],
"settings": {
"foreground": "#C586C0"
}
},
{
"name": "Variable and parameter name",
"scope": [
"variable",
"meta.definition.variable.name",
"support.variable",
"entity.name.variable",
"constant.other.placeholder"
],
"settings": {
"foreground": "#9CDCFE"
}
},
{
"name": "Constants and enums",
"scope": ["variable.other.constant", "variable.other.enummember"],
"settings": {
"foreground": "#4FC1FF"
}
},
{
"name": "Object keys, TS grammar specific",
"scope": ["meta.object-literal.key"],
"settings": {
"foreground": "#9CDCFE"
}
},
{
"name": "CSS property value",
"scope": [
"support.constant.property-value",
"support.constant.font-name",
"support.constant.media-type",
"support.constant.media",
"constant.other.color.rgb-value",
"constant.other.rgb-value",
"support.constant.color"
],
"settings": {
"foreground": "#CE9178"
}
},
{
"name": "Regular expression groups",
"scope": [
"punctuation.definition.group.regexp",
"punctuation.definition.group.assertion.regexp",
"punctuation.definition.character-class.regexp",
"punctuation.character.set.begin.regexp",
"punctuation.character.set.end.regexp",
"keyword.operator.negation.regexp",
"support.other.parenthesis.regexp"
],
"settings": {
"foreground": "#CE9178"
}
},
{
"scope": [
"constant.character.character-class.regexp",
"constant.other.character-class.set.regexp",
"constant.other.character-class.regexp",
"constant.character.set.regexp"
],
"settings": {
"foreground": "#d16969"
}
},
{
"scope": ["keyword.operator.or.regexp", "keyword.control.anchor.regexp"],
"settings": {
"foreground": "#DCDCAA"
}
},
{
"scope": "keyword.operator.quantifier.regexp",
"settings": {
"foreground": "#d7ba7d"
}
},
{
"scope": "constant.character",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "constant.character.escape",
"settings": {
"foreground": "#d7ba7d"
}
},
{
"scope": "entity.name.label",
"settings": {
"foreground": "#C8C8C8"
}
}
],
"semanticTokenColors": {
"newOperator": "#C586C0",
"stringLiteral": "#ce9178",
"customLiteral": "#DCDCAA",
"numberLiteral": "#b5cea8"
},
"colors": {
"checkbox.border": "#6B6B6B",
"editor.background": "#1E1E1E",
"editor.foreground": "#D4D4D4",
"editor.inactiveSelectionBackground": "#3A3D41",
"editorIndentGuide.background": "#404040",
"editorIndentGuide.activeBackground": "#707070",
"editor.selectionHighlightBackground": "#ADD6FF26",
"list.dropBackground": "#383B3D",
"activityBarBadge.background": "#007ACC",
"sideBarTitle.foreground": "#BBBBBB",
"input.placeholderForeground": "#A6A6A6",
"menu.background": "#252526",
"menu.foreground": "#CCCCCC",
"menu.separatorBackground": "#454545",
"menu.border": "#454545",
"statusBarItem.remoteForeground": "#FFF",
"statusBarItem.remoteBackground": "#16825D",
"ports.iconRunningProcessForeground": "#369432",
"sideBarSectionHeader.background": "#0000",
"sideBarSectionHeader.border": "#ccc3",
"tab.lastPinnedBorder": "#ccc3",
"list.activeSelectionIconForeground": "#FFF",
"terminal.inactiveSelectionBackground": "#3A3D41",
"widget.border": "#303031"
},
"type": "dark"
}

Some files were not shown because too many files have changed in this diff Show More