Compare commits

...

256 Commits

Author SHA1 Message Date
iCrawl
5d1b233047 chore(core): release @discordjs/core@1.0.1 2023-08-17 21:44:09 +02:00
iCrawl
4ab05e994e chore(ws): release @discordjs/ws@1.0.1 2023-08-17 21:43:19 +02:00
iCrawl
4bc2667a5f chore(proxy): release @discordjs/proxy@2.0.1 2023-08-17 21:42:44 +02:00
iCrawl
84ead6e554 chore(rest): release @discordjs/rest@2.0.1 2023-08-17 21:41:52 +02:00
iCrawl
0220fb7278 chore(builders): release @discordjs/builders@1.6.5 2023-08-17 21:40:56 +02:00
iCrawl
6da456767e chore(util): release @discordjs/util@1.0.1 2023-08-17 21:39:49 +02:00
iCrawl
67b30355ea chore(formatters): release @discordjs/formatters@0.3.2 2023-08-17 21:38:59 +02:00
iCrawl
58d9024245 chore(create-discord-bot): release create-discord-bot@0.2.1 2023-08-17 21:38:20 +02:00
iCrawl
5cd6382d1d chore(collection): release @discordjs/collection@1.5.3 2023-08-17 21:35:04 +02:00
iCrawl
fffe625a0c chore(brokers): release @discordjs/brokers@0.2.2 2023-08-17 21:34:23 +02:00
iCrawl
7321c291da chore: revert bump 2023-08-17 21:33:46 +02:00
iCrawl
ab3efb0211 chore(discord.js): release discord.js@14.13.0 2023-08-17 21:31:59 +02:00
iCrawl
7fa9ea3f0f chore: pin discord-api-deps, revert undici 2023-08-17 21:31:04 +02:00
iCrawl
af0f1aa765 chore(discord.js): release discord.js@14.13.0 2023-08-17 21:09:00 +02:00
iCrawl
99cadfd82b chore: deps 2023-08-17 21:08:09 +02:00
Jiralite
8f572a6bad docs(EmbedBuilder): @readonly length (#9778)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-08-17 19:07:02 +00:00
Jaw0r3k
d90ba8dce8 feat(Client): add deleteWebhook method (#9777)
* feat(Client): deleteWebhook method

* Update packages/discord.js/src/client/Client.js

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

* chore: suggested changes

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-08-17 16:12:05 +00:00
Jiralite
24fbb11ba2 fix(Action): Do not add the client user as a recipient (#9774)
fix(Action): do not add the client user as a recipient

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-08-14 17:24:00 +00:00
Jiralite
727dc094d5 types(GuildInvitableChannelResolvable): Allow forum channels (#9775)
types(GuildInvitableChannelResolvable): allow forum channels

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-08-14 17:19:47 +00:00
Jiralite
d5be4242c6 docs(WebhookEditOptions): Add all of the types (#9776)
docs(WebhookEditOptions): add all the types

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-08-14 17:16:24 +00:00
Jiralite
c1ff545bf1 fix(DMChannel): Correct partial typo (#9773)
fix(DMChannel): correct partial typo
2023-08-14 17:11:46 +00:00
Toast
188877c50a docs: Update Node.js requirement to 16.11.0 (#9764)
* Update Node.js requirement in README file

* chore: update the rest of files with nodejs 16.11.0

* Update 03-updating-to-v14.mdx

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2023-08-13 19:49:33 +00:00
Jiralite
a1605b3a5e build: Update vite and other related dependencies (#9772)
* build: update vite and other related dependencies

* chore: use `"errors-only"`
2023-08-13 21:36:23 +02:00
advaith
9ed1b59df6 feat(ClientPresence): allow setting activity state (#9743)
* feat(ClientPresence): allow setting activity state

* fix: add to map

* feat: use name as fallback state
2023-08-12 11:31:29 +00:00
Jaw0r3k
0a9a3ede29 typings: Make activity name required (#9765)
* fix: activity name is required

* chore: add suggested changes

Co-authored-by: Aura Román <kyradiscord@gmail.com>

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Aura Román <kyradiscord@gmail.com>
2023-08-12 11:12:33 +00:00
ckohen
346fa57f95 fix(CachedManager): allow overriding constructor for makeCache (#9763)
* fix(CachedManager): allow overriding constructor for makeCache

* feat: allow determining makeCache based on non-overriden constructor

* types: cleanup leftovers

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-08-12 07:08:47 +00:00
Lily Bergonzat
b3c85d34a6 fix(types): fixed CachedManager constructor arguments in type (#9761)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-08-11 10:04:13 +00:00
Jiralite
d8e37551ce fix(Action): Do not set undefined values (#9755)
fix(Action): do not set `undefined` values

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-08-11 07:19:59 +00:00
Jiralite
a30d46c5f5 types(BaseButtonComponentData): Narrow component type (#9735)
* types(BaseButtonComponentData): narrow `type`

* test: fix suddenly broken tests

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-08-11 07:15:05 +00:00
Jiralite
632a9b4965 feat(ClientApplication): Approximate guild count and new GET route (#9713)
* feat: add approx guild count and get route

* refactor: replace route

* docs: update description of class

The replacement route justifies this change.

* feat(ClientApplication): add `approximateGuildCount`

* refactor: revert now-unnecessary changes
2023-08-11 07:11:13 +00:00
n1ck_pro
3b18e5b08d feat(Role): add flags (#9694)
* feat(Role): add `flags`

* types: use RoleFlags enum

* Update packages/discord.js/typings/index.d.ts

Co-authored-by: Jaw0r3k <jaworekwiadomosci@gmail.com>

---------

Co-authored-by: Jaw0r3k <jaworekwiadomosci@gmail.com>
2023-08-10 21:20:13 +00:00
Almeida
692f0fc96d feat(Attachment): add flags (#9686)
* feat(Attachment): add `flags`

* fix: import

* fix: flags casing

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-08-10 20:53:54 +00:00
Synbulat Biishev
0de071d0a5 feat: add Client#webhooksUpdate (#9732)
* feat: add `Client#webhooksUpdate`

* feat: add deprecation in the types

* docs: add full stops

* types: reference non-deprecated type

This helps with future-proofing (deduplication).

* docs(ClientEvents): fix reference link

This now hyperlinks correctly with IntelliSense.

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-08-09 14:15:00 +00:00
Jaw0r3k
99194fc270 fix(create-discord-bot): add shebang in main file (#9747)
* fix: no running in cmd

Co-authored-by: ROB0520 <ynoreyesminecraft@gmail.com>

* chore: delete in create-discord-bot file

---------

Co-authored-by: ROB0520 <ynoreyesminecraft@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-08-09 09:48:58 +00:00
Almeida
cf72d42ea5 ci: upload /formatters test coverage (#9753)
chore: upload /formatters test coverage
2023-08-08 06:44:27 +00:00
iCrawl
7295a3a94a chore(discord.js): release discord.js@14.12.1 2023-08-01 02:23:51 +02:00
n1ck_pro
1af7e5a0bb fix(BaseClient): fix destroy method (#9742) 2023-07-31 22:18:11 +00:00
iCrawl
3bc0dce04a chore: fix changelog 2023-07-31 22:17:37 +02:00
iCrawl
c40f7540cd chore(discord.js): release discord.js@15.0.0 2023-07-31 22:07:38 +02:00
iCrawl
1fc9308fef chore(builders): release @discordjs/builders@1.6.4 2023-07-31 21:35:26 +02:00
iCrawl
43d3b48565 chore(create-discord-bot): release create-discord-bot@0.2.0 2023-07-31 21:33:51 +02:00
iCrawl
8d50f2056d chore(core): release @discordjs/core@1.0.0 2023-07-31 21:30:19 +02:00
iCrawl
acc560204f chore(proxy): release @discordjs/proxy@2.0.0 2023-07-31 21:28:52 +02:00
iCrawl
ccf65c2d09 chore(ws): release @discordjs/ws@1.0.0 2023-07-31 21:27:09 +02:00
iCrawl
d04987f09b chore(rest): release @discordjs/rest@2.0.0 2023-07-31 21:24:28 +02:00
iCrawl
ca5a2626b4 chore(collection): release @discordjs/collection@1.5.2 2023-07-31 21:22:35 +02:00
iCrawl
3c5a4dc7a7 chore(util): release @discordjs/util@1.0.0 2023-07-31 21:18:33 +02:00
iCrawl
447652ec8a chore: format 2023-07-31 21:08:13 +02:00
iCrawl
488aa58b29 chore: fixup yarn.lock 2023-07-30 23:16:45 +02:00
iCrawl
a92d19212d ci: publishing dev version semver 2023-07-30 23:08:46 +02:00
iCrawl
fd8197fe5b chore: deps 2023-07-30 02:13:39 +02:00
ckohen
8f4256db8a refactor(REST): remove double classing (#9722)
* refactor(REST): remove double classing

BREAKING CHANGE: `REST` and `RequestManager` have been combined, most of the properties, methods, and events from both classes can now be found on `REST`
BREAKING CHANGE: `REST#raw` has been removed in favor of `REST#queueRequest`
BREAKING CHANGE: `REST#getAgent` has been removed in favor of `REST#agent`

* chore: update for /rest changes
2023-07-25 08:40:21 +00:00
Saya
6307f81385 feat: add ws option support for "buildIdentifyThrottler" (#9728)
* feat: add option support for "buildIdentifyThrottler"

* docs: add documentation for buildIdentifyThrottler

* docs: fix example code
2023-07-22 08:57:24 +00:00
iCrawl
d26e022afc chore: deps 2023-07-19 11:59:42 +02:00
iCrawl
afa98793cd chore: deps 2023-07-17 09:45:07 +02:00
Vlad Frangu
386f206caf feat: no-de-no-de, now with extra buns (#9683)
BREAKING CHANGE: The REST and RequestManager classes now extend AsyncEventEmitter
from `@vladfrangu/async_event_emitter`, which aids in cross-compatibility
between Node, Deno, Bun, CF Workers, Vercel Functions, etc.

BREAKING CHANGE: DefaultUserAgentAppendix has been adapted to support multiple
different platforms (previously mentioned Deno, Bun, CF Workers, etc)

BREAKING CHANGE: the entry point for `@discordjs/rest` will now differ
in non-node-like environments (CF Workers, etc.)

Co-authored-by: Suneet Tipirneni <77477100+suneettipirneni@users.noreply.github.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: suneettipirneni <suneettipirneni@icloud.com>
2023-07-17 08:27:57 +02:00
David Malchin
351a18bc35 feat(create-discord-bot): throw error if the directory is a file (#9719) 2023-07-16 17:38:22 +00:00
Suneet Tipirneni
84f1b1890d feat(create-discord-bot): Add prompts, command handler and deployment script (#9570)
* feat(create-discord-bot): Add prompts, command handler and deployment script

* fix: revert package template name

* chore: make requested changes

* chore: make requested changes

* fix: remove uneeded listeners

* fix: use `0` for eslint

* fix: remaining requested changes

* chore: requested changes

* fix: remove redundant call
2023-07-16 17:13:18 +00:00
Jiralite
e5effb6f6a docs: Change Channel to BaseChannel (#9718)
docs: change `Channel` to `BaseChannel`
2023-07-16 15:06:14 +00:00
Jiralite
125405f1cf docs(BaseChannel): Remove APIChannel (#9717)
docs(BaseChannel): remove `APIChannel`
2023-07-16 14:59:35 +00:00
David Malchin
bc83cabfda feat(WebhooksAPI): allow with token requests without bot auth (#9715)
* Also move `get`'s `token` parameter to options.
2023-07-16 10:25:29 +00:00
brynpttrsn
20268ac0c4 feat(website): switch overload on hash location (#9699)
* feat(website): switch overload on hash location

* Update apps/website/src/components/OverloadSwitcher.tsx

---------

Co-authored-by: Noel <buechler.noel@outlook.com>
2023-07-15 19:34:28 +02:00
Aura Román
3c85fb21e6 feat(Client): add guildAvailable event (#9692)
* feat(Client): add `guildAvailable` event

* refactor: sort entries alphabetically

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-07-15 17:33:47 +00:00
brynpttrsn
4141405dcd fix(website): type alias search symbol (#9705) 2023-07-15 19:25:56 +02:00
David Malchin
8c782bfd52 fix(WebSocketShard): close errors not being catchable (#9704)
* fix(WebSocketShard): close errors not being catchable

* fix(WebSocketShard): requested changes
2023-07-15 10:55:05 +00:00
Almeida
8d97017458 feat: user avatar decorations (#8914)
feat(User): add avatar decorations

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2023-07-13 20:33:38 +00:00
DD
ceab07bec8 refactor(WebSocketShard): throttling error handling (#9701)
* refactor(WebSocketShard): handle unknown identify errors

* chore: use better abort check

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-07-12 17:19:11 +00:00
Jiralite
7fb91c57f7 fix(ChannelUpdate): Check against unknown channels (#9697)
fix(ChannelUpdate): check against unknown channels
2023-07-11 18:13:25 +00:00
Aura Román
09b0382c45 perf(Channel): linear speed position getter (#9497)
* perf(Channel): linear speed position getter

* fix: add another set of parens

* perf: lower memory and CPU usage

* refactor: add shared private utility for group types

* perf: improve readability and performance

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>

* feat: add support for voice sortables

---------

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-07-09 20:57:33 +00:00
Jiralite
719e54a921 fix(Action): Use existing recipients if available (#9653)
* fix(Action): use existing recipients if available

* fix: account for message delete -> interaction

* fix(Action): use `last_message_id` if it exists

* refactor(Action): use ternary

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-07-09 19:30:48 +00:00
Jiralite
ede9f4e5e2 docs(BuildersSelectMenuOption): Update link (#9690)
docs(BuildersSelectMenuOption): update link
2023-07-09 19:26:21 +00:00
MovementGH
df40dcdb85 feat: add silent option to ShardingManager (#9506)
* feat: add silent option to ShardingManager

* chore: update ShardingManagerOptions type

* chore: update typings

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2023-07-07 23:32:29 +00:00
RedGuy12
53c17e00c0 feat(EmbedBuilder): add .length (#8682)
* feat(Embed): add `.length`

Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com>

* Move to mainlib

Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com>

* types: add typings

---------

Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com>
Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-07-07 21:53:19 +00:00
Almeida
dc73c938ff feat: guild onboarding (#9120)
* feat: guild onboarding

* feat: types and /core method

* fix: route

* fix: make emoji name non-nullable

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-07-07 21:48:17 +00:00
Jiralite
a48d0efb09 types(MessageManager): Allow comparison of messages again (#9612)
* types(MessageManager): allow comparison of messages again

* feat: match TypeScript

* docs: update wording

* chore: revert breaking change

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-07-07 21:39:01 +00:00
GrapeColor
a73d54e43a feat: add resume event in shard (#9650)
Co-authored-by: GrapeColor <grapecolor@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-07-07 21:34:42 +00:00
Rodrigo Leitão
cf8012c200 refactor(User): remove deprecation warning from tag (#9660)
* refactor(User): remove deprecation warning from tag

* fix: remove unnecessary declarations

* style: fix formatting issue

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-07-06 16:49:03 +00:00
Synbulat Biishev
0803eb562b fix: everyone role members (#9685)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-07-06 14:09:31 +00:00
David Malchin
78381a56cf feat(ChannelsAPI): add permission overwrites (#9651)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-07-06 13:55:29 +00:00
Suneet Tipirneni
6d5840c61e fix(core): fix inconsistencies on core (#9680)
* fix(core): fix inconsistencies on `core`

* fix: add `createForumPost` back

* fix: create -> createWebhook
2023-07-06 13:35:10 +00:00
ShadowAya
75d91b52b3 setNameLocalization typo (#9656)
fix JSDoc typo in NameAndDescription.ts > setNameLocalization
2023-06-17 14:29:35 +00:00
Almeida
766be94b93 chore: bump discord-api-types to 0.37.45 (#9654) 2023-06-15 19:06:52 +00:00
RedGuy12
b61e4fb0dc fix: awaitMessageComponent with MessageComponentInteractions (#8598)
* fix: `awaitMessageComponent` with `MessageComponentInteraction`s

Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com>

* Update packages/discord.js/src/structures/InteractionCollector.js

Co-authored-by: Almeida <almeidx@pm.me>

* types: revert unrelated changes

Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com>

* Remove unneeded truey check

Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com>

* chore: remove extra block

Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com>

* fix: `awaitMessageComponent` with `MessageComponentInteraction`s

Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com>

* fix: `awaitMessageComponent` with `MessageComponentInteraction`s

Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com>

---------

Signed-off-by: RedGuy12 <61329810+RedGuy12@users.noreply.github.com>
Co-authored-by: Almeida <almeidx@pm.me>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-06-14 18:25:53 +00:00
Jiralite
d64330a157 types(AutoModerationActionExecution): Add forum channels as a possible type in channel() (#9623)
types(AutoModerationActionExecution): type forum channels

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-06-14 18:14:16 +00:00
Jiralite
2818d7cc1d fix(client): Add missing application command permissions update event (#9639)
fix(client): add missing event

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-06-14 18:11:00 +00:00
RedGuy12
a6dbe163dd fix(ThreadManager): Fix internal crash upon conditionally resolving thread members (#9648)
* fix(ThreadManager): Fix crash

* chore: prettier

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-06-14 18:07:48 +00:00
Souji
73c2f8aa17 feat(presence): re-introduce image resolving for other platforms (#9637)
* spotify, youtube, twitch
* regression born from misunderstanding in 9a6e691

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-06-14 17:45:28 +00:00
GrapeColor
a873ec1e85 feat: Add message to send resume event to shard (#9626)
* fix(WebSocketManager): Fix not ready after reconnect

* fix(WebSocketManager): Move fixes from Ready to Resume

* fix: undo changes

* feat: Add message to send resume event to shard

---------

Co-authored-by: GrapeColor <grapecolor@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-06-14 11:43:23 +00:00
dependabot[bot]
db925fa269 build(deps-dev): bump vite from 4.3.8 to 4.3.9 (#9625)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 4.3.8 to 4.3.9.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v4.3.9/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-06-13 19:20:07 +00:00
Ryan Munro
8f3bd38072 fix(User): check global name in equals (#9631)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-06-13 19:17:21 +00:00
ckohen
8f014c65ef chore: unpin rest, unbump node version req (#9632)
* chore: unpin rest, unbump node version req

* chore: update readmes

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-06-13 19:08:35 +00:00
brynpttrsn
1716202f44 fix(website): Overload switcher duplicates reference links (#9614)
fix(website): add idx to link key
2023-06-13 21:01:18 +02:00
Rodrigo Leitão
1cab79f6fd types(ModalSubmitFields): components is an array (#9406)
* types(ModalSubmitFields): components is an array

* types(ModalSubmitFields): make type coherent with docs

* refactor(ModalSubmitInteraction): remove undefined props
2023-06-13 20:58:50 +02:00
brynpttrsn
7b494aca5b fix(website): switch signature with overload (#9643) 2023-06-13 20:55:56 +02:00
Jiralite
cd6986854f types: Use readonly arrays and const type parameters in places (#9641)
* types: wrap arrays in `Readonly<>`

* refactor: prefer `readonly` syntax

Co-authored-by: Almeida <almeidx@pm.me>

* types: `const` type parameters

* test: add tests

* fix: fix more instances

---------

Co-authored-by: Almeida <almeidx@pm.me>
2023-06-12 18:00:45 +00:00
Jiralite
6c2242f4f9 types(BaseInteraction): appPermissions not null in guilds (#9601)
types(BaseInteraction): allow `appPermissions` only in guilds

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-06-09 10:02:22 +00:00
Mogyuchi
75308f2669 fix(WebSocketManager): await WebSocket destroy (#9519)
fix(WebSocketManager): await ws destroy

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-06-09 09:59:18 +00:00
İbrahim
bc2798b8ee docs: Define /core token in example (#9586)
* Importing Token

Importing token was missing, so I added it. It is getting the token from configuration file. 

It could have been done with defining a constant variable too, but importing makes more sense.

* Updated Token Field to "dotenv"

From now, user's can use dotenv package to keep their Discord client token.

* Referring token as DISCORD_TOKEN

Co-authored-by: Jaw0r3k <jaworekwiadomosci@gmail.com>

---------

Co-authored-by: Jaw0r3k <jaworekwiadomosci@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-06-09 09:41:07 +00:00
volcanofr
a531dc9f1c RESTOptions: (really) small description update for cdn (#9615)
Small option update cnd REST.ts

I just add "``" to uniform cdn @defaultValue with others one (like api).
The link is not broken anymore, because it do not work anymore.

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-06-09 09:00:20 +00:00
Ryoh827
911e6eff75 docs(ClientOptions): change default value of sweepers in docs (#9591)
* docs(ClientOptions): change default value of sweepers in docs

* docs(ClientOptions): set sweeper defaults by type

* docs(ClientOptions): add 'this.' to DefaultSweeperSettings in docs

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-06-09 08:45:42 +00:00
Synbulat Biishev
1ab60f9da4 feat: support new username system (#9512)
* feat: support new username system

* docs(GuildMember#displayName): update

* fix(User#hasNewUsername): use User#discriminator

* feat(User#tag): add new username

* docs(User#tag): update

* docs(User#hasNewUsername): update

* docs: update

* feat(User#defaultAvatarURL): update

* feat: remove some newly added properties

* docs(User#discriminator): update

* docs(User#globalName): bad Copilot

* feat(User#displayName): return display name

* types(User#displayName): not a partial structure

* feat: add `calculateUserDefaultAvatarId` function

* docs(CND#defaultAvatar): update

* docs(CDN#defaultAvatar): update

* feat: change default avatar id to type

* feat: deprecate `User#tag`

* docs(CDN#defaultAvatar): update

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

* docs(User): update

Co-authored-by: Rodrigo Leitão <38259440+ImRodry@users.noreply.github.com>

* feat: update

* typing: update

Co-authored-by: Jan <66554238+vaporoxx@users.noreply.github.com>

* feat: change the param type to `Snowflake`

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: Rodrigo Leitão <38259440+ImRodry@users.noreply.github.com>
Co-authored-by: Jan <66554238+vaporoxx@users.noreply.github.com>
2023-06-08 18:16:42 +00:00
Qjuh
df8b6e9934 fix(WebSocketManager): always cache result of fetchGatewayInformation (#9611)
* fix(WebSocketManager): always cache result of fetchGatewayInformation

* fix: add comment
2023-05-29 18:45:17 +00:00
nopeless
24a61495b9 fix(Client): safe call for possibly null WebSocket (#9600)
fix(client): safe call for possibly null Websocket
2023-05-26 17:27:17 +00:00
Jiralite
e1b6eeed0e docs(Client): Correct invite gateway permission checks (#9597)
docs: correct invite gateway permission checks
2023-05-26 08:18:48 +00:00
Jiralite
53aa24d418 fix(ThreadManager): Ensure fetchActive() only returns active threads in a channel (#9568)
fix(ThreadManager): revert breaking change

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-25 17:16:48 +00:00
iCrawl
9b06734445 chore: deps 2023-05-23 20:14:15 +02:00
Digital
9345d1b1ac fix(LimitedCollection): allow items to be stored if keepOverLimit is true when maxSize is 0 (#9534)
Update LimitedCollection.js

Keep value if it matches the limit.

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-22 08:11:47 +00:00
Jiralite
47843493a5 feat(guide): Add formatters popular topic (#9566)
* feat: add formatters page

* chore: rename pages
2023-05-22 10:06:48 +02:00
David Malchin
6c7a5ed1e7 fix(api): various fixes for overlooked stuff (#9588)
* types(GuildsAPI): fix `getWidgetSettings()` result type

* types(GuildsAPI): fix `beginPrune()` result type

* types(GuildsAPI): fix `editAutoModerationRule()` result type

* types(ApplicationCommandsAPI): fix guild application types

* types(GuildsAPI): fix `createTemplate()` body type

* fix(InteractionsAPI): make `followUp()` return the message

According to the Discord docs, creating a followup message is the same as executing a webhook, but *wait is always true*, meaning we always get the message.

* types(WebhooksAPI): fix result types
2023-05-22 07:59:57 +00:00
Jaw0r3k
985def3f25 feat(api): add stage instances (#9578)
* feat(core): add stage instances

* chore: use one import

* Apply suggestions from code review

Co-authored-by: Aura Román <kyradiscord@gmail.com>

* chore: requested changes

Co-authored-by: David Malchin <malchin459@gmail.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: David Malchin <malchin459@gmail.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

---------

Co-authored-by: Aura Román <kyradiscord@gmail.com>
Co-authored-by: David Malchin <malchin459@gmail.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-19 19:18:33 +00:00
Jiralite
1c4a12c7d6 types: use Snowflake instead of string for snowflakes (#9583)
fix: use `Snowflake` not `string`

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-19 17:33:43 +00:00
brynpttrsn
8107c5c0e7 fix(website): header and sidebar icons (#9574)
* fix: header icons

* Reorder imports

* remove field symbol

* fix type sidebar icon

* remove import

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-19 17:28:37 +00:00
Aura Román
b85a3f2dde refactor(collection): reduce reduce's code (#9581)
* refactor(collection): reduce `reduce`'s code

* fix: resolved bug in tests

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-18 21:05:48 +00:00
David Malchin
3535321b98 fix(GuildsAPI): use level rather than mfa_level when editing MFA (#9584) 2023-05-18 17:03:09 +00:00
Idris
46167a79d7 refactor(GuildMember): make _roles property non-enumerable (#9387)
* refactor(GuildMember): make _roles a getter and roles a normal prop

* fix: revert changes

* fix: requested changes

* fix: requested changes

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-18 10:13:19 +00:00
Idris
cffa5d19c9 feat(guide): port Collectors page (#9567)
* feat(guide): port Collectors page

* fix: requested changes

* fix: requested changes

* fix: suggested changes

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-18 09:06:00 +00:00
David Malchin
5d6eed6414 feat(GuildsAPI): add removeMember() (#9576) 2023-05-16 09:37:06 +00:00
iCrawl
90e243d350 chore: deps 2023-05-16 00:14:14 +02:00
David Malchin
590f5bc38e feat(api): add getMemberBans() query options and getMemberBan() (#9569) 2023-05-14 07:40:04 +00:00
Jiralite
7196fe36e8 fix(AutocompleteInteraction): Prevent snake casing of locales (#9565)
* fix(AutocompleteInteraction): prevent snake casing of locales

* fix: avoid unnecessary overhead ops

Co-authored-by: Aura Román <kyradiscord@gmail.com>

---------

Co-authored-by: Aura Román <kyradiscord@gmail.com>
2023-05-12 22:03:10 +00:00
iCrawl
5351ab429b build(ui): tree-shake ui components 2023-05-12 20:20:42 +02:00
iCrawl
d033f925e0 fix(websites/guide): remove flex-basis 2023-05-12 20:02:04 +02:00
iCrawl
13073acefc fix(ui): expose options for discord components 2023-05-12 20:00:02 +02:00
iCrawl
7c8849fae5 chore: deps 2023-05-12 19:58:51 +02:00
Jiralite
d01e8aa8af docs: Fix broken links to new documentation (#9563)
docs: fix broken links
2023-05-12 16:54:25 +00:00
DD
4dcc9c50f8 fix(WebSocketShard): handle initial connect being a resume (#9549)
* fix(WebSocketShard): handle initial connect being a resume

* chore: remove leftover

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-11 17:42:10 +00:00
Jiralite
a51c48e743 fix(webhooks): Remove incorrect properties in edit message code sample (#9562)
fix(webhooks): remove incorrect properties in edit message code sample
2023-05-11 16:43:54 +00:00
Jiralite
1edd01a7a4 feat(client): Support more request member fields (#9475)
feat(client): support more request member fields

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-10 16:40:52 +00:00
Jiralite
7b617bd22e feat: Create discord-api-types documentation component (#9555)
feat: add discord-api-types link component

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-09 19:49:07 +00:00
brynpttrsn
0c175c02e9 fix(website): package and version button formatting (#9556)
* fix for djs package button and removed redundant divs

* version arrow formatting

* formatting
2023-05-09 16:02:52 +02:00
Jiralite
460ac7dfe2 chore: Add repository directory (#9552)
chore: add repository directory
2023-05-08 17:12:31 +00:00
Jiralite
64324a8be1 feat: Check for empty directory (#9539)
* feat: check for empty directory

* fix: correct error

Co-authored-by: space <spaceeec@yahoo.com>

---------

Co-authored-by: space <spaceeec@yahoo.com>
2023-05-08 09:00:09 +02:00
Jiralite
b6162bc5b5 fix: Resolve imports for Windows (#9546)
fix: windows why

Co-authored-by: DD <didinele.dev@gmail.com>
Co-authored-by: Almeida <almeidx@pm.me>
2023-05-08 08:59:53 +02:00
synicalsyntax
280ed0ce08 fix(website): various UI improvements (#9538)
* fix(website): raise category z-index to prevent clipping

* refactor(website): wrap footer badges when necessary to prevent overflow
2023-05-08 08:59:25 +02:00
Jaw0r3k
5b8d535fd6 fix: Non-existing data property (#9544)
fix: not existed data prop

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-07 19:39:43 +00:00
Almeida
c4a3120354 feat(create-discord-bot): inherit stdio when installing deps (#9543) 2023-05-07 19:27:33 +00:00
Jaw0r3k
8482e3c95d fix(create-discord-bot): Fix start script (#9542)
fix: not working npm start

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-07 19:08:23 +00:00
Superchupu
12482b70ed docs: add Cloudflare sponsorship in create-discord-bot readme (#9540)
chore: add cloudflare sponsorship in `create-discord-bot` readme
2023-05-07 18:47:46 +00:00
iCrawl
fa746b079d chore: patch tsup 2023-05-07 19:17:10 +02:00
iCrawl
fc3f0756a4 chore: configs 2023-05-07 18:26:46 +02:00
iCrawl
3bfb6197a0 chore(create-discord-bot): release create-discord-bot@0.1.0 2023-05-07 18:13:04 +02:00
iCrawl
5ecc4f13b5 chore: re-gen yarn.lock 2023-05-07 18:10:56 +02:00
Jiralite
f83a8a58c9 feat: create-discord-bot (#9420)
* feat: basic initialisation

* fix: no scope

* chore: add options for issues

* feat: good word, Monbrey

* feat: basic README.md

* fix: no documentation for this

* feat: install for them

* chore: update licencing

* chore: fix year

* fix: build tsup

* feat: add TypeScript option

* feat: add `name` option

* chore: ignore annoying errors

* chore: add tsconfig.json

* refactor: remove name

We can just use the name of the directory instead.

* chore: update cliff jumper rc

* chore: bump dependencies

* chore: bump dependencies

* fix: build in prepack

* fix: configure ESLint correctly

* feat: infer package manager

* docs(packageManager): document `install()`

* fix(packageManager): do not emit a warning for `npm`

* refactor: change project name colour to yellow

* docs(constants): basic documentation

* feat: add link

* chore: add `verbatimModuleSyntax`

* chore: bump discord.js

* refactor: switch to @sapphire/ts-config

* refactor: file name changes

* refactor: tweak description

* chore: update yarn.lock

* fix: add .env

* chore: bump dependencies

* feat: event handler

* refactor: use `default`

* refactor: simpler event

* chore: bump discord.js

* fix: add release script and reorder

* style: reorder package.json

* chore: remove unneeded ignores

* chore: bump minimum Node.js version

* chore: add @types/node to TypeScript package.json

* chore: apply requested changes

Co-authored-by: Noel <buechler.noel@outlook.com>

* style: run ESLint + Prettier

* refactor: prefer "the"

* refactor: remove some comments

* feat: add ESLint + Prettier

* chore: requested changes

Co-authored-by: Noel <buechler.noel@outlook.com>

* chore: more requested changes

Co-authored-by: Noel <buechler.noel@outlook.com>

---------

Co-authored-by: Noel <buechler.noel@outlook.com>
2023-05-07 18:05:26 +02:00
iCrawl
9ddb3ff71d chore: deps 2023-05-07 18:04:10 +02:00
Superchupu
7ce6bb99e9 chore(deps): pin @discordjs/ws version in discord.js (#9533)
* chore(discord.js): pin `@discordjs/ws` version

* chore: update lockfile
2023-05-07 16:40:19 +02:00
Jiralite
da6de92e8c feat(DocsLink): Support overriding text (#9525) 2023-05-07 16:22:05 +02:00
synicalsyntax
bbf80efbb0 fix(guide): various UI improvements (#9532) 2023-05-07 16:20:54 +02:00
Synbulat Biishev
227232112d feat(GuildAuditLogsEntry#extra): add missing channel property (#9518)
* feat(GuildAuditLogsEntry#extra): add missing `channel` property

* types(GuildAuditLogsEntryExtraField): update

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2023-05-07 00:55:02 +00:00
Jaw0r3k
37181ab232 perf(Role): improve members getter (#9529)
perf(Role): improve members
2023-05-06 23:37:43 +00:00
ckohen
cdaa0a36f5 refactor(rest): switch api to fetch-like and provide strategies (#9416)
BREAKING CHANGE: NodeJS v18+ is required when using node due to the use of global `fetch`
BREAKING CHANGE: The raw method of REST now returns a web compatible `Respone` object.
BREAKING CHANGE: The `parseResponse` utility method has been updated to operate on a web compatible `Response` object.
BREAKING CHANGE: Many underlying internals have changed, some of which were exported.
BREAKING CHANGE: `DefaultRestOptions` used to contain a default `agent`, which is now set to `null` instead.
2023-05-06 21:09:19 +02:00
iCrawl
fc5b9c523b ci: positional arguments 2023-05-06 17:00:27 +02:00
iCrawl
256b68bf9f ci: pass push arguments to buildx 2023-05-06 16:54:08 +02:00
Jiralite
75195cc539 chore(guide): Update discord.js version to 14.11.0 (#9523) 2023-05-06 14:57:13 +02:00
DD
8d5ce32080 fix(proxy-container): docker builds (#9524) 2023-05-06 13:59:14 +02:00
iCrawl
5bfcf93e29 chore(discord.js): release discord.js@14.11.0 2023-05-06 11:06:10 +02:00
iCrawl
28f51791a2 chore(ws): release @discordjs/ws@0.8.3 2023-05-06 11:05:18 +02:00
DD
e6274681fd fix(WebSocketManager): properly emit shard error events (#9521)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-05 20:17:05 +00:00
DD
00da44a120 fix(WebSocketShard): wait a little before reconnecting (#9517)
* fix(WebSocketShard): wait a little before reconnecting

* chore: leftover comment

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-05 20:14:53 +00:00
Mateusz Burzyński
ab39683a50 chore: "hide" ./defaultWorker entrypoint with types: null (#9485)
* fix: move `types` condition to the front

* Update packages/ws/package.json

---------

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2023-05-05 20:08:12 +00:00
DD
3e80f0b384 tests(ws): fix tests (#9520) 2023-05-05 22:01:28 +02:00
iCrawl
e53b94c979 chore: remove cron 2023-05-05 21:21:09 +02:00
Almeida
6b2c3fb9d0 feat(Guild): safety alerts channel and mention raid protection (#8959)
* feat(Guild): add safety alerts channel and mention raid protection

* docs: add missing tag

* fix: keep other properties in triggerMetadata

* docs(Guild): update example usage

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2023-05-05 18:39:58 +00:00
iCrawl
d8d21cf370 chore: cloudflare sponsorship 2023-05-05 20:29:10 +02:00
Aura Román
8e9a2b4630 perf(Role): linear speed position getter (#9493)
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2023-05-05 12:49:00 +00:00
iCrawl
d4f12e0a0c chore: deps 2023-05-05 14:30:47 +02:00
Jiralite
d7b18f0681 types(StringSelectMenuComponentData): options is required (#9515)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-05 12:28:39 +00:00
Jiralite
166c961261 fix(roleConnections): Fix body type for updateMetadataRecords() (#9516) 2023-05-05 09:42:12 +02:00
Jiralite
506f7fcd3b fix(guide): Fix unknown component in v14 update guide (#9514)
* chore(constants): update discord.js to 14.10.2

* feat(DocsLink): handle only a package

* fix(updating-to-v14): fix unknown component
2023-05-05 07:37:47 +00:00
Jiralite
51d53f5117 feat(guide): Add webhooks (#9479)
* feat: add page

* feat: add assets

* chore: DocsLink

Co-authored-by: Ryan Munro <monbrey@gmail.com>

* fix: config import

* fix: another assert bug

* chore: lint

* fix: actually fix the destructure

---------

Co-authored-by: Ryan Munro <monbrey@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-04 08:49:18 +00:00
Idris
aa32d67619 chore(voice-description): being consistency with Node.js casing (#9505)
docs(voice-description): being consistency with Node.js casing

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-05-04 08:44:35 +00:00
Suneet Tipirneni
4b077e679c refactor(guide): use @type comments for command data code (#9489)
* refactor(guide): use `@type` comments for command data code

* fix: typo

* fix: use correct doc comment

* fix: use one line

* feat: add param types

* fix: use command interaction instead

* Update apps/guide/src/content/03-creating-your-bot/03-adding-commands.mdx

Co-authored-by: Souji <timoqueezle@gmail.com>

* chore: lint

---------

Co-authored-by: Souji <timoqueezle@gmail.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2023-05-03 23:58:53 +00:00
iCrawl
c190c57d64 chore: re-apply patches 2023-05-03 18:25:20 +02:00
Jaw0r3k
423bfce286 docs: Add bug/feature form for ui and util (#9504)
feat: add tags for other packages
2023-05-03 16:20:09 +00:00
iCrawl
5d05b7230a chore: re-gen yarn.lock 2023-05-03 02:30:15 +02:00
iCrawl
7e875f6919 chore: refactor workspace 2023-05-03 02:14:22 +02:00
Qjuh
c429763be8 fix(WebSocketManager): emit raw ws events again (#9502) 2023-05-02 19:49:55 +00:00
Jiralite
1b7981e469 docs(Client): Update Sapphire's snowflake utility links (#9501) 2023-05-02 12:56:38 +02:00
Aura Román
f2ad0762c5 refactor(GuildChannel): simplify constructor (#9499) 2023-05-02 10:32:47 +00:00
iCrawl
6d7f3b0f5c chore(discord.js): release discord.js@14.10.2 2023-05-02 01:18:23 +02:00
Almeida
201b002ad4 fix: correct isJSONEncodable() import (#9495)
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2023-05-01 22:25:51 +00:00
Almeida
b327f4925f fix(Client): generateInvite() scope validation (#9492) 2023-05-01 22:11:11 +00:00
iCrawl
e262df9117 chore(discord.js): release discord.js@14.10.1 2023-05-01 23:50:16 +02:00
iCrawl
7a761d4cfb chore: properly use cjs in module 2023-05-01 23:49:59 +02:00
iCrawl
ac9bf3ac06 fix(Client): spelling of InvalidScopesWithPermissions 2023-05-01 23:49:32 +02:00
iCrawl
133a6bbf49 chore(builders): release @discordjs/builders@1.6.3 2023-05-01 23:35:14 +02:00
iCrawl
63fc9f8504 chore(util): release @discordjs/util@0.3.1 2023-05-01 23:34:16 +02:00
Almeida
a9f2bff82a refactor(ShardClientUtil): logic de-duplication (#9491)
added docs to the `calculateShardId` function
2023-05-01 20:08:45 +00:00
Almeida
54ceedf6c5 refactor: remove @discordjs/util re-export (#9488) 2023-05-01 21:47:30 +02:00
Jiralite
36b7fb2f00 feat(guide): Update to 14.10.0 (#9487) 2023-05-01 21:05:29 +02:00
iCrawl
9a56edaf8c chore: add module resolution for the website 2023-05-01 18:21:02 +02:00
iCrawl
be81c8bf3b chore(discord.js): release discord.js@14.10.0 2023-05-01 17:42:07 +02:00
iCrawl
c77a1765c4 chore(core): release @discordjs/core@0.6.0 2023-05-01 17:35:40 +02:00
iCrawl
3665ad65d0 chore(ws): release @discordjs/ws@0.8.2 2023-05-01 17:34:27 +02:00
iCrawl
d4de1cd09e chore(proxy): release @discordjs/proxy@1.4.1 2023-05-01 17:31:00 +02:00
iCrawl
481bbc924f chore(rest): release @discordjs/rest@1.7.1 2023-05-01 17:29:19 +02:00
iCrawl
ca81e61b4c chore(formatters): release @discordjs/formatters@0.3.1 2023-05-01 17:24:36 +02:00
iCrawl
eb603c0d11 chore(util): release @discordjs/util@0.3.0 2023-05-01 17:22:55 +02:00
iCrawl
072c9da5df chore(collection): release @discordjs/collection@1.5.1 2023-05-01 17:20:16 +02:00
iCrawl
cd591327d6 chore(builders): release @discordjs/builders@1.6.2 2023-05-01 17:16:31 +02:00
iCrawl
71e67d8c97 chore(brokers): release @discordjs/brokers@0.2.1 2023-05-01 17:15:29 +02:00
Qjuh
a9e0de4288 refactor(WebSocketManager): use /ws package internally (#9099) 2023-05-01 17:08:23 +02:00
iCrawl
c5b4423a5a ci: properly add all tsconfig options 2023-05-01 16:44:36 +02:00
iCrawl
47b08be509 ci: fix start scripts for actions 2023-05-01 16:34:56 +02:00
iCrawl
2e0804ae92 ci: update node version 2023-05-01 16:27:29 +02:00
iCrawl
71983cc5d1 ci: make package optional for docs upload 2023-05-01 16:02:05 +02:00
iCrawl
0f3d870ce6 ci: properly look for docs 2023-05-01 15:57:28 +02:00
iCrawl
ce4e1fc2c6 ci: dramatically speed up docs building 2023-05-01 15:55:14 +02:00
iCrawl
0762ddb3f4 chore: deps 2023-05-01 15:29:51 +02:00
iCrawl
80af9696d3 build: leftover extensions 2023-05-01 15:23:15 +02:00
iCrawl
48cab844d4 build: switch to bundler moduleResolution 2023-05-01 13:14:13 +02:00
iCrawl
8b71f44b1f chore: deps 2023-05-01 12:05:55 +02:00
Almeida
f27631175a feat(ui): support embed image, thumbnail, author url, and url (#9478) 2023-04-30 16:56:34 +02:00
GrapeColor
ad217cc760 fix(ShardClientUtil): Fix client event names (#9474)
Co-authored-by: GrapeColor <grapecolor@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-30 10:16:44 +00:00
iCrawl
1ba7394a7d chore: deps 2023-04-30 09:01:17 +02:00
Jiralite
260c46d51c feat(guide): Add audit logs (#9429) 2023-04-30 07:50:14 +02:00
iCrawl
6229597db2 fix(guide): add more headings 2023-04-29 20:10:06 +02:00
iCrawl
8d212e4859 feat(guide): anchor links 2023-04-29 20:05:22 +02:00
iCrawl
51e8f02ef7 fix(ui): author and reply author spacing 2023-04-29 17:44:02 +02:00
Tetie
496c4e2884 feat(ui): support verified bots and colored authors (#9471)
Co-authored-by: Noel <buechler.noel@outlook.com>
2023-04-29 17:26:31 +02:00
Jiralite
77191a2e7b docs: Add new issue form for applications (#9470) 2023-04-28 21:47:19 +02:00
Jiralite
375483b0f9 feat(guide): Add threads (#9430) 2023-04-28 21:46:45 +02:00
Jiralite
17cd4b610a fix(guide): Show rest of TypeScript code block (#9465) 2023-04-28 21:46:27 +02:00
Suneet Tipirneni
988f8c7546 refactor(website): consolidate styling for buttons (#9466) 2023-04-28 21:42:47 +02:00
Jiralite
217e5d8100 fix(BaseClient): Prevent user agent mutation (#9425)
fix(BaseClient): prevent user agent mutation
2023-04-28 17:46:50 +00:00
Jaw0r3k
40d07fbbbd fix(BitField): use only enum names in iterating (#9357)
* fix: use only names in iterating bitfield

* fix: not a number

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>

* fix: serialize

Co-authored-by: SpaceEEC <spaceeec@yahoo.com>

---------

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>
Co-authored-by: SpaceEEC <spaceeec@yahoo.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-28 17:25:11 +00:00
anandre
1b15d31b5a docs(AutoModerationRule): update docs (#9464)
* Update AutoModerationRule.js

Update the docs descriptions for some of the AutoModerationRule methods to make them clearer and easier to understand

* Update AutoModerationRule.js

Updated the `exemptRoles` and `exemptChannels` parameter descriptions for their respective helper methods to match the typedef `AutoModerationRuleEditOptions`.

* Update AutoModerationRule.js

Update the `mentionTotalLimit` description to match the one in the `AutoModerationTriggerMetadataOptions` typdef.

* Update packages/discord.js/src/structures/AutoModerationRule.js

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-28 17:19:14 +00:00
Idris
d26df5faba types(AutoModerationActionMetadataOptions): make channel property optional (#9460)
* refactor(GuildMember): make _roles a getter and roles a normal prop

* types(AutoModerationActionMetadataOptions): make channel property optional

* fix: revert changes from another PR

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-28 17:13:27 +00:00
Ryan Munro
78fe247fc4 feat(guide): port and update lots of stuff (#9385)
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: Noel <buechler.noel@outlook.com>
2023-04-27 20:58:25 +02:00
RanAS
36216c0e1a docs: include StreamType enum description in new docs (#9457) 2023-04-27 20:29:27 +02:00
Jiralite
6212bffa30 docs: Update example usage (#9461) 2023-04-27 20:28:44 +02:00
Suneet Tipirneni
b2e1bb92d0 fix(website): don't duplicate overloaded methods (#9462) 2023-04-27 20:25:23 +02:00
Almeida
2e09cb4c1f feat(ui): support embed fields, footer icon, and timestamp (#9442) 2023-04-25 19:05:14 +02:00
Michael H
1b4d34cebf chore: Add directory to package.json (#9456)
* chore: Add directory to package.json

* fix: run fmt

* fix: undo formatting
2023-04-25 14:27:44 +00:00
iCrawl
8153ca99ee ci: update formatting tag 2023-04-24 20:19:40 +02:00
iCrawl
43e205b840 chore: deps 2023-04-24 19:46:44 +02:00
Jiralite
c6ca5a83e7 docs(APITypes): Document role & user select menu components (#9435)
docs(APITypes): document select menu components

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-21 20:56:51 +00:00
Jiralite
5c52bb9590 docs(BaseMessageOptions): Fix embeds and components (#9437)
docs(BaseMessageOptions): fix embeds and components

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-21 20:43:17 +00:00
DD
a49ed0a2d5 refactor(proxy): rely on auth header instead (#9422)
* refactor(proxy): rely on auth header instead

* chore: typo

* chore: language

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

* chore: more language

Co-authored-by: Aura Román <kyradiscord@gmail.com>

* chore: more language nitpicks

Co-authored-by: ckohen <chaikohen@gmail.com>

* fix: unnecessary async

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: Aura Román <kyradiscord@gmail.com>
Co-authored-by: ckohen <chaikohen@gmail.com>
2023-04-21 20:36:15 +00:00
Almeida
3e01f91bbb feat(Attachment): voice messages (#9392)
Co-authored-by: Tetie <tjvssr@gmail.com>
2023-04-21 11:09:37 +02:00
anandre
7d341000d4 docs(InteractionResponses): showModal docs change (#9434)
* Update InteractionResponses.js

Update the parameters for the showModal method

* Update APITypes.js

Add APIModalInteractionResponseCallbackData link for docs link for showModal method

* Update InteractionResponses.js

Change the order of parameters so the ModalBuilder is listed first

* Update APITypes.js

Disable the ESline line length rule for the APITypes file

* Update InteractionResponses.js

Add ModalComponentData to the JSDoc, it was omitted before.
2023-04-21 08:17:44 +00:00
Jiralite
b49c5c9f48 ci: Fix issue triage (#9433) 2023-04-20 20:41:21 +02:00
MrMythicalYT
ce287f21d1 docs: use ESM code in examples (#9427)
* docs(Partials): use import statement

* docs: update readme to ESM

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-20 06:40:15 +00:00
MrMythicalYT
d6bca9bb4d docs: reference package names properly (#9426) 2023-04-20 06:25:32 +00:00
Jiralite
a2ab2b8717 feat(scripts): Sort issue labels (#9423)
feat(scripts): sort issue labels

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-19 20:09:05 +00:00
Suneet Tipirneni
ecd1b5da11 refactor(website): consolidate badge logic (#9417)
Co-authored-by: Noel <buechler.noel@outlook.com>
2023-04-19 18:58:35 +02:00
Jiralite
0eb866357b ci: Update actions (#9424) 2023-04-19 18:57:16 +02:00
Almeida
d2d27ce734 fix: use new permission name (#9274)
* fix: use new permission name

* docs: requested changes

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-19 14:32:07 +00:00
Jiralite
603446a692 chore: Bump dependencies for template package (#9418)
* chore: bump dependencies

* chore: update .yarnrc.yml
2023-04-19 05:43:46 +00:00
Suneet Tipirneni
f1fdd5b010 refactor(website): extract shared code heading styling into component (#9414)
* refactor(website): extract shared code heading styling into component

* chore: remove redundant variable
2023-04-17 21:33:51 +00:00
DD
5d1a4c27d5 feat(core): abstract gateway (#9410)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-17 20:05:05 +00:00
iCrawl
98a76db482 docs: generate static imports for types with api-extractor 2023-04-17 19:08:01 +02:00
Suneet Tipirneni
516c46117c feat(website): use tsdoc parameter names wherever possible (#9413)
Co-authored-by: Noel <buechler.noel@outlook.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2023-04-17 18:44:17 +02:00
Tetie
7d7972d239 chore(Client): robust error checking (#9390)
* chore: robust error checking

* fix: check for null

* chore: add period

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

* fix: check for undefined correctly

* chore: robust error checking

* fix: check for null

* chore: add period

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

* fix: check for undefined correctly

* fix: check for null

* fix: update typings

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2023-04-17 15:05:32 +00:00
Jiralite
7b10e8cb63 feat: Add FAQ (#9407)
* feat: port over FAQ

* refactor: use async/await

Co-authored-by: Almeida <almeidx@pm.me>

* style: spacing

* refactor: use role id

* feat: more links!

* refactor: move comment up

* refactor: consistent naming style

---------

Co-authored-by: Almeida <almeidx@pm.me>
2023-04-17 08:31:53 +00:00
Jiralite
2f18323d2c fix(updating-to-v14): Parameter typo (#9408)
fix: parameter typo
2023-04-17 01:35:34 +00:00
Suneet Tipirneni
e04801bba1 fix(website): add equals to enum members (#9405) 2023-04-16 21:43:37 +00:00
527 changed files with 20869 additions and 12678 deletions

View File

@@ -1,31 +1,33 @@
# Packages
node_modules/
**/node_modules
# Log files
logs/
*.log
npm-debug.log*
**/logs
**/*.log
**/npm-debug.log*
# Runtime data
pids
*.pid
*.seed
**/pids
**/*.pid
**/*.seed
# Env
.env
**/.env
# Dist
dist/
**/dist/
**/dist-docs/
# Miscellaneous
.tmp/
.vscode/*
.idea/
.DS_Store
.turbo
tsconfig.tsbuildinfo
coverage/
__tests__/
**/.tmp
**/.vscode
**/.idea
**/.DS_Store
**/.turbo
**/tsconfig.tsbuildinfo
**/coverage
**/__tests__
**/out
# yarn
.pnp.*
@@ -37,20 +39,29 @@ __tests__/
!.yarn/versions
# Cache
.prettiercache
.eslintcache
**/.prettiercache
**/.eslintcache
**/.vercel
# Docker specific
.cliff-jumperrc.json
api-extractor.json
.eslintrc.json
.lintstagedrc.cjs
.lintstagedrc.cjs
.prettierignore
.prettierrc.js
.prettierrc.cjs
cliff.toml
CHANGELOG.md
README.md
tsconfig.eslint.json
docs/
**/.cliff-jumperrc.json
**/api-extractor.json
**/api-extractor-docs.json
**/.eslintignore
**/.eslintrc.json
**/.lintstagedrc.js
**/.lintstagedrc.cjs
**/.lintstagedrc.json
**/.prettierignore
**/.prettierrc.js
**/.prettierrc.cjs
**/.prettierrc.json
**/cliff.toml
**/CHANGELOG.md
**/README.md
**/LICENSE
**/tsconfig.eslint.json
**/tsconfig.docs.json
**/docs/
**/vitest.config.ts

View File

@@ -2,7 +2,7 @@
"root": true,
"extends": ["neon/common", "neon/node", "neon/typescript", "neon/prettier"],
"parserOptions": {
"project": "./tsconfig.eslint.json"
"project": ["./tsconfig.eslint.json", "./apps/*/tsconfig.eslint.json", "./packages/*/tsconfig.eslint.json"]
},
"rules": {
"@typescript-eslint/consistent-type-definitions": ["error", "interface"]

1
.github/CODEOWNERS vendored
View File

@@ -13,6 +13,7 @@
/packages/builders/ @discordjs/builders
/packages/collection/ @discordjs/collection
/packages/core/ @discordjs/core
/packages/create-discord-bot/ @discordjs/guide
/packages/discord.js/ @discordjs/core
/packages/docgen/ @iCrawl
/packages/formatters/ @discordjs/formatters

View File

@@ -45,4 +45,4 @@ yarn create-package <package-name> [package-description]
```
This will create new package directory under `packages/` with the required configuration files. You can
begin to make changes within the `src/` directory.
begin to make changes within the `src/` directory. It may also be required to update the CODEOWNERS file.

View File

@@ -1,11 +1,13 @@
name: Bug report
description: Report incorrect or unexpected behavior of a package
description: Report an issue with discord.js or another package.
labels: [bug, need repro]
body:
- type: markdown
attributes:
value: |
Use Discord for questions: https://discord.gg/djs
Thank you for filing an issue! If you are here to ask a question, use Discord instead: https://discord.gg/djs
This issue form is for discord.js, including other packages.
- type: dropdown
id: package
attributes:
@@ -16,11 +18,14 @@ body:
- builders
- collection
- core
- create-discord-bot
- formatters
- next
- proxy
- proxy-container
- rest
- ui
- util
- voice
- ws
validations:
@@ -29,57 +34,44 @@ body:
id: description
attributes:
label: Issue description
description: |
Describe the issue in as much detail as possible.
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files into it.
description: Describe the issue in as much detail as possible.
placeholder: |
Steps to reproduce with below code sample:
1. do thing
2. do thing in Discord client
3. observe behavior
4. see error logs below
1. Do thing
2. Do thing in Discord client
3. Observe behavior
4. See error logs below
validations:
required: true
- type: textarea
id: codesample
id: code_sample
attributes:
label: Code sample
description: Include a reproducible, minimal code sample. This will be automatically formatted into code, so no need for backticks.
render: typescript
placeholder: |
Your code sample should be...
... Minimal - Use as little code as possible that still produces the same problem (and is understandable)
... Complete - Provide all parts someone else needs to reproduce your problem
... Reproducible - Test the code you're about to provide to make sure it reproduces the problem
- type: input
id: djs-version
attributes:
label: Package version
description: Which version of the package are you using? Run `npm list <package>` in your project directory and paste the output.
placeholder: We no longer support version 12 or earlier of discord.js
validations:
required: true
- type: input
id: node-version
attributes:
label: Node.js version
description: |
Which version of Node.js are you using? Run `node --version` in your project directory and paste the output.
If you are using TypeScript, please include its version (`npm list typescript`) as well.
placeholder: Node.js version 16.9+ is required for version 14.0.0+
Your code sample should be:
1. Minimal - Use as little code as possible that still produces the same problem (and is understandable)
2. Complete - Provide all parts someone else needs to reproduce your problem
3. Reproducible - Test the code you're about to provide to make sure it reproduces the problem
This will be automatically formatted into code, so no need for backticks.
render: typescript
- type: textarea
id: versions
attributes:
label: Versions
description: List necessary versions here. This includes your package version, runtime version, operating system etc.
placeholder: |
- discord.js 14.12.1 (`npm ls discord.js` or another package)
- Node.js 16.11.0 (`node --version`)
- TypeScript 5.1.6 (`npm ls typescript` if you use it)
- macOS Ventura 13.3.1
validations:
required: true
- type: input
id: os
attributes:
label: Operating system
description: Which OS does your application run on?
- type: dropdown
id: priority
attributes:
label: Priority this issue should have
description: Please be realistic. If you need to elaborate on your reasoning, please use the Issue description field above.
label: Issue priority
description: Please be realistic. If you need to elaborate on your reasoning, please use the issue description field above.
options:
- Low (slightly annoying)
- Medium (should be fixed soon)
@@ -90,12 +82,9 @@ body:
id: partials
attributes:
label: Which partials do you have configured?
description: |
Check your Client constructor for the `partials` key.
Tip: you can select multiple items
description: Check your `Client` constructor for the `partials` key.
options:
- Not applicable (subpackage bug)
- Not applicable
- No Partials
- User
- Channel
@@ -111,12 +100,9 @@ body:
id: intents
attributes:
label: Which gateway intents are you subscribing to?
description: |
Check your Client constructor options for the `intents` key.
Tip: you can select multiple items
description: Check your `Client` constructor for the `intents` key.
options:
- Not applicable (subpackage bug)
- Not applicable
- No Intents
- Guilds
- GuildMembers
@@ -141,7 +127,7 @@ body:
validations:
required: true
- type: input
id: dev-release
id: dev_release
attributes:
label: I have tested this issue on a development release
placeholder: d23280c (commit hash)

View File

@@ -0,0 +1,58 @@
name: Websites bug report
description: Report an issue with the documentation or guide websites.
labels: [bug, need repro]
body:
- type: markdown
attributes:
value: |
Thank you for filing an issue! If you are here to ask a question, use Discord instead: https://discord.gg/djs
This issue form is for our documentation and guide websites.
- type: dropdown
id: application
attributes:
label: Which application is this bug report for?
options:
- Documentation
- Guide
validations:
required: true
- type: textarea
id: description
attributes:
label: Issue description
description: Describe the issue in as much detail as possible.
validations:
required: true
- type: textarea
id: steps_to_reproduce
attributes:
label: Steps to Reproduce
description: What steps must be taken to reproduce this issue?
placeholder: |
1. Visit a page
2. Click a link
3. ...
validations:
required: true
- type: textarea
id: versions
attributes:
label: Versions
description: List necessary versions here. This includes your browser, operating system etc.
placeholder: |
- Safari 16.4 (18615.1.26.11.23)
- macOS Ventura 13.3.1
validations:
required: true
- type: dropdown
id: priority
attributes:
label: Issue priority
description: Please be realistic. If you need to elaborate on your reasoning, please use the issue description field above.
options:
- Low (slightly annoying)
- Medium (should be fixed soon)
- High (immediate attention needed)
validations:
required: true

View File

@@ -9,20 +9,25 @@ body:
We do not implement unreleased features.
Use Discord for questions: https://discord.gg/djs
- type: dropdown
id: package
id: application_or_package
attributes:
label: Which package is this feature request for?
label: Which application or package is this feature request for?
options:
- discord.js
- Documentation
- Guide
- brokers
- builders
- collection
- core
- create-discord-bot
- formatters
- next
- proxy
- proxy-container
- rest
- ui
- util
- voice
- ws
validations:
@@ -48,7 +53,7 @@ body:
label: Alternative solutions or implementations
description: A clear and concise description of any alternative solutions or features you have considered.
- type: textarea
id: additional-context
id: additional_context
attributes:
label: Other context
description: Any other context, screenshots, or file uploads that help us understand your feature request.

View File

@@ -1,24 +1,51 @@
apps:guide:
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\nGuide\\n"
apps:website:
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\nDocumentation\\n"
packages:brokers:
- '### Which package is this (bug report|feature request) for\?\n\nbrokers'
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\nbrokers\\n"
packages:builders:
- '### Which package is this (bug report|feature request) for\?\n\nbuilders'
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\nbuilders\\n"
packages:collection:
- '### Which package is this (bug report|feature request) for\?\n\ncollection'
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\ncollection\\n"
packages:core:
- '### Which package is this (bug report|feature request) for\?\n\ncore'
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\ncore\\n"
packages:create-discord-bot:
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\ncreate-discord-bot\\n"
packages:discord.js:
- '### Which package is this (bug report|feature request) for\?\n\ndiscord.js'
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\ndiscord.js\\n"
packages:formatters:
- '### Which package is this (bug report|feature request) for\?\n\nformatters'
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\nformatters\\n"
packages:next:
- '### Which package is this (bug report|feature request) for\?\n\nnext'
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\nnext\\n"
packages:proxy:
- '### Which package is this (bug report|feature request) for\?\n\nproxy'
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\nproxy\\n"
packages:proxy-container:
- '### Which package is this (bug report|feature request) for\?\n\nproxy-container'
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\nproxy-container\\n"
packages:rest:
- '### Which package is this (bug report|feature request) for\?\n\nrest'
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\nrest\\n"
packages:ui:
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\ui\\n"
packages:util:
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\util\\n"
packages:voice:
- '### Which package is this (bug report|feature request) for\?\n\nvoice'
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\nvoice\\n"
packages:ws:
- '### Which package is this (bug report|feature request) for\?\n\nws'
- "### Which (application|package|application or package) is this (bug
report|feature request) for\\?\\n\\nws\\n"

9
.github/labeler.yml vendored
View File

@@ -16,18 +16,21 @@ packages:collection:
packages:core:
- packages/core/*
- packages/core/**/*
packages:create-discord-bot:
- packages/create-discord-bot/*
- packages/create-discord-bot/**/*
packages:discord.js:
- packages/discord.js/*
- packages/discord.js/**/*
packages:next:
- packages/next/*
- packages/next/**/*
packages:docgen:
- packages/docgen/*
- packages/docgen/**/*
packages:formatters:
- packages/formatters/*
- packages/formatters/**/*
packages:next:
- packages/next/*
- packages/next/**/*
packages:proxy:
- packages/proxy/*
- packages/proxy/**/*

14
.github/labels.yml vendored
View File

@@ -1,7 +1,7 @@
- name: api changes
color: '5663e9'
color: 5663e9
- name: api support
color: '5663e9'
color: 5663e9
- name: apps:guide
color: fbca04
- name: apps:website
@@ -23,7 +23,7 @@
- name: dependencies
color: 276bd1
- name: discord
color: '5663e9'
color: 5663e9
- name: discussion
color: b6b1f9
- name: documentation
@@ -41,7 +41,7 @@
- name: has PR
color: 4b1f8e
- name: help wanted
color: '008672'
color: 008672
- name: in progress
color: ffccd7
- name: in review
@@ -60,14 +60,16 @@
color: fbca04
- name: packages:core
color: fbca04
- name: packages:discord.js
- name: packages:create-discord-bot
color: fbca04
- name: packages:next
- name: packages:discord.js
color: fbca04
- name: packages:docgen
color: fbca04
- name: packages:formatters
color: fbca04
- name: packages:next
color: fbca04
- name: packages:proxy
color: fbca04
- name: packages:proxy-container

BIN
.github/powered-by-workers.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -24,7 +24,7 @@ concurrency:
cancel-in-progress: true
jobs:
build:
name: Build documentation
name: Build & upload documentation
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
@@ -36,10 +36,10 @@ jobs:
with:
ref: ${{ inputs.ref || '' }}
- name: Install node.js v16
- name: Install node.js v18
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install dependencies
uses: ./packages/actions/src/yarnCache
@@ -50,71 +50,6 @@ jobs:
- name: Build docs
run: yarn docs
- name: Upload docgen artifacts
uses: actions/upload-artifact@v3
with:
name: docgen
path: packages/*/docs/docs.json
- name: Upload api-extractor artifacts
uses: actions/upload-artifact@v3
with:
name: api-extractor
path: packages/*/docs/docs.api.json
upload:
name: Upload Documentation
needs: build
strategy:
max-parallel: 1
fail-fast: false
matrix:
package:
[
'brokers',
'builders',
'collection',
'core',
'discord.js',
'next',
'formatters',
'proxy',
'rest',
'util',
'voice',
'ws',
]
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Install node.js v16
uses: actions/setup-node@v3
with:
node-version: 16
- name: Install dependencies
uses: ./packages/actions/src/yarnCache
- name: Build actions
run: yarn workspace @discordjs/actions build
- name: Download docgen artifacts
uses: actions/download-artifact@v3
with:
name: docgen
path: docs
- name: Download api-extractor artifacts
uses: actions/download-artifact@v3
with:
name: api-extractor
path: docs
- name: Checkout docs repository
uses: actions/checkout@v3
with:
@@ -130,7 +65,7 @@ jobs:
tag: ${{ github.ref_name }}
- name: Upload documentation to database
if: ${{ github.ref_type == 'tag' && matrix.package == steps.extract-tag.outputs.package }}
if: ${{ github.ref_type == 'tag' }}
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
uses: ./packages/actions/src/uploadDocumentation
@@ -139,17 +74,16 @@ jobs:
version: ${{ steps.extract-tag.outputs.semver }}
- name: Move docs to correct directory
if: ${{ github.ref_type == 'tag' && matrix.package == steps.extract-tag.outputs.package }}
if: ${{ github.ref_type == 'tag' }}
env:
PACKAGE: ${{ steps.extract-tag.outputs.package }}
SEMVER: ${{ steps.extract-tag.outputs.semver }}
run: |
mkdir -p out/${PACKAGE}
if [[ $PACKAGE == "discord.js" ]]; then
mv docs/${PACKAGE}/docs/docs.json out/${PACKAGE}/${SEMVER}.json
fi
if [[ $PACKAGE != "discord.js" ]]; then
mv docs/${PACKAGE}/docs/docs.api.json out/${PACKAGE}/${SEMVER}.api.json
mkdir -p "out/${PACKAGE}"
if [[ "${PACKAGE}" == "discord.js" ]]; then
mv "packages/${PACKAGE}/docs/docs.json" "out/${PACKAGE}/${SEMVER}.json"
else
mv "packages/${PACKAGE}/docs/docs.api.json" "out/${PACKAGE}/${SEMVER}.api.json"
fi
- name: Upload documentation to database
@@ -157,21 +91,20 @@ jobs:
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
uses: ./packages/actions/src/uploadDocumentation
with:
package: ${{ matrix.package }}
- name: Move docs to correct directory
if: ${{ github.ref_type == 'branch' }}
env:
PACKAGE: ${{ matrix.package }}
run: |
mkdir -p out/${PACKAGE}
if [[ $PACKAGE == "discord.js" ]]; then
mv docs/${PACKAGE}/docs/docs.json out/${PACKAGE}/${GITHUB_REF_NAME}.json
fi
if [[ $PACKAGE != "discord.js" ]]; then
mv docs/${PACKAGE}/docs/docs.api.json out/${PACKAGE}/${GITHUB_REF_NAME}.api.json
fi
declare -a PACKAGES=("brokers" "builders" "collection" "core" "discord.js" "next" "formatters" "proxy" "rest" "util" "voice" "ws")
for PACKAGE in "${PACKAGES[@]}"; do
if [[ "${PACKAGE}" == "discord.js" ]]; then
mkdir -p "out/${PACKAGE}"
mv "packages/${PACKAGE}/docs/docs.json" "out/${PACKAGE}/${GITHUB_REF_NAME}.json"
else
mkdir -p "out/${PACKAGE}"
mv "packages/${PACKAGE}/docs/docs.api.json" "out/${PACKAGE}/${GITHUB_REF_NAME}.api.json"
fi
done
- name: Commit and push
run: |

View File

@@ -6,7 +6,7 @@ jobs:
issue-triage:
runs-on: ubuntu-latest
steps:
- uses: github/issue-labeler@v2.5
- uses: github/issue-labeler@v3.1
with:
repo-token: '${{ secrets.GITHUB_TOKEN }}'
configuration-path: .github/issue-labeler.yml

View File

@@ -10,7 +10,7 @@ jobs:
steps:
- name: Get Vercel preview URL
id: get_preview_url
uses: actions/github-script@v3
uses: actions/github-script@v6
with:
script: |
const comment = context.payload.comment;
@@ -53,7 +53,7 @@ jobs:
- name: Format lighthouse score
if: ${{ steps.get_preview_url.outputs.vercel_preview_url != '' }}
id: format_lighthouse_score
uses: actions/github-script@v3
uses: actions/github-script@v6
with:
script: |
const result = ${{ steps.lighthouse_audit.outputs.manifest }}[0].summary

View File

@@ -12,10 +12,10 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3
- name: Install node.js v16
- name: Install node.js v18
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install dependencies
uses: ./packages/actions/src/yarnCache

View File

@@ -12,10 +12,10 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3
- name: Install node.js v16
- name: Install node.js v18
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install dependencies
uses: ./packages/actions/src/yarnCache
@@ -29,8 +29,5 @@ jobs:
- name: Login to DockerHub
run: echo ${{ secrets.DOCKER_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
- name: Build the image
run: yarn docker build @discordjs/proxy-container -t discordjs/proxy:latest
- name: Push image to DockerHub
run: docker push discordjs/proxy:latest
- name: Build & push docker image
run: yarn docker build --buildkit @discordjs/proxy-container -t discordjs/proxy:latest --push

View File

@@ -42,11 +42,13 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install node.js v16
- name: Install node.js v18
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
registry-url: https://registry.npmjs.org/
- name: Install dependencies

View File

@@ -9,10 +9,10 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3
- name: Install node.js v16
- name: Install node.js v18
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install dependencies
uses: ./packages/actions/src/yarnCache
@@ -26,11 +26,5 @@ jobs:
- name: Login to DockerHub
run: echo ${{ secrets.DOCKER_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
- name: Build docker image
run: yarn docker build @discordjs/proxy-container -t discordjs/proxy:latest
- name: Tag image with major
run: docker tag discordjs/proxy discordjs/proxy:$(cut -d '.' -f1 <<< $(jq --raw-output '.version' packages/proxy-container/package.json))
- name: Push image to DockerHub
run: docker push --all-tags discordjs/proxy
- name: Build & push docker image
run: yarn docker build --buildkit @discordjs/proxy-container -t discordjs/proxy:$(cut -d '.' -f1 <<< $(jq --raw-output '.version' packages/proxy-container/package.json)) --push

View File

@@ -14,10 +14,10 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3
- name: Install node.js v16
- name: Install node.js v18
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
registry-url: https://registry.npmjs.org/
- name: Install dependencies

View File

@@ -19,10 +19,10 @@ jobs:
with:
fetch-depth: 0
- name: Install node.js v16
- name: Install node.js v18
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Install dependencies
uses: ./packages/actions/src/yarnCache

18
.gitignore vendored
View File

@@ -1,8 +1,8 @@
# Packages
node_modules/
node_modules
# Log files
logs/
logs
*.log
npm-debug.log*
@@ -15,20 +15,20 @@ pids
.env
# Dist
dist/
dist-docs/
dist
dist-docs
# Miscellaneous
.tmp/
.vscode/*
.tmp
.vscode
!.vscode/extensions.json
!.vscode/settings.json
.idea/
.idea
.DS_Store
.turbo
tsconfig.tsbuildinfo
coverage/
out/
coverage
out
# yarn
.pnp.*

View File

@@ -1 +1,2 @@
CODEOWNERS
CHANGELOG.md

View File

@@ -0,0 +1,18 @@
diff --git a/package.json b/package.json
index fc35658a40f9ba3e3513c459ba9f4f6e1b3f59f5..bc35eda66f270c95ba52e721cb6976fd61622c58 100644
--- a/package.json
+++ b/package.json
@@ -26,11 +26,13 @@
},
"exports": {
".": {
+ "types": "./dist/index.d.ts",
"node": "./dist/index.js",
"default": "./browser/index.js"
},
"./package.json": "./package.json",
"./util": {
+ "types": "./dist/util.d.ts",
"node": "./dist/util.js",
"default": "./browser/dist/util.js"
}

View File

@@ -215,7 +215,8 @@ module.exports = {
await c({ workspace: e, report: t, destination: u });
});
}
await i.execUtils.pipevp('docker', ['build', ...this.args, '-f', s, '.'], {
const h = this.buildKit ? ['buildx', 'build'] : ['build'];
await i.execUtils.pipevp('docker', [...h, ...this.args, '-f', s, '.'], {
cwd: o,
strict: !0,
stdin: this.context.stdin,
@@ -241,6 +242,7 @@ module.exports = {
'yarn docker build --copy secret.key --copy config.json @foo/bar',
],
['Install production dependencies only', 'yarn docker build --production @foo/bar'],
['Build a Docker image using BuildKit', 'yarn docker build --buildkit @foo/bar'],
],
})),
d([r.Command.String()], f.prototype, 'workspaceName', void 0),
@@ -248,6 +250,7 @@ module.exports = {
d([r.Command.String('-f,--file')], f.prototype, 'dockerFilePath', void 0),
d([r.Command.Array('--copy')], f.prototype, 'copyFiles', void 0),
d([r.Command.Boolean('--production')], f.prototype, 'production', void 0),
d([r.Command.Boolean('--buildkit')], f.prototype, 'buildKit', void 0),
d([r.Command.Path('docker', 'build')], f.prototype, 'execute', null);
const u = { commands: [f] };
plugin = e;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,13 @@
logFilters:
- code: YN0002
level: discard
- code: YN0013
level: discard
- code: YN0032
level: discard
- code: YN0060
level: discard
nodeLinker: node-modules
plugins:
@@ -10,9 +20,4 @@ plugins:
- path: .yarn/plugins/@yarnpkg/plugin-docker-build.cjs
spec: 'https://github.com/Dcard/yarn-plugins/releases/latest/download/plugin-docker-build.js'
yarnPath: .yarn/releases/yarn-3.5.0.cjs
packageExtensions:
'@storybook/core-common@*':
dependencies:
'@storybook/react-vite': '7.0.5'
yarnPath: .yarn/releases/yarn-3.6.1.cjs

View File

@@ -13,6 +13,7 @@
</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>

View File

@@ -88,9 +88,44 @@
*
* DEFAULT VALUE: no overrideTsconfig section
*/
// "overrideTsconfig": {
// . . .
// }
"overrideTsconfig": {
// Type Checking
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"exactOptionalPropertyTypes": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"strict": true,
"useUnknownInCatchVariables": true,
"noUncheckedIndexedAccess": true,
// Modules
"module": "ESNext",
"moduleResolution": "node",
"resolveJsonModule": true,
// Emit
"declaration": true,
"declarationMap": true,
"importHelpers": true,
"inlineSources": true,
"newLine": "lf",
"noEmitHelpers": true,
"outDir": "dist",
"removeComments": false,
"sourceMap": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
// Language and Environment
"experimentalDecorators": true,
"lib": ["ESNext"],
"target": "ES2021",
"useDefineForClassFields": true
}
/**
* This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended
* and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when
@@ -181,7 +216,7 @@
/**
* (REQUIRED) Whether to generate the .d.ts rollup file.
*/
"enabled": false
"enabled": true,
/**
* Specifies the output path for a .d.ts rollup file to be generated without any trimming.
@@ -195,7 +230,7 @@
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<projectFolder>/dist/<unscopedPackageName>.d.ts"
*/
// "untrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>.d.ts",
"untrimmedFilePath": "<projectFolder>/dist-docs/index.d.ts"
/**
* Specifies the output path for a .d.ts rollup file to be generated with trimming for an "alpha" release.

1
apps/guide/.eslintignore Normal file
View File

@@ -0,0 +1 @@
next-env.d.ts

21
apps/guide/.gitignore vendored
View File

@@ -1,8 +1,8 @@
# Packages
node_modules/
node_modules
# Log files
logs/
logs
*.log
npm-debug.log*
@@ -16,18 +16,13 @@ pids
.env*.local
# Dist
dist/
typings/
.cache/
build/
.contentlayer
.next
public/searchIndex
src/styles/unocss.css
.next/
# Miscellaneous
.tmp/
coverage/
.vercel
public/searchIndex
.tmp
.vscode
lighthouse-results/
.contentlayer
lighthouse-results

View File

@@ -1,15 +1,7 @@
# Autogenerated
CHANGELOG.md
.contentlayer
.next
.turbo
dist/
docs/**/*
!docs/index.yml
!docs/README.md
coverage/
.cache
build/
.vscode
coverage
src/styles/unocss.css
api/
.next/
.vercel/
.cache/
next-env.d.ts

View File

@@ -10,6 +10,7 @@
</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>

View File

@@ -1,9 +1,9 @@
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 { 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';
@@ -36,33 +36,31 @@ export const Content = defineDocumentType(() => ({
},
}));
// const LinkIcon = h(
// 'svg',
// {
// width: '1rem',
// height: '1rem',
// 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 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) => {
// const node = h('span.sr-only', `Section titled ${escape(text)}`);
// node.properties!['is:raw'] = true;
// return node;
// };
const createSROnlyLabel = (text: any) => {
return h('span', { class: 'sr-only' }, `Section titled ${escape(text)}`);
};
export default makeSource({
contentDirPath: 'src/content',
@@ -71,31 +69,26 @@ export default makeSource({
remarkPlugins: [remarkGfm, [remarkCodeHike, { theme: codeHikeThemeDarkPlus, lineNumbers: true }]],
rehypePlugins: [
rehypeSlug,
// [
// rehypeAutolinkHeadings,
// {
// properties: {
// class:
// 'relative inline-flex w-6 h-6 place-items-center place-content-center outline-none text-black dark:text-white ml-2',
// },
// behavior: 'after',
// group: async ({ tagName }: { tagName: string }) =>
// h('div', {
// class: `[&>*]:inline-block [&>h1]:m-0 [&>h2]:m-0 [&>h3]:m-0 [&>h4]:m-0 level-${tagName}`,
// tabIndex: -1,
// }),
// content: (heading: Node) => [
// h(
// `span.anchor-icon`,
// {
// ariaHidden: 'true',
// },
// LinkIcon,
// ),
// createSROnlyLabel(toString(heading)),
// ],
// },
// ],
[
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

@@ -7,16 +7,15 @@
"test": "vitest run",
"test:lighthouse": "lighthouse http://localhost:3000 --output-path=./lighthouse-results",
"build:local": "yarn build:prod",
"build:prod": "yarn workspaces foreach -ptR run build && yarn build:css && yarn build:next",
"build:prod": "yarn build:css && yarn build:next",
"build:next": "next build",
"build:css": "yarn generate:css",
"build:analyze": "cross-env-shell ANALYZE=true yarn build:prod",
"build:analyze": "cross-env ANALYZE=true yarn build:prod",
"preview": "next start",
"dev": "concurrently 'yarn dev:contentlayer' 'yarn dev:css' 'yarn dev:next'",
"dev": "concurrently 'yarn dev:css' 'yarn dev:next'",
"dev:next": "next dev",
"dev:css": "yarn generate:css --watch",
"dev:contentlayer": "contentlayer dev",
"generate:css": "unocss 'src/**/*.tsx' '../../packages/ui/src/lib/components/**/*.tsx' --out-file ./src/styles/unocss.css --config ../../unocss.config.ts",
"generate:css": "unocss 'src/**/*.tsx' 'contentlayer.config.ts' '../../packages/ui/src/lib/components/**/*.tsx' --out-file ./src/styles/unocss.css --config ../../unocss.config.ts",
"lint": "prettier --check . && cross-env TIMING=1 eslint src --ext .mjs,.js,.cjs,.ts,.tsx --format=pretty",
"format": "prettier --write . && cross-env TIMING=1 eslint src --ext .mjs,.js,.cjs,.ts,.tsx --fix --format=pretty",
"fmt": "yarn format"
@@ -37,64 +36,63 @@
],
"repository": {
"type": "git",
"url": "https://github.com/discordjs/discord.js.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",
"dependencies": {
"@code-hike/mdx": "^0.8.2",
"@code-hike/mdx": "^0.9.0",
"@discordjs/ui": "workspace:^",
"@react-icons/all-files": "^4.1.0",
"@vercel/analytics": "^0.1.11",
"@vercel/edge-config": "^0.1.7",
"@vercel/og": "^0.5.2",
"@vercel/analytics": "^1.0.2",
"@vercel/edge-config": "^0.2.1",
"@vercel/og": "^0.5.11",
"ariakit": "^2.0.0-next.44",
"cmdk": "^0.2.0",
"contentlayer": "^0.3.1",
"next": "^13.3.0",
"next-contentlayer": "^0.3.1",
"contentlayer": "0.3.1",
"next": "^13.4.17",
"next-contentlayer": "0.3.1",
"next-themes": "^0.2.1",
"react": "^18.2.0",
"react-custom-scrollbars-2": "^4.5.0",
"react-dom": "^18.2.0",
"react-use": "^17.4.0",
"rehype-autolink-headings": "^6.1.1",
"rehype-ignore": "^1.0.5",
"rehype-raw": "^6.1.1",
"rehype-slug": "^5.1.0",
"remark-gfm": "^3.0.1",
"server-only": "^0.0.1",
"sharp": "^0.32.0"
"sharp": "^0.32.5"
},
"devDependencies": {
"@next/bundle-analyzer": "^13.3.0",
"@next/bundle-analyzer": "^13.4.17",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/node": "18.15.11",
"@types/react": "^18.0.35",
"@types/react-dom": "^18.0.11",
"@unocss/cli": "^0.51.4",
"@unocss/eslint-config": "^0.51.4",
"@unocss/reset": "^0.51.4",
"@vitejs/plugin-react": "^3.1.0",
"@vitest/coverage-c8": "^0.30.1",
"concurrently": "^8.0.1",
"@types/html-escaper": "^3.0.0",
"@types/node": "18.17.5",
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
"@unocss/cli": "^0.55.1",
"@unocss/eslint-config": "^0.55.1",
"@unocss/reset": "^0.55.1",
"@vitejs/plugin-react": "^4.0.4",
"@vitest/coverage-v8": "^0.34.2",
"concurrently": "^8.2.0",
"cross-env": "^7.0.3",
"eslint": "^8.38.0",
"eslint-config-neon": "^0.1.42",
"eslint": "^8.47.0",
"eslint-config-neon": "^0.1.47",
"eslint-formatter-pretty": "^5.0.0",
"happy-dom": "^9.7.1",
"happy-dom": "^10.10.2",
"hast-util-to-string": "^2.0.0",
"hastscript": "^7.2.0",
"html-escaper": "^3.0.3",
"lighthouse": "^10.1.1",
"prettier": "^2.8.7",
"typescript": "^5.0.4",
"unocss": "^0.51.4",
"vercel": "^28.18.5",
"vitest": "^0.29.8"
"lighthouse": "^10.4.0",
"prettier": "^2.8.8",
"turbo": "^1.10.12",
"typescript": "^5.1.6",
"unocss": "^0.55.1",
"vercel": "^31.4.0",
"vitest": "^0.34.2"
},
"engines": {
"node": ">=18.13.0"

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

@@ -3,7 +3,7 @@
import { Providers } from './providers';
import { inter } from '~/util/fonts';
export default function GlobalError({ error }: { error: Error }) {
export default function GlobalError({ error }: { readonly error: Error }) {
console.error(error);
return (

View File

@@ -1,6 +1,6 @@
'use client';
export default function Error({ error }: { error: Error }) {
export default function Error({ error }: { readonly error: Error }) {
console.error(error);
return (

View File

@@ -1,23 +0,0 @@
'use client';
import { Providers } from './providers';
import { inter } from '~/util/fonts';
export default function GlobalError({ error }: { 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

@@ -6,7 +6,7 @@ export async function generateStaticParams() {
return allContents.map((content) => ({ slug: [content.slug] }));
}
export default function Page({ params }: { params: { slug: string[] } }) {
export default function Page({ params }: { readonly params: { slug: string[] } }) {
const content = allContents.find((content) => content.slug === params.slug?.join('/'));
if (!content) {
@@ -14,7 +14,7 @@ export default function Page({ params }: { params: { slug: string[] } }) {
}
return (
<article className="max-w-none prose">
<article className="max-w-none px-5 prose">
<Mdx code={content?.body.code ?? ''} />
</article>
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,91 @@
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,4 +1,5 @@
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 {
@@ -7,19 +8,19 @@ interface DocsLinkOptions {
*
* @remarks Functions automatically infer this.
*/
brackets?: boolean;
readonly brackets?: boolean;
/**
* The package.
*
* @defaultValue `'discord.js'`
*/
package?: (typeof PACKAGES)[number];
readonly package?: (typeof PACKAGES)[number];
/**
* The initial documentation class, function, interface etc.
*
* @example `'Client'`
*/
parent: string;
readonly parent?: string;
/**
* Whether to reference a static property.
*
@@ -27,20 +28,20 @@ interface DocsLinkOptions {
* This should only be used for the https://discord.js.org domain
* as static properties are not identified in the URL.
*/
static?: boolean;
readonly static?: boolean;
/**
* The symbol belonging to the parent.
*
* @example '`login'`
*/
symbol?: string;
readonly symbol?: string;
/**
* The type of the {@link DocsLinkOptions.parent}.
*
* @example `'class'`
* @example `'Function'`
*/
type: string;
readonly type?: string;
}
export function DocsLink({
@@ -50,29 +51,35 @@ export function DocsLink({
symbol,
brackets,
static: staticReference,
}: DocsLinkOptions) {
const bracketText = brackets || type.toUpperCase() === 'FUNCTION' ? '()' : '';
const trimmedSymbol = symbol;
let url;
let text;
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 (docs === PACKAGES[0]) {
url = `${BASE_URL_LEGACY}/${VERSION}/${type}/${parent}`;
if (trimmedSymbol) url += `?scrollTo=${trimmedSymbol}`;
// If there is a type and parent, we need to do some parsing.
if (type && parent) {
const bracketText = brackets || type?.toUpperCase() === 'FUNCTION' ? '()' : '';
text = `${parent}${trimmedSymbol ? (trimmedSymbol.startsWith('s-') ? '.' : '#') : ''}${
// eslint-disable-next-line prefer-named-capture-group
trimmedSymbol ? `${trimmedSymbol.replace(/(e|s)-/, '')}` : ''
}${bracketText}`;
} else {
url = `${BASE_URL}/${docs}/stable/${parent}:${type}`;
if (trimmedSymbol) url += `#${trimmedSymbol}`;
text = `${parent}${trimmedSymbol ? `${staticReference ? '.' : '#'}${trimmedSymbol}` : ''}${bracketText}`;
// 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">
{text}
{children ?? text}
<FiExternalLink size={18} />
</a>
);

View File

@@ -1,11 +1,12 @@
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="mx-auto max-w-6xl flex flex-col place-items-center gap-12 pt-12 lg:place-content-center">
<div className="w-full flex flex-col place-content-between place-items-center gap-12 md:flex-row md:gap-0">
<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"
@@ -22,56 +23,72 @@ export default function Footer() {
width={212}
/>
</a>
<div className="flex flex-row gap-6 md:gap-12">
<div className="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>
<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 className="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 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>

View File

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

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

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

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

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

View File

@@ -5,7 +5,7 @@ const LINK_HEIGHT = 30;
const INDICATOR_SIZE = 10;
const INDICATOR_OFFSET = (LINK_HEIGHT - INDICATOR_SIZE) / 2;
export function Outline({ headings }: { headings: any[] }) {
export function Outline({ headings }: { readonly headings: any[] }) {
// eslint-disable-next-line react/hook-use-state
const [active /* setActive */] = useState(0);

View File

@@ -1,4 +1,12 @@
export function PageButton({ url, title, direction }: { direction: 'next' | 'prev'; title: string; url: string }) {
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"

View File

@@ -34,7 +34,7 @@ export function Sidebar() {
<div className="flex flex-col gap-3 p-3">
{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"
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}
>

View File

@@ -136,6 +136,27 @@ Whenever some text does not need to be in the main body, you can put it here.
>
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

View File

@@ -1,231 +0,0 @@
---
title: Initial files
category: Creating your bot
---
# Initial files
Once you [add your bot to a server](preparations/adding-your-bot-to-servers.md), the next step is to start coding and get it online! Let's start by initializing your package.json, creating a config file for your client token, and a main file for your bot application.
## Creating package.json
This command creates a _`package.json`_ file for you, which will keep track of the dependencies your project uses, as well as other information.
<CH.Code>
```sh npm
npm init -y; npm pkg set type="module"
```
```sh yarn
yarn add dotenv
# You must go into your package.json file and add "type": "module"
```
```sh pnpm
pnpm init; pnpm pkg set type="module"
```
</CH.Code>
Once you're done with that, onto the next step!
## Using config.json
As explained in the ["What is a token, anyway?"](preparations/setting-up-a-bot-application.md#what-is-a-token-anyway) section, your token is essentially your bot's password, and you should protect it as best as possible. This can be done through a _`config.json`_ file or by using environment variables.
Open your application in the [Discord Developer Portal](https://discord.com/developers/applications) and go to the "Bot" page to copy your token.
Storing data in a _`config.json`_ file is a common way of keeping your sensitive values safe. Create a _`config.json`_ file in your project directory and paste in your token.
<CH.Code>
```json config.json
{
"token": "your-token-goes-here"
}
```
</CH.Code>
You can then access your token inside other files by using _`import`_.
<CH.Code>
```ts
import config from './config.json' assert { type: 'json' };
console.log(config.token);
```
</CH.Code>
<Alert title="Caution" type="danger">
If you're using Git, you should not commit this file and should [ignore it via
_`.gitignore`_](/creating-your-bot/#git-and-gitignore).
</Alert>
### Using environment variables
Environment variables are special values for your environment (e.g., terminal session, Docker container, or environment variable file). You can pass these values into your code's scope so that you can use them.
One way to pass in environment variables is via the command line interface. When starting your app, instead of _`node index.js`_, use _`TOKEN=your-token-goes-here node index.js`_. You can repeat this pattern to expose other values as well.
You can access the set values in your code via the _`process.env`_ global variable, accessible in any file. Note that values passed this way will always be strings and that you might need to parse them to a number, if using them to do calculations.
<CH.Code>
```shellscript Command line
A=123 B=456 DISCORD_TOKEN=your-token-goes-here node index.js
```
---
```js Usage
console.log(process.env.A);
console.log(process.env.B);
console.log(process.env.DISCORD_TOKEN);
```
</CH.Code>
#### Using dotenv
Another common approach is storing these values in a _`.env`_ file. This spares you from always copying your token into the command line. Each line in a _`.env`_ file should hold a _`KEY=value`_ pair.
You can use the [_`dotenv`_ package](https://www.npmjs.com/package/dotenv) for this. Once installed, preload the package to load your _`.env`_ file and attach the variables to _`process.env`_:
##### Installing dotenv
<CH.Code>
```sh npm
npm install dotenv
```
```sh yarn
yarn add dotenv
```
```sh pnpm
pnpm add dotenv
```
</CH.Code>
##### Defining your variables
<CH.Code>
```text .env
A=123
B=456
DISCORD_TOKEN=your-token-goes-here
```
</CH.Code>
<Alert title="Caution" type="danger">
If you're using Git, you should not commit this file and should [ignore it via
_`.gitignore`_](/creating-your-bot/#git-and-gitignore).
</Alert>
##### Utilizing your variables
<CH.Code>
```sh node
node --require dotenv/config yourFile.js
```
```sh yarn
yarn node --require dotenv/config yourFile.js
```
---
```ts yourFile
console.log(process.env.A); // 123
console.log(process.env.B); // 456
console.log(process.env.DISCORD_TOKEN); // your-token-goes-here
```
</CH.Code>
<Section title="Online editors (Glitch, Heroku, Replit, etc.)" defaultClosed padded background gutter>
While we generally do not recommend using online editors as hosting solutions, but rather invest in a proper virtual private server, these services do offer ways to keep your credentials safe as well! Please see the respective service's documentation and help articles for more information on how to keep sensitive values safe:
- Glitch: [Storing secrets in .env](https://glitch.happyfox.com/kb/article/18)
- Heroku: [Configuration variables](https://devcenter.heroku.com/articles/config-vars)
- Replit: [Secrets and environment variables](https://docs.replit.com/repls/secrets-environment-variables)
</Section>
### Git and .gitignore
Git is a fantastic tool to keep track of your code changes and allows you to upload progress to services like [GitHub](https://github.com/), [GitLab](https://about.gitlab.com/), or [Bitbucket](https://bitbucket.org/product). While this is super useful to share code with other developers, it also bears the risk of uploading your configuration files with sensitive values!
You can specify files that Git should ignore in its versioning systems with a _`.gitignore`_ file. Create a _`.gitignore`_ file in your project directory and add the names of the files and folders you want to ignore:
<CH.Code>
```
node_modules
.env
config.json
```
</CH.Code>
<Alert title="Tip" type="success">
Aside from keeping credentials safe, _`node_modules`_ should be included here. Since this directory can be restored
based on the entries in your _`package.json`_ and _`package-lock.json`_ files by running _`npm install`_, it does not
need to be included in Git. You can specify quite intricate patterns in _`.gitignore`_ files, check out the [Git
documentation on _`.gitignore`_](https://git-scm.com/docs/gitignore) for more information!
</Alert>
## Creating the main file
Open your code editor and create a new file. We suggest that you save the file as _`index.ts`_, or _`index.js`_, depending on whether you use TypeScript. You may name it whatever you wish, however.
Here's the base code to get you started:
<CH.Code>
```ts index.ts
// Import the necessary structures.
import { Client, Events, GatewayIntentBits } from 'discord.js';
import config from './config.json';
// Create a new client instance.
const client = new Client({ intents: GatewayIntentBits.Guilds });
// When the client is ready, run this code (only once).
client.once(Events.ClientReady, () => {
console.log('Ready!');
});
// Log in to Discord with your client's token.
client.login(config.token);
```
</CH.Code>
This is how you create a client instance for your Discord bot and login to Discord. The _`GatewayIntentBits.Guilds`_ intents option is necessary for your client to work properly, as it ensures that the caches for guilds, channels and roles are populated and available for internal use.
Intents also define which events Discord should send to your bot, and you may wish to enable more than just the minimum. You can read more about the other intents on the [Intents topic](popular-topics/intents).
Open your terminal, compile your code (JavaScript users do not do this), and run _`node index.js`_ to start the process. If you see "Ready!" after a few seconds, you're good to go!
<Alert title="Tip" type="success">
You can open your _`package.json`_ file and edit the _`"main": "index.js"`_ field to point to your main file. You can
then run _`node .`_ in your terminal to start the process! After closing the process with <kbd>⌃ Control</kbd>{' '}
<kbd>C</kbd>, you can press <kbd>↑</kbd> on your keyboard to bring up the latest commands you've run. Pressing{' '}
<kbd>↑</kbd> then <kbd>⏎ Enter</kbd> after closing the process is a quick way to start it up again.
</Alert>
## Resulting code
<ResultingCode path="creating-your-bot/initial-files" />
Code is indeed a result of code. That being said, it's being worked on. With code. Definitely.

View File

@@ -1,246 +0,0 @@
---
title: Creating commands
category: Creating your bot
---
# Creating commands
<Alert title="Tip" type="success">
This page is a follow-up and bases its code on [the previous page](/creating-your-bot/).
</Alert>
<DiscordMessages rounded>
<DiscordMessage
interaction={{
author: {
avatar:
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
username: 'Crawl',
},
command: 'ping',
}}
author={{
avatar:
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
username: 'Crawl',
time: 'Today at 21:00',
}}
>
Pong!
</DiscordMessage>
</DiscordMessages>
Discord allows developers to register [slash commands](https://discord.com/developers/docs/interactions/application-commands), which provide users a first-class way of interacting directly with your application. Before being able to reply to a command, you must first register it.
## Registering commands
This section will cover only the bare minimum to get you started, but you can refer to our [in-depth page on registering slash commands](/interactions/slash-commands.md#registering-slash-commands) for further details. It covers guild commands, global commands, options, option types, and choices.
### Command deployment script
Create a _`deploy-commands.js`_ file in your project directory. This file will be used to register and update the slash commands for your bot application.
Since commands only need to be registered once, and updated when the definition (description, options etc) is changed, it's not necessary to connect a whole client to the gateway or do this on every _`ready`_ event. As such, a standalone script using the lighter REST manager is preferred.
Below is a deployment script you can use. Focus on these variables:
- _`clientId`_: Your application's client id
- _`guildId`_: Your development server's id
- _`commands`_: An array of commands to register. The [slash command builder](/popular-topics/builders.md#slash-command-builders) from _`discord.js`_ is used to build the data for your commands
<Alert title="Tip" type="success">
In order to get your application's client id, go to [Discord Developer
Portal](https://discord.com/developers/applications) and choose your application. Find the id under "Application ID"
in General Information subpage. To get guild id, open Discord and go to your settings. On the "Advanced" page, turn on
"Developer Mode". This will enable a "Copy ID" button in the context menu when you right-click on a server icon, a
user's profile, etc.
</Alert>
<CH.Code>
```js deploy-commands.js mark=4,6:10
const { REST, SlashCommandBuilder, Routes } = require('discord.js');
const { clientId, guildId, token } = require('./config.json');
const commands = [
new SlashCommandBuilder().setName('ping').setDescription('Replies with pong!'),
new SlashCommandBuilder().setName('server').setDescription('Replies with server info!'),
new SlashCommandBuilder().setName('user').setDescription('Replies with user info!'),
].map((command) => command.toJSON());
const rest = new REST({ version: '10' }).setToken(token);
rest
.put(Routes.applicationGuildCommands(clientId, guildId), { body: commands })
.then((data) => console.log(`Successfully registered ${data.length} application commands.`))
.catch(console.error);
```
---
```json config.json mark=2:3
{
"clientId": "123456789012345678",
"guildId": "876543210987654321",
"token": "your-token-goes-here"
}
```
</CH.Code>
Once you fill in these values, run _`node deploy-commands.js`_ in your project directory to register your commands to a single guild. It's also possible to [register commands globally](/interactions/slash-commands.md#global-commands).
<Alert title="Tip" type="success">
You only need to run `node deploy-commands.js` once. You should only run it again if you add or edit existing
commands.
</Alert>
## Replying to commands
Once you've registered your commands, you can listen for interactions via <DocsLink type="class" parent="Client" symbol="e-interactionCreate" /> in your _`index.js`_ file.
You should first check if an interaction is a chat input command via <DocsLink type="class" parent="BaseInteraction" symbol="isChatInputCommand" brackets>_`.isChatInputCommand()`_</DocsLink>, and then check the <DocsLink type="class" parent="CommandInteraction" symbol="commandName">_`.commandName`_</DocsLink> property to know which command it is. You can respond to interactions with <DocsLink type="class" parent="CommandInteraction" symbol="reply" brackets>_`.reply()`_</DocsLink>.
<CH.Code>
```js mark=5:16
client.once('ready', () => {
console.log('Ready!');
});
client.on('interactionCreate', async (interaction) => {
if (!interaction.isChatInputCommand()) return;
const { commandName } = interaction;
if (commandName === 'ping') {
await interaction.reply('Pong!');
} else if (commandName === 'server') {
await interaction.reply('Server info.');
} else if (commandName === 'user') {
await interaction.reply('User info.');
}
});
client.login(token);
```
</CH.Code>
### Server info command
Note that servers are referred to as "guilds" in the Discord API and discord.js library. _`interaction.guild`_ refers to the guild the interaction was sent in (a <DocsLink type="class" parent="Guild" /> instance), which exposes properties such as _`.name`_ or _`.memberCount`_.
<CH.Code>
```js focus=7
client.on('interactionCreate', async (interaction) => {
if (!interaction.isChatInputCommand()) return;
const { commandName } = interaction;
if (commandName === 'ping') {
await interaction.reply('Pong!');
} else if (commandName === 'server') {
await interaction.reply(`Server name: ${interaction.guild.name}\nTotal members: ${interaction.guild.memberCount}`);
} else if (commandName === 'user') {
await interaction.reply('User info.');
}
});
```
</CH.Code>
<DiscordMessages rounded>
<DiscordMessage
interaction={{
author: {
avatar:
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
username: 'Crawl',
},
command: 'server',
}}
author={{
avatar:
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
username: 'Crawl',
time: 'Today at 21:00',
}}
>
<p>Server name: discord.js Guide</p>
<p>Total members: 2</p>
</DiscordMessage>
</DiscordMessages>
You could also display the date the server was created, or the server's verification level. You would do those in the same manner use _`interaction.guild.createdAt`_ or _`interaction.guild.verificationLevel`_, respectively.
<Alert title="Tip" type="success">
Refer to the <DocsLink type="class" parent="Guild" /> documentation for a list of all the available properties and
methods!
</Alert>
### User info command
A "user" refers to a Discord user. _`interaction.user`_ refers to the user the interaction was sent by (a <DocsLink type="class" parent="User" /> instance), which exposes properties such as _`.tag`_ or _`.id`_.
<CH.Code>
```js focus=9
client.on('interactionCreate', async (interaction) => {
if (!interaction.isChatInputCommand()) return;
const { commandName } = interaction;
if (commandName === 'ping') {
await interaction.reply('Pong!');
} else if (commandName === 'server') {
await interaction.reply(`Server name: ${interaction.guild.name}\nTotal members: ${interaction.guild.memberCount}`);
} else if (commandName === 'user') {
await interaction.reply(`Your tag: ${interaction.user.tag}\nYour id: ${interaction.user.id}`);
}
});
```
</CH.Code>
<DiscordMessages rounded>
<DiscordMessage
interaction={{
author: {
avatar:
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
username: 'Crawl',
},
command: 'user',
}}
author={{
avatar:
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
username: 'Crawl',
time: 'Today at 21:00',
}}
>
<p>Your tag: User#0001</p>
<p>Your id: 123456789012345678</p>
</DiscordMessage>
</DiscordMessages>
<Alert title="Tip" type="success">
Refer to the <DocsLink type="class" parent="User" /> documentation for a list of all the available properties and
methods!
</Alert>
And there you have it!
## The problem with if/else if
If you don't plan on making more than a couple commands, then using an _`if`_/_`else if`_ chain is fine; however, this isn't always the case. Using a giant _`if`_/_`else if`_ chain will only hinder your development process in the long run.
Here's a small list of reasons why you shouldn't do so:
- Takes longer to find a piece of code you want;
- Easier to fall victim to [spaghetti code](https://en.wikipedia.org/wiki/Spaghetti_code);
- Difficult to maintain as it grows;
- Difficult to debug;
- Difficult to organize;
- General bad practice.
Next, we'll be diving into something called a "command handler" code that makes handling commands easier and much more efficient. This allows you to move your commands into individual files.
## Resulting code
<ResultingCode />

View File

@@ -0,0 +1,102 @@
---
title: Installing Node.js and discord.js
category: Installations and preparations
---
# Installing Node.js and discord.js
## Installing Node.js
To use discord.js, you'll need to install [Node.js](https://nodejs.org/). discord.js v14 requires Node v16.11.0 or higher.
<Alert title="Tip" type="success">
To check if you already have Node installed on your machine \(e.g., if you're using a VPS\), run _`node -v`_ in your
terminal. If it outputs _`v16.11.0`_ or higher, then you're good to go! Otherwise, continue reading.
</Alert>
On Windows, it's as simple as installing any other program. Download the latest version from [the Node.js website](https://nodejs.org/), open the downloaded file, and follow the steps from the installer.
On macOS, either:
- Download the latest version from [the Node.js website](https://nodejs.org/), open the package installer, and follow the instructions
- Use a package manager like [Homebrew](https://brew.sh/) with the command _`brew install node`_
On Linux, you can consult [this page](https://nodejs.org/en/download/package-manager/) to determine how you should install Node. Native package managers often default to outdated versions of Node, so make sure you follow the recommended approach for your chosen Linux distribution carefully.
## Preparing the essentials
To use discord.js, you'll need to install it via npm \(Node's package manager\). npm comes with every Node installation, so you don't have to worry about installing that. However, before you install anything, you should set up a new project folder.
Navigate to a suitable place on your machine and create a new folder named _`discord-bot`_ (or whatever you want). Next you'll need to open your terminal.
### Opening the terminal
<Alert title="Tip" type="success">
If you use [Visual Studio Code](https://code.visualstudio.com/), you can press <kbd>Ctrl + `</kbd> (backtick) to open
its integrated terminal.
</Alert>
On Windows, either:
- <kbd>Shift + Right-click</kbd> inside your project directory and choose the "Open command window here" option
- Press <kbd>Win + R</kbd> and run _`cmd.exe`_, and then _`cd`_ into your project directory
On macOS, either:
- Open Launchpad or Spotlight and search for "Terminal"
- In your "Applications" folder, under "Utilities", open the Terminal app
On Linux, you can quickly open the terminal with <kbd>Ctrl + Alt + T</kbd>.
With the terminal open, run the _`node -v`_ command to make sure you've successfully installed Node.js. If it outputs _`v16.11.0`_ or higher, great!
### Initiating a project folder
<CH.Code lineNumbers={false}>
```sh npm
npm init; npm pkg set type="module"
```
```sh yarn
yarn init
# You must go into your package.json file and add "type": "module"
```
```sh pnpm
pnpm init; pnpm pkg set type="module"
```
</CH.Code>
This is the next command you'll be running. This command creates a _`package.json`_ file for you, which will keep track of the dependencies your project uses, as well as other info.
This command will ask you a sequence of questionsyou should fill them out as you see fit. If you're not sure of something or want to skip it as a whole, leave it blank and press enter. Setting the package type as _`module`_ tells Node that you'll be writing this project using ESM \(ECMAScript modules\), supporting the latest JavaScript syntax and features.
Once you're done with that, you're ready to install discord.js!
## Installing discord.js
Now that you've installed Node.js and know how to open your console and run commands, you can finally install discord.js! Run the following command in your terminal:
<CH.Code lineNumbers={false}>
```sh npm
npm install discord.js
```
```sh yarn
yarn add discord.js
```
```sh pnpm
pnpm add discord.js
```
</CH.Code>
And that's it! With all the necessities installed, you're almost ready to start coding your bot.
## Installing a linter
While you are coding, it's possible to run into numerous syntax errors or code in an inconsistent style. You should [install a linter](./setting-up-a-linter) to ease these troubles. While code editors generally can point out syntax errors, linters coerce your code into a specific style as defined by the configuration. While this is not required, it is advised.

View File

@@ -0,0 +1,6 @@
---
title: Setting up a linter
category: Installations and preparations
---
TODO: Rewrite. Placeholder page for ordering.

View File

@@ -0,0 +1,61 @@
---
title: Setting up a bot application
category: Installations and preparations
---
# Setting up a bot application
## Creating your bot
Now that you've installed Node, discord.js, and hopefully a linter, you're almost ready to start coding! The next step you need to take is setting up an actual Discord bot application via Discord's website.
It's effortless to create one. The steps you need to take are as follows:
1. Open the [Discord developer portal](https://discord.com/developers/applications) and log into your account.
2. Click on the "New Application" button.
3. Enter a name and confirm the pop-up window by clicking the "Create" button.
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. 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. When we ask you to paste your bot's token somewhere, this is the value that you need to put in. If you happen to lose your bot's token at some point, you need to come back to this page and reset your bot's token again which will reveal the new token, invalidating all old ones.
### What is a token, anyway?
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!
In the [configuration files](../creating-your-bot/configuration-files) page of the guide, we cover how to safely store your bot's token in a configuration file.
<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

@@ -0,0 +1,52 @@
---
title: Adding your bot to servers
category: Installations and preparations
---
# Adding your bot to servers
After you [set up a bot application](./setting-up-a-bot-application), you'll notice that it's not in any servers yet. So how does that work?
Before you're able to see your bot in your own (or other) servers, you'll need to add it by creating and using a unique invite link using your bot application's client id.
## 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%20applications.commands
```
</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%20applications.commands`_ 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 "Require 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 [My Apps](https://discord.com/developers/applications/me) page under the "Applications" section, click on your bot application, and open the OAuth2 page.
In the sidebar, you'll find the OAuth2 URL generator. Select the _`bot`_ and _`applications.commands`_ options. 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 enter it in your browser. You should see something like this (with your bot's username and avatar):
![Bot Authorization page](/assets/bot-auth-page.png)
Choose the server you want to add it to and click "Authorize". Do note that you'll need the "Manage Server" permission on a server to add your bot there. This should then present you a nice confirmation message:
![Bot authorized](/assets/bot-authorized.png)
Congratulations! You've successfully added your bot to your Discord server. It should show up in your server's member list somewhat like this:
![Bot in server's member list](/assets/bot-in-memberlist.png)

View File

@@ -0,0 +1,132 @@
---
title: Configuration files
category: Creating your bot
---
# Configuration files
Once you [add your bot to a server](../installations-and-preparations/adding-your-bot-to-servers), the next step is to start coding and get it online! Let's start by creating a config file to prepare the necessary values your client will need.
As explained in the ["What is a token, anyway?"](../installations-and-preparations/setting-up-a-bot-application.md#what-is-a-token-anyway) section, your token is essentially your bot's password, and you should protect it as best as possible. This can be done through a _`config.json`_ file or by using environment variables.
Open your application in the [Discord Developer Portal](https://discord.com/developers/applications) and go to the "Bot" page to copy your token.
## Using config.json
Storing data in a _`config.json`_ file is a common way of keeping your sensitive values safe. Create a _`config.json`_ file in your project directory and paste in your token. You can access your token inside other files by importing this file.
<CH.Code lineNumbers={false}>
```json config.json
{
"token": "your-token-goes-here"
}
```
```js index.js
import config from './config.json' assert { type: 'json' };
console.log(config.token);
```
</CH.Code>
<Alert title="Danger" type="danger">
If you're using Git, you should not commit this file and should [ignore it via `.gitignore`](#git-and-gitignore).
</Alert>
## Using environment variables
Environment variables are special values for your environment (e.g., terminal session, Docker container, or environment variable file). You can pass these values into your code's scope so that you can use them.
One way to pass in environment variables is via the command line interface. When starting your app, instead of _`node index.js`_, use _`TOKEN=your-token-goes-here node index.js`_. You can repeat this pattern to expose other values as well.
You can access the set values in your code via the _`process.env`_ global variable, accessible in any file. Note that values passed this way will always be strings and that you might need to parse them to a number, if using them to do calculations.
<CH.Code lineNumbers={false} rows={3}>
```sh Shell
A=123 B=456 DISCORD_TOKEN=your-token-goes-here node index.js
```
```js index.js
console.log(process.env.A);
console.log(process.env.B);
console.log(process.env.DISCORD_TOKEN);
```
</CH.Code>
### Using dotenv
Another common approach is storing these values in a _`.env`_ file. This spares you from always copying your token into the command line. Each line in a _`.env`_ file should hold a _`KEY=value`_ pair.
You can use the [`dotenv` package](https://www.npmjs.com/package/dotenv) for this. Once installed, require and use the package to load your _`.env`_ file and attach the variables to _`process.env`_:
<CH.Code lineNumbers={false}>
```sh npm
npm install dotenv
```
```sh yarn
yarn add dotenv
```
```sh pnpm
pnpm add dotenv
```
</CH.Code>
<CH.Code lineNumbers={false} rows={7}>
```sh .env
A=123
B=456
DISCORD_TOKEN=your-token-goes-here
```
```js index.js
import { config } from 'dotenv';
config();
console.log(process.env.A);
console.log(process.env.B);
console.log(process.env.DISCORD_TOKEN);
```
</CH.Code>
<Alert title="Danger" type="danger">
If you're using Git, you should not commit this file and should [ignore it via `.gitignore`](#git-and-gitignore).
</Alert>
<Alert title="Online editors (Glitch, Heroku, Replit, etc.)" type="info">
While we generally do not recommend using online editors as hosting solutions, but rather invest in a proper virtual private server, these services do offer ways to keep your credentials safe as well! Please see the respective service's documentation and help articles for more information on how to keep sensitive values safe:
- Glitch: [Storing secrets in .env](https://glitch.happyfox.com/kb/article/18)
- Heroku: [Configuration variables](https://devcenter.heroku.com/articles/config-vars)
- Replit: [Secrets and environment variables](https://docs.replit.com/repls/secrets-environment-variables)
</Alert>
## Git and .gitignore
Git is a fantastic tool to keep track of your code changes and allows you to upload progress to services like [GitHub](https://github.com/), [GitLab](https://about.gitlab.com/), or [Bitbucket](https://bitbucket.org/product). While this is super useful to share code with other developers, it also bears the risk of uploading your configuration files with sensitive values!
You can specify files that Git should ignore in its versioning systems with a*`.gitignore`* file. Create a _`.gitignore`_ file in your project directory and add the names of the files and folders you want to ignore:
```
node_modules
.env
config.json
```
<Alert title="Tip" type="success">
Aside from keeping credentials safe, _`node_modules`_ should be included here. Since this directory can be restored based on the entries in your _`package.json`_ and _`package-lock.json`_ files by running _`npm install`_, it does not need to be included in Git.
You can specify quite intricate patterns in _`.gitignore`_ files, check out the [Git documentation on `.gitignore`](https://git-scm.com/docs/gitignore) for more information!
</Alert>

View File

@@ -0,0 +1,60 @@
---
title: Creating the main file
category: Creating your bot
---
# Creating the main file
<Alert title="Tip" type="success">
This page assumes you've already prepared the [configuration files](./configuration-files) from the previous page.
We're using the _`config.json`_ approach, however feel free to substitute your own!
</Alert>
Open your code editor and create a new file. We suggest that you save the file as _`index.js`_, but you may name it whatever you wish.
Here's the base code to get you started:
<CH.Code>
```js
// Require the necessary discord.js classes
import { Client, Events, GatewayIntentBits } from 'discord.js';
import config from './config.json' assert { type: 'json' };
// Create a new client instance
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
// When the client is ready, run this code (only once)
// We use 'c' for the event parameter to keep it separate from the already defined 'client'
client.once(Events.ClientReady, (c) => {
console.log(`Ready! Logged in as ${c.user.tag}`);
});
// Log in to Discord with your client's token
client.login(config.token);
```
</CH.Code>
This is how you create a client instance for your Discord bot and log in to Discord. The _`GatewayIntentBits.Guilds`_ intents option is necessary for the discord.js client to work as you expect it to, as it ensures that the caches for guilds, channels, and roles are populated and available for internal use.
<Alert title="Tip" type="success">
The term "guild" is used by the Discord API and in discord.js to refer to a Discord server.
</Alert>
Intents also define which events Discord should send to your bot, and you may wish to enable more than just the minimum. You can read more about the other intents on the [Intents topic](../popular-topics/intents).
## Running your application
Open your terminal and run _`node index.js`_ to start the process. If you see "Ready!" after a few seconds, you're good to go! The next step is to start adding [slash commands](./adding-commands) to develop your bot's functionality.
<Alert title="Tip" type="success">
You can open your _`package.json`_ file and edit the _`"main": "index.js"`_ field to point to your main file. You can then run _`node .`_ in your terminal to start the process!
After closing the process with _`Ctrl + C`_, you can press the up arrow on your keyboard to bring up the latest commands you've run. Pressing up and then enter after closing the process is a quick way to start it up again.
</Alert>
#### Resulting code
<ResultingCode path="creating-your-bot/initial-files" />

View File

@@ -0,0 +1,163 @@
---
title: Adding commands
category: Creating your bot
---
# Creating slash commands
Discord allows developers to register [slash commands](https://discord.com/developers/docs/interactions/application-commands), which provide users a first-class way of interacting directly with your application.
Slash commands provide a huge number of benefits over manual message parsing, including:
- Integration with the Discord client interface.
- Automatic command detection and parsing of the associated options/arguments.
- Typed argument inputs for command options, e.g. "String", "User", or "Role".
- Validated or dynamic choices for command options.
- In-channel private responses (ephemeral messages).
- Pop-up form-style inputs for capturing additional information.
...and many more!
<Alert title="Read first!" type="info">
For fully functional slash commands, there are three important pieces of code that need to be written. They are:
1. The individual command files, containing their definitions and functionality.
2. The [command handler](command-handling.html), which dynamically reads the files and executes the commands.
3. The [command deployment script](command-deployment.html), to register your slash commands with Discord so they appear in the interface.
These steps can be done in any order, but **all are required** before the commands are fully functional.
On this page, you'll complete Step 1. Make sure to also complete the other pages linked above!
</Alert>
## Before you continue
Assuming you've followed the guide so far, your project directory should look something like this:
```:no-line-numbers
discord-bot/
├── node_modules
├── config.json
├── index.js
├── package-lock.json
└── package.json
```
## Individual command files
Create a new folder named _`commands`_, which is where you'll store all of your command files.
At a minimum, the definition of a slash command must have a name and a description. Slash command names must be between 1-32 characters and contain no capital letters, spaces, or symbols other than _`-`_ and _`_`_. Using the builder, a simple _`ping`\_ command definition would look like this:
<CH.Code>
```js
/** @type {import('discord.js').RESTPostAPIApplicationCommandsJSONBody} */
export const data = {
name: 'ping',
description: 'Replies with Pong!',
};
```
</CH.Code>
A slash command also requires a function to run when the command is used, to respond to the interaction. Using an interaction response method confirms to Discord that your bot successfully received the interaction, and has responded to the user. Discord enforces this to ensure that all slash commands provide a good user experience (UX). Failing to respond will cause Discord to show that the command failed, even if your bot is performing other actions as a result.
The simplest way to acknowledge and respond to an interaction is the _`interaction.reply()`_ method. Other methods of replying are covered on the [Response methods](../slash-commands/response-methods) page later in this section.
<CH.Code>
```js
/** @param {import('discord.js').CommandInteraction} interaction */
export async function execute(interaction) {
await interaction.reply('Pong!');
}
```
</CH.Code>
<Alert title="@type and @param tags" type="info">
[`@type`](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#type) and
[`@param`](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#param-and-returns) tags allow you
to annotate your code with type information. The tags are not required for your code to run but provide autocomplete
and type information for fields and parameters, which can majorly improve your developer experience when working with
them.
</Alert>
Put these two together by creating a `commands/ping.js` file for your first command. Inside this file, you're going to define and export two items.
- The `data` property, which will provide the command definition shown above for registering to Discord.
- The `execute` method, which will contain the functionality to run from our event handler when the command is used.
The _`export`_ keyword ensures these values can be imported and read by other files; namely the command loader and command deployment scripts mentioned earlier.
<CH.Code>
```js commands/ping.js
/** @type {import('discord.js').RESTPostAPIApplicationCommandsJSONBody} */
export const data = {
name: 'ping',
description: 'Replies with Pong!',
};
/** @param {import('discord.js').CommandInteraction} interaction */
export async function execute(interaction) {
await interaction.reply('Pong!');
}
```
</CH.Code>
<Alert title="Tip" type="success">
[`module.exports`](https://nodejs.org/api/modules.html#modules_module_exports) is how you export data in Node.js so that you can [`require()`](https://nodejs.org/api/modules.html#modules_require_id) it in other files.
If you need to access your client instance from inside a command file, you can access it via `interaction.client`. If you need to access external files, packages, etc., you should `require()` them at the top of the file.
</Alert>
That's it for your basic ping command. Below are examples of two more commands we're going to build upon throughout the guide, so create two more files for these before you continue reading.
<CH.Code>
```js commands/user.js
/** @type {import('discord.js').RESTPostAPIApplicationCommandsJSONBody} */
export const data = {
name: 'user',
description: 'Provides information about the user.',
};
/** @param {import('discord.js').CommandInteraction} interaction */
export async function execute(interaction) {
// interaction.user is the object representing the User who ran the command
// interaction.member is the GuildMember object, which represents the user in the specific guild
await interaction.reply(
`This command was run by ${interaction.user.username}, who joined on ${interaction.member.joinedAt}.`,
);
}
```
```js commands/server.js
/** @type {import('discord.js').RESTPostAPIApplicationCommandsJSONBody} */
export const data = {
name: 'server',
description: 'Provides information about the server.',
};
/** @param {import('discord.js').CommandInteraction} interaction */
export async function execute(interaction) {
// interaction.guild is the object representing the Guild in which the command was run
await interaction.reply(`This server is ${interaction.guild.name} and has
${interaction.guild.memberCount} members.`);
}
```
</CH.Code>
#### Next steps
You can implement additional commands by creating additional files in the _`commands`_ folder, but these three are the ones we're going to use for the examples as we go on. For now let's move on to the code you'll need for command handling, to load the files and respond to incoming interactions.
#### Resulting code
<ResultingCode />

View File

@@ -0,0 +1,297 @@
---
title: Handling command interactions
category: Creating your bot
---
# Command handling
Unless your bot project is small, it's not a very good idea to have a single file with a giant _`if`_/_`else if`_ chain for commands. If you want to implement features into your bot and make your development process a lot less painful, you'll want to implement a command handler. Let's get started on that!
<Alert title="Read first!" type="info">
For fully functional slash commands, there are three important pieces of code that need to be written. They are:
1. The [individual command files](slash-commands), containing their definitions and functionality.
2. The command handler, which dynamically reads the files and executes the commands.
3. The [command deployment script](command-deployment), to register your slash commands with Discord so they appear in the interface.
These steps can be done in any order, but **all are required** before the commands are fully functional.
This page details how to complete **Step 2**. Make sure to also complete the other pages linked above!
</Alert>
## Loading command files
Now that your command files have been created, your bot needs to load these files on startup.
In your _`index.js`_ file, make these additions to the base template:
<CH.Code>
```js JavaScript mark=1:4,9
import { readdir } from 'node:fs/promises';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { Client, Collection, Events, GatewayIntentBits } from 'discord.js';
import config from './config.json' assert { type: 'json' };
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
const commands = new Collection();
client.once(Events.ClientReady, () => {
console.log('Ready!');
});
```
```ts TypeScript mark=1:11,16:21
import { readdir } from 'node:fs/promises';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import {
Client,
Collection,
Events,
GatewayIntentBits,
type RESTPostAPIChatInputApplicationCommandsJSONBody,
type ChatInputCommandInteraction,
} from 'discord.js';
import config from './config.json';
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
interface CommandModule {
data: RESTPostAPIChatInputApplicationCommandsJSONBody;
execute(interaction: ChatInputCommandInteraction): Promise<void>;
}
const commands = new Collection<string, CommandModule>();
client.once(Events.ClientReady, () => {
console.log('Ready!');
});
```
</CH.Code>
<Alert title="Tip" type="info">
- The [`fs`](https://nodejs.org/api/fs.html) module is Node's native file system module. _`readdir`_ is used to read
the _`commands`_ directory and identify our command files. - The [`path`](https://nodejs.org/api/path.html) module is
Node's native path utility module. _`join`_ helps construct paths to access files and directories. One of the
advantages of _`path.join`_ is that it automatically detects the operating system and uses the appropriate joiners. -
The [`url`](https://nodejs.org/api/url.html) module provides utilities for URL resolution and parsing.
_`fileURLToPath`_ ensuring a cross-platform valid absolute path string.
- The{' '}
<DocsLink type="class" parent="Collection" /> class extends JavaScript's native
[_`Map`_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) class, and includes
more extensive, useful functionality. _`Collection`_ is used to store and efficiently retrieve commands for execution.
</Alert>
Next, using the modules imported above, dynamically retrieve your command files with a few more additions to the _`index.js`_ file:
<CH.Code>
```js JavaScript focus=3:15
const commands = new Collection();
const commandsPath = fileURLToPath(new URL('commands', import.meta.url));
const commandFiles = await readdir(commandsPath).then((files) => files.filter((file) => file.endsWith('.js')));
for (const file of commandFiles) {
const filePath = join(commandsPath, file);
const command = await import(filePath);
// Set a new item in the Collection with the key as the command name and the value as the exported module
if ('data' in command && 'execute' in command) {
commands.set(command.data.name, command);
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
```
```ts TypeScript focus=3:15
const commands = new Collection<string, CommandModule>();
const commandsPath = fileURLToPath(new URL('commands', import.meta.url));
const commandFiles = await readdir(commandsPath).then((files) => files.filter((file) => file.endsWith('.js')));
for (const file of commandFiles) {
const filePath = join(commandsPath, file);
const command = await import(filePath);
// Set a new item in the Collection with the key as the command name and the value as the exported module
if ('data' in command && 'execute' in command) {
commands.set(command.data.name, command);
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
```
</CH.Code>
First, [url.fileURLToPath()](https://nodejs.org/api/url.html) helps to construct a path to the _`commands`_ directory. The [fs.readdir()](https://nodejs.org/api/fs.html#fspromisesreaddirpath-options) method then reads the path to the directory and returns a Promise which resolves to an array of all the file names it contains, currently _`['ping.js', 'server.js', 'user.js']`_. To ensure only command files get processed, _`Array.filter()`_ removes any non-JavaScript files from the array.
With the correct files identified, the last step is to loop over the array and dynamically set each command into the _`commands`_ Collection. For each file being loaded, check that it has at least the _`data`_ and _`execute`_ properties. This helps to prevent errors resulting from loading empty, unfinished or otherwise incorrect command files while you're still developing.
## Receiving command interactions
Every slash command is an _`interaction`_, so to respond to a command, you need to create a listener for the <DocsLink type="class" parent="Client" symbol="e-interactionCreate" /> event that will execute code when your application receives an interaction. Place the code below in the _`index.js`_ file you created earlier.
<CH.Code>
```js
client.on(Events.InteractionCreate, (interaction) => {
console.log(interaction);
});
```
</CH.Code>
Not every interaction is a slash command (e.g. _`MessageComponent`_ interactions). Make sure to only handle slash commands in this function by making use of the <DocsLink type="class" parent="BaseInteraction" symbol="isChatInputCommand" brackets /> method to exit the handler if another type is encountered. This method also provides type guarding for TypeScript users, narrowing the type from _`BaseInteraction`_ to <DocsLink type="class" parent="ChatInputCommandInteraction" />.
<CH.Code>
```js focus=2
client.on(Events.InteractionCreate, (interaction) => {
if (!interaction.isChatInputCommand()) return;
console.log(interaction);
});
```
</CH.Code>
## Executing commands
When your bot receives a <DocsLink type="class" parent="Client" symbol="e-interactionCreate" /> event, the interaction object contains all the information you need to dynamically retrieve and execute your commands!
Let's take a look at the _`ping`_ command again. Note the _`execute()`_ function that will reply to the interaction with "Pong!".
<CH.Code>
```js
export const data = {
name: 'ping',
description: 'Replies with Pong!',
};
export async function execute(interaction) {
await interaction.reply('Pong!');
}
```
</CH.Code>
First, you need to get the matching command from the _`commands`_ Collection based on the _`interaction.commandName`_. If no matching command is found, log an error to the console and ignore the event.
With the right command identified, all that's left to do is call the command's _`.execute()`_ method and pass in the _`interaction`_ variable as its argument. Note that the event listener has been made _`async`_, allowing Promises to be awaited. In case something goes wrong and the Promise rejects, catch and log any error to the console.
<CH.Code>
```js focus=4:20
// focus[37:42]
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isChatInputCommand()) return;
const command = commands.get(interaction.commandName);
if (!command) {
console.error(`No command matching ${interaction.commandName} was found.`);
return;
}
try {
await command.execute(interaction);
} catch (error) {
console.error(error);
if (interaction.replied || interaction.deferred) {
await interaction.followUp({ content: 'There was an error while executing this command!', ephemeral: true });
} else {
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
}
}
});
```
</CH.Code>
## Command categories
So far, all of your command files are in a single _`commands`_ folder. This is fine at first, but as your project grows, the number of files in the _`commands`_ folder will too. Keeping track of that many files can be a little tough. To make this a little easier, you can categorize your commands and put them in subfolders inside the _`commands`_ folder. You will have to make a few changes to your existing code in _`index.js`_ for this to work out.
If you've been following along, your project structure should look something like this:
![Project structure before sorting](/assets/before-sorting.png)
After moving your commands into subfolders, it will look something like this:
![Project structure after sorting](/assets/after-sorting.png)
<Alert title="Warning" type="warning">
Make sure you put every command file you have inside one of the new subfolders. Leaving a command file directly under
the _`commands`_ folder will create problems.
</Alert>
It is not necessary to name your subfolders exactly like we have named them here. You can create any number of subfolders and name them whatever you want. Although, it is a good practice to name them according to the type of commands stored inside them.
Back in your _`index.js`_ file, where the code to [dynamically read command files](#loading-command-files) is, use the same pattern to read the subfolder directories, and then require each command inside them.
<CH.Code>
```js JavaScript focus=3:7,19
const commands = new Collection();
const foldersPath = fileURLToPath(new URL('commands', import.meta.url));
const commandFolders = await readdir(foldersPath);
for (const folder of commandFolders) {
const commandsPath = join(foldersPath, folder);
const commandFiles = await readdir(commandsPath).then((files) => files.filter((file) => file.endsWith('.js')));
for (const file of commandFiles) {
const filePath = join(commandsPath, file);
const command = await import(filePath);
// Set a new item in the Collection with the key as the command name and the value as the exported module
if ('data' in command && 'execute' in command) {
commands.set(command.data.name, command);
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}
```
```ts Typescript mark=3:7,19
const commands = new Collection<string, CommandModule>();
const foldersPath = fileURLToPath(new URL('commands', import.meta.url));
const commandFolders = await readdir(foldersPath);
for (const folder of commandFolders) {
const commandsPath = join(foldersPath, folder);
const commandFiles = await readdir(commandsPath).then((files) => files.filter((file) => file.endsWith('.js')));
for (const file of commandFiles) {
const filePath = join(commandsPath, file);
const command = await import(filePath);
// Set a new item in the Collection with the key as the command name and the value as the exported module
if ('data' in command && 'execute' in command) {
commands.set(command.data.name, command);
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}
```
</CH.Code>
That's it! When creating new files for commands, make sure you create them inside one of the subfolders (or a new one) in the _`commands`_ folder.
#### Next steps
Your command files are now loaded into your bot, and the event listener is prepared and ready to respond. In the next section, we cover the final step - a command deployment script you'll need to register your commands so they appear in the Discord client.
#### Resulting code
<ResultingCode />
It also includes some bonus commands!

View File

@@ -0,0 +1,155 @@
---
title: Registering slash commands
category: Creating your bot
---
# Registering slash commands
<Alert title="Read first!" type="info">
For fully functional slash commands, you need three important pieces of code:
1. The [individual command files](slash-commands), containing their definitions and functionality.
2. The [command handler](command-handling), which dynamically reads the files and executes the commands.
3. The command deployment script, to register your slash commands with Discord so they appear in the interface.
These steps can be done in any order, but **all are required** before the commands are fully functional.
This page details how to complete **Step 3**. Make sure to also complete the other pages linked above!
</Alert>
## Command registration
Slash commands can be registered in two ways; in one specific guild, or for every guild the bot is in. We're going to look at single-guild registration first, as this is a good way to develop and test your commands before a global deployment.
Your application will need the _`applications.commands`_ scope authorized in a guild for any of its slash commands to appear, and to be able to register them in a specific guild without error.
Slash commands only need to be registered once, and updated when the definition (description, options etc) is changed. As there is a daily limit on command creations, it's not necessary nor desirable to connect a whole client to the gateway or do this on every _`ClientReady`_ event. As such, a standalone script using the lighter REST manager is preferred.
This script is intended to be run separately, only when you need to make changes to your slash command **definitions** - you're free to modify parts such as the execute function as much as you like without redeployment.
### Guild commands
Create a _`deploy-commands.js`_ file in your project directory. This file will be used to register and update the slash commands for your bot application.
Add two more properties to your _`config.json`_ file, which we'll need in the deployment script:
- _`clientId`_: Your application's client id ([Discord Developer Portal](https://discord.com/developers/applications) > "General Information" > application id)
- _`guildId`_: Your development server's id ([Enable developer mode](https://support.discord.com/hc/en-us/articles/206346498) > Right-click the server title > "Copy Server ID")
<CH.Code lineNumbers={false}>
```json
{
"token": "your-token-goes-here",
"clientId": "your-application-id-goes-here",
"guildId": "your-server-id-goes-here"
}
```
</CH.Code>
With these defined, you can use the deployment script below:
<CH.Code>
```js deploy-commands.js
import { REST, Routes } from 'discord.js';
import { readdir } from 'node:fs/promises';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import config from './config.json' assert { type: 'json' };
const { clientId, guildId, token } = config;
const commands = [];
// Grab all the command files from the commands directory you created earlier
const foldersPath = fileURLToPath(new URL('commands', import.meta.url));
const commandFolders = await readdir(foldersPath);
for (const folder of commandFolders) {
// Grab all the command files from the commands directory you created earlier
const commandsPath = join(foldersPath, folder);
const commandFiles = await readdir(commandsPath).then((files) => files.filter((file) => file.endsWith('.js')));
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
for (const file of commandFiles) {
const filePath = join(commandsPath, file);
const command = await import(filePath);
if ('data' in command && 'execute' in command) {
commands.push(command.data.toJSON());
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}
// Construct and prepare an instance of the REST module
const rest = new REST().setToken(token);
try {
console.log(`Started refreshing ${commands.length} application (/) commands.`);
// The put method is used to fully refresh all commands in the guild with the current set
const data = await rest.put(Routes.applicationGuildCommands(clientId, guildId), { body: commands });
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
} catch (error) {
// And of course, make sure you catch and log any errors!
console.error(error);
}
```
</CH.Code>
Once you fill in these values, run _`node deploy-commands.js`_ in your project directory to register your commands to the guild specified. If you see the success message, check for the commands in the server by typing _`/`_! If all goes well, you should be able to run them and see your bot's response in Discord!
### Global commands
Global application commands will be available in all the guilds your application has the _`applications.commands`_ scope authorized in, and in direct messages by default.
To deploy global commands, you can use the same script from the [guild commands](#guild-commands) section and simply adjust the route in the script to _`.applicationCommands(clientId)`_
Test
<CH.Code rows="focus">
```js focus=5
try {
console.log(`Started refreshing ${commands.length} application (/) commands.`);
// The put method is used to fully refresh all commands in the guild with the current set
const data = await rest.put(Routes.applicationCommands(clientId), { body: commands });
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
} catch (error) {
// And of course, make sure you catch and log any errors!
console.error(error);
}
```
</CH.Code>
### Where to deploy
<Alert title="Tip" type="success">
Guild-based deployment of commands is best suited for development and testing in your own personal server. Once you're satisfied that it's ready, deploy the command globally to publish it to all guilds that your bot is in.
You may wish to have a separate application and token in the Discord Dev Portal for your dev application, to avoid duplication between your guild-based commands and the global deployment.
</Alert>
#### Further reading
You've successfully sent a response to a slash command! However, this is only the most basic of command event and response functionality. Much more is available to enhance the user experience including:
- applying this same dynamic, modular handling approach to events with an [Event handler](./event-handling).
- utilising the different [Response methods](../slash-commands/response-methods) that can be used for slash commands.
- expanding on these examples with additional validated option types in [Advanced command creation](../slash-commands/advanced-creation).
- adding formatted [Embeds](../popular-topics/embeds) to your responses.
- enhancing the command functionality with [Buttons](../interactions/buttons) and [Select Menus](../interactions/select-menus).
- prompting the user for more information with [Modals](../interactions/modals).
#### Resulting code
<ResultingCode path="creating-your-bot/command-deployment" />

View File

@@ -0,0 +1,219 @@
---
title: Event handling
category: Creating your bot
---
# Event handling
Node.js uses an event-driven architecture, making it possible to execute code when a specific event occurs. The discord.js library takes full advantage of this. You can visit the <DocsLink type="class" parent="Client" /> documentation to see the full list of events.
<Alert title="Tip" type="success">
This page assumes you've followed the guide up to this point, and created your _`index.js`_ and individual slash
commands according to those pages.
</Alert>
At this point, your `index.js` file has code for loading commands, and listeners for two events: `ClientReady` and `InteractionCreate`.
<CH.Code>
```js Commands
const commands = new Collection();
const foldersPath = fileURLToPath(new URL('commands', import.meta.url));
const commandFolders = await readdir(foldersPath);
for (const folder of commandFolders) {
const commandsPath = join(foldersPath, folder);
const commandFiles = await readdir(commandsPath).then((files) => files.filter((file) => file.endsWith('.js')));
for (const file of commandFiles) {
const filePath = join(commandsPath, file);
const command = await import(filePath);
// Set a new item in the Collection with the key as the command name and the value as the exported module
if ('data' in command && 'execute' in command) {
commands.set(command.data.name, command);
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}
```
```js ClientReady
client.once(Events.ClientReady, (c) => {
console.log(`Ready! Logged in as ${c.user.tag}`);
});
```
```js InteractionCreate
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isChatInputCommand()) return;
const command = commands.get(interaction.commandName);
if (!command) {
console.error(`No command matching ${interaction.commandName} was found.`);
return;
}
try {
await command.execute(interaction);
} catch (error) {
console.error(`Error executing ${interaction.commandName}`);
console.error(error);
}
});
```
</CH.Code>
Currently, all of this code is in the _`index.js`_ file. <DocsLink type="class" parent="Client" symbol="e-ready" /> emits once when the _`Client`_ becomes ready for use, and <DocsLink type="class" parent="Client" symbol="e-interactionCreate" /> emits whenever an interaction is received.
Moving the event listener code into individual files is simple, and we'll be taking a similar approach to the [command handler](./handling-command-interactions).
## Individual event files
Your project directory should look something like this:
```
discord-bot/
├── commands/
├── node_modules/
├── config.json
├── deploy-commands.js
├── index.js
├── package-lock.json
└── package.json
```
Create an _`events`_ folder in the same directory. You can then move the code from your event listeners in _`index.js`_ to separate files: _`events/ready.js`_ and _`events/interactionCreate.js`_. The _`InteractionCreate`_ event is responsible for command handling, so the command loading code will move here too.
<CH.Code>
```js events/interactionCreate.js
import { readdir } from 'node:fs/promises';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { Collection, Events } from 'discord.js';
const commands = new Collection();
const foldersPath = fileURLToPath(new URL('commands', import.meta.url));
const commandFolders = await readdir(foldersPath);
for (const folder of commandFolders) {
const commandsPath = join(foldersPath, folder);
const commandFiles = await readdir(commandsPath).then((files) => files.filter((file) => file.endsWith('.js')));
for (const file of commandFiles) {
const filePath = join(commandsPath, file);
const command = await import(filePath);
// Set a new item in the Collection with the key as the command name and the value as the exported module
if ('data' in command && 'execute' in command) {
commands.set(command.data.name, command);
} else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}
export const data = {
name: Events.InteractionCreate,
};
export async function execute(interaction) {
if (!interaction.isChatInputCommand()) return;
const command = commands.get(interaction.commandName);
if (!command) {
console.error(`No command matching ${interaction.commandName} was found.`);
return;
}
try {
await command.execute(interaction);
} catch (error) {
console.error(`Error executing ${interaction.commandName}`);
console.error(error);
}
}
```
```js events/ready.js
import { Events } from 'discord.js';
export const data = {
name: Events.ClientReady,
once = true,
};
export async function execute(client) {
console.log(`Ready! Logged in as ${client.user.tag}`);
}
```
```js index.js
import { readdir } from 'node:fs/promises';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { Client, GatewayIntentBits } from 'discord.js';
import config from './config.json' assert { type: 'json' };
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.login(config.token);
```
</CH.Code>
The _`name`_ property states which event this file is for, and the _`once`_ property holds a boolean value that specifies if the event should run only once. You don't need to specify this in _`interactionCreate.js`_ as the default behavior will be to run on every event instance. The _`execute`_ function holds your event logic, which will be called by the event handler whenever the event emits.
## Reading event files
Next, let's write the code for dynamically retrieving all the event files in the _`events`_ folder. We'll be taking a similar approach to our [command handler](./handling-command-interactions). Place the new code highlighted below in your _`index.js`_.
_`fs.readdir()`_ combined with _`array.filter()`_ returns an array of all the file names in the given directory and filters for only _`.js`_ files, i.e. _`['ready.js', 'interactionCreate.js']`_.
<CH.Code>
```js focus=9:20
import { readdir } from 'node:fs/promises';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { Client, GatewayIntentBits } from 'discord.js';
import config from './config.json' assert { type: 'json' };
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
const eventsPath = fileURLToPath(new URL('events', import.meta.url));
const eventFiles = await readdir(eventsPath).then((files) => files.filter((file) => file.endsWith('.js')));
for (const file of eventFiles) {
const filePath = join(eventsPath, file);
const event = await import(filePath);
if (event.data.once) {
client.once(event.data.name, (...args) => event.execute(...args));
} else {
client.on(event.data.name, (...args) => event.execute(...args));
}
}
client.login(config.token);
```
</CH.Code>
You'll notice the code looks very similar to the command loading above it - read the files in the events folder and load each one individually.
The <DocsLink type="class" parent="Client" /> class in discord.js extends the [`EventEmitter`](https://nodejs.org/api/events.html#events_class_eventemitter) class. Therefore, the _`client`_ object exposes the [`.on()`](https://nodejs.org/api/events.html#events_emitter_on_eventname_listener) and [`.once()`](https://nodejs.org/api/events.html#events_emitter_once_eventname_listener) methods that you can use to register event listeners. These methods take two arguments: the event name and a callback function. These are defined in your separate event files as _`name`_ and _`execute`_.
The callback function passed takes argument(s) returned by its respective event, collects them in an _`args`_ array using the _`...`_ [rest parameter syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters), then calls _`event.execute()`_ while passing in the _`args`_ array using the _`...`_ [spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax). They are used here because different events in discord.js have different numbers of arguments. The rest parameter collects these variable number of arguments into a single array, and the spread syntax then takes these elements and passes them to the _`execute`_ function.
After this, listening for other events is as easy as creating a new file in the _`events`_ folder. The event handler will automatically retrieve and register it whenever you restart your bot.
<Alert title="Tip" type="success">
In most cases, you can access your _`client`_ instance in other files by obtaining it from one of the other discord.js
structures, e.g. _`interaction.client`_ in the _`InteractionCreate`_ event. You do not need to manually pass it to
your events.
</Alert>
## Resulting code
<ResultingCode />

View File

@@ -0,0 +1,496 @@
---
title: Frequently asked questions
category: Popular 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

@@ -0,0 +1,165 @@
---
title: Audit logs
category: Popular 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

@@ -0,0 +1,223 @@
---
title: Collectors
category: Popular 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/en-US/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/en-US/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/en-US/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/en-US/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

@@ -0,0 +1,95 @@
---
title: Formatters
category: Popular 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

@@ -0,0 +1,202 @@
---
title: Threads
category: Popular 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

@@ -0,0 +1,227 @@
---
title: Webhooks
category: Popular 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

@@ -7,7 +7,7 @@ category: Additional info
## Before you start
v14 requires Node 16.9 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.
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
@@ -41,7 +41,7 @@ discord.js v14 makes the switch to Discord API v10!
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 [discord-api-types](https://discord-api-types.dev/api/discord-api-types-v10).
In addition, the old enums exported by discord.js v13 and lower are replaced with new enums from <DiscordAPITypesLink />.
#### New enum differences
@@ -121,7 +121,7 @@ Areas like _`Client`_ initialization, JSON slash commands and JSON message compo
#### Channels
Some channel type guard methods that narrowed to one channel type have been removed. Instead compare the _`type`_ property against a [ChannelType](https://discord-api-types.dev/api/discord-api-types-v10/enum/ChannelType) enum member to narrow 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>
@@ -361,7 +361,7 @@ _`IntegrationApplication#summary`_ has been removed as it is no longer supported
### Interaction
Whenever an interaction is replied to and one fetches the reply, it could possibly give an [APIMessage](https://discord-api-types.dev/api/discord-api-types-v10/interface/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`_.
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"/>.
@@ -419,7 +419,7 @@ The base interaction class is now <DocsLink type="class" parent="BaseInteraction
### MessageManager
The second paramter of <DocsLink type="class" parent="MessageManager" symbol="fetch" brackets /> has been removed. The <DocsLink type="class" parent="BaseFetchOptions" /> the second parameter once was is now merged into the first parameter.
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>
@@ -490,6 +490,8 @@ In addition to this, there is now a new partial: _`Partials.ThreadMember`_.
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.
@@ -647,6 +649,10 @@ The second parameter of <DocsLink type="class" parent="Webhook" symbol="fetchMes
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
@@ -684,7 +690,7 @@ Added support for role connection metadata.
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 [ComponentType](https://discord-api-types.dev/api/discord-api-types-v10/enum/ComponentType) enum values:
Component collector options now use the <DiscordAPITypesLink type="enum" parent="ComponentType" /> enum values:
<CH.Code>
@@ -777,6 +783,8 @@ Added <DocsLink type="class" parent="MessageReaction" symbol="react" brackets />
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.
@@ -797,9 +805,14 @@ 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

@@ -2,6 +2,8 @@ export const BASE_URL = 'https://discord.js.org/docs/packages' as const;
export const BASE_URL_LEGACY = 'https://old.discordjs.dev/#/docs/discord.js' as const;
export const BASE_URL_DISCORD_API_TYPES = 'https://discord-api-types.dev' as const;
export const DESCRIPTION = 'Imagine a guide... that explores the many possibilities for your discord.js bot.';
export const GITHUB_BASE_PAGES_PATH = 'https://github.com/discordjs/discord.js/tree/main/apps/guide/src/pages';
@@ -24,4 +26,14 @@ export const PACKAGES = [
/**
* The stable version of discord.js.
*/
export const VERSION = '14.9.0' as const;
export const VERSION = '14.11.0' as const;
/**
* The API version (for discord-api-types). This is prefixed with a "v".
*/
export const DISCORD_API_TYPES_VERSION = 'v10' as const;
/**
* The voice API version (for discord-api-types). This is prefixed with a "v".
*/
export const DISCORD_API_TYPES_VOICE_VERSION = 'v4' as const;

View File

@@ -19,15 +19,9 @@
"~/*": ["./src/*"],
"contentlayer/generated": ["./.contentlayer/generated"]
},
"strictNullChecks": true
"strictNullChecks": true,
"moduleResolution": "node"
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"next-env.d.ts",
"types.d.ts",
".next/types/**/*.ts",
".contentlayer/generated"
],
"include": ["src/**/*.ts", "src/**/*.tsx", "next-env.d.ts", ".next/types/**/*.ts", ".contentlayer/generated"],
"exclude": ["node_modules"]
}

View File

@@ -1 +0,0 @@
declare module '*.css';

View File

@@ -0,0 +1 @@
next-env.d.ts

View File

@@ -1,8 +1,8 @@
# Packages
node_modules/
node_modules
# Log files
logs/
logs
*.log
npm-debug.log*
@@ -16,19 +16,14 @@ pids
.env*.local
# Dist
dist/
typings/
.cache/
build/
.contentlayer
.next
public/searchIndex
src/assets/readme
src/styles/unocss.css
.next/
src/assets/readme/
static/
# Miscellaneous
.tmp/
coverage/
.vercel
public/searchIndex
.tmp
.vscode
lighthouse-results/
lighthouse-results

View File

@@ -1,16 +1,8 @@
# Autogenerated
CHANGELOG.md
.next
.turbo
dist/
docs/**/*
!docs/index.yml
!docs/README.md
coverage/
.cache
build/
src/styles/unocss.css
api/
.next/
.vercel/
.cache/
.vscode
coverage
public/searchIndex
src/assets/readme
src/styles/unocss.css
next-env.d.ts

View File

@@ -10,6 +10,7 @@
</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>

View File

@@ -7,15 +7,15 @@
"test": "vitest run",
"test:lighthouse": "lighthouse http://localhost:3000 --output-path=./lighthouse-results",
"build:copy_readme": "cpy '../../packages/*/README.md' 'src/assets/readme' --rename='home-{{basename}}'",
"build:local": "yarn run --top-level docs --force && yarn build:copy_readme && cross-env-shell NEXT_PUBLIC_LOCAL_DEV=true yarn build:prod",
"build:prod": "yarn workspaces foreach -ptR run build && yarn build:copy_readme && yarn build:css && yarn build:next",
"build:local": "cross-env NEXT_PUBLIC_LOCAL_DEV=true yarn build:prod",
"build:prod": "yarn build:copy_readme && yarn build:css && yarn build:next",
"build:next": "next build",
"build:css": "yarn generate:css",
"build:search_indices": "yarn node scripts/generateAllIndices.js",
"build:analyze": "yarn run --top-level docs --force && cross-env-shell ANALYZE=true NEXT_PUBLIC_LOCAL_DEV=true yarn build:prod",
"build:analyze": "turbo run docs && cross-env ANALYZE=true NEXT_PUBLIC_LOCAL_DEV=true yarn build:prod",
"preview": "next start",
"dev": "yarn run --top-level docs && concurrently 'yarn dev:css' 'yarn dev:next'",
"dev:next": "yarn workspaces foreach -ptR run build && next dev",
"dev": "concurrently 'yarn dev:css' 'yarn dev:next'",
"dev:next": "next dev",
"dev:css": "yarn generate:css --watch",
"generate:css": "unocss 'src/**/*.tsx' '../../packages/ui/src/lib/components/**/*.tsx' --out-file ./src/styles/unocss.css --config ../../unocss.config.ts",
"lint": "prettier --check . && cross-env TIMING=1 eslint src --ext .mjs,.js,.cjs,.ts,.tsx --format=pretty",
@@ -38,7 +38,8 @@
],
"repository": {
"type": "git",
"url": "https://github.com/discordjs/discord.js.git"
"url": "https://github.com/discordjs/discord.js.git",
"directory": "apps/website"
},
"bugs": {
"url": "https://github.com/discordjs/discord.js/issues"
@@ -48,58 +49,56 @@
"@discordjs/api-extractor-utils": "workspace:^",
"@discordjs/scripts": "workspace:^",
"@discordjs/ui": "workspace:^",
"@microsoft/api-extractor-model": "7.26.4",
"@microsoft/api-extractor-model": "7.27.6",
"@microsoft/tsdoc": "0.14.2",
"@planetscale/database": "1.7.0",
"@react-icons/all-files": "^4.1.0",
"@vercel/analytics": "^0.1.11",
"@vercel/edge-config": "^0.1.7",
"@vercel/og": "^0.5.2",
"@vscode/codicons": "^0.0.32",
"@vercel/analytics": "^1.0.2",
"@vercel/edge-config": "^0.2.1",
"@vercel/og": "^0.5.11",
"ariakit": "^2.0.0-next.44",
"bright": "^0.7.1",
"bright": "^0.8.4",
"class-variance-authority": "^0.7.0",
"cmdk": "^0.2.0",
"meilisearch": "^0.32.3",
"next": "^13.3.1-canary.8",
"meilisearch": "^0.34.1",
"next": "^13.4.17",
"next-mdx-remote": "^4.4.1",
"next-themes": "^0.2.1",
"react": "^18.2.0",
"react-custom-scrollbars-2": "^4.5.0",
"react-dom": "^18.2.0",
"react-use": "^17.4.0",
"rehype-ignore": "^1.0.5",
"rehype-raw": "^6.1.1",
"rehype-slug": "^5.1.0",
"remark-gfm": "^3.0.1",
"server-only": "^0.0.1",
"sharp": "^0.32.0",
"swr": "^2.1.3"
"sharp": "^0.32.5",
"swr": "^2.2.1"
},
"devDependencies": {
"@next/bundle-analyzer": "^13.3.0",
"@next/bundle-analyzer": "^13.4.17",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/node": "18.15.11",
"@types/react": "^18.0.35",
"@types/react-dom": "^18.0.11",
"@unocss/cli": "^0.51.4",
"@unocss/eslint-config": "^0.51.4",
"@unocss/reset": "^0.51.4",
"@vitejs/plugin-react": "^3.1.0",
"@vitest/coverage-c8": "^0.30.1",
"concurrently": "^8.0.1",
"cpy-cli": "^4.2.0",
"@types/node": "18.17.5",
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
"@unocss/cli": "^0.55.1",
"@unocss/eslint-config": "^0.55.1",
"@unocss/reset": "^0.55.1",
"@vitejs/plugin-react": "^4.0.4",
"@vitest/coverage-v8": "^0.34.2",
"concurrently": "^8.2.0",
"cpy-cli": "^5.0.0",
"cross-env": "^7.0.3",
"eslint": "^8.38.0",
"eslint-config-neon": "^0.1.42",
"eslint": "^8.47.0",
"eslint-config-neon": "^0.1.47",
"eslint-formatter-pretty": "^5.0.0",
"happy-dom": "^9.7.1",
"lighthouse": "^10.1.1",
"prettier": "^2.8.7",
"typescript": "^5.0.4",
"unocss": "^0.51.4",
"vercel": "^28.18.5",
"vitest": "^0.29.8"
"happy-dom": "^10.10.2",
"lighthouse": "^10.4.0",
"prettier": "^2.8.8",
"turbo": "^1.10.12",
"typescript": "^5.1.6",
"vercel": "^31.4.0",
"vitest": "^0.34.2"
},
"engines": {
"node": ">=18.13.0"

View File

@@ -0,0 +1,94 @@
'use client';
import { Analytics } from '@vercel/analytics/react';
import type { Metadata } from 'next';
import { Providers } from './providers';
import { DESCRIPTION } from '~/util/constants';
import { inter } from '~/util/fonts';
import '@unocss/reset/tailwind-compat.css';
import '~/styles/unocss.css';
import '~/styles/cmdk.css';
import '~/styles/main.css';
export const metadata: Metadata = {
title: 'discord.js',
description: DESCRIPTION,
viewport: {
minimumScale: 1,
initialScale: 1,
width: 'device-width',
},
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',
themeColor: [
{ media: '(prefers-color-scheme: light)', color: '#f1f3f5' },
{ media: '(prefers-color-scheme: dark)', color: '#1c1c1e' },
],
colorScheme: 'light dark',
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': '#1c1c1e',
},
};
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>
<Analytics />
</body>
</html>
);
}

View File

@@ -1,29 +0,0 @@
import { get } from '@vercel/edge-config';
import { NextResponse } from 'next/server';
export const runtime = 'edge';
export async function GET() {
try {
const url = await get<string>('DISCORD_WEBHOOK_URL');
const imageUrl = await get<string>('IT_IS_WEDNESDAY_MY_DUDES');
if (url && imageUrl) {
await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'It is wednesday, my dudes',
embeds: [
{
image: {
url: imageUrl,
},
},
],
}),
});
}
} catch {}
return NextResponse.json({ message: 'It is wednesday, my dudes' });
}

View File

@@ -1,5 +1,3 @@
import 'server-only';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { connect } from '@planetscale/database';

View File

@@ -116,7 +116,7 @@ export async function generateStaticParams({ params: { package: packageName, ver
}));
}
function Member({ member }: { member?: ApiItem }) {
function Member({ member }: { readonly member?: ApiItem }) {
switch (member?.kind) {
case 'Class':
return <Class clazz={member as ApiClass} />;

View File

@@ -1,6 +1,6 @@
'use client';
export default function Error({ error }: { error: Error }) {
export default function Error({ error }: { readonly error: Error }) {
console.error(error);
return (

View File

@@ -2,7 +2,6 @@ import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import type { SerializeOptions } from 'next-mdx-remote/dist/types';
import { MDXRemote } from 'next-mdx-remote/rsc';
import rehypeIgnore from 'rehype-ignore';
import rehypeRaw from 'rehype-raw';
import rehypeSlug from 'rehype-slug';
import remarkGfm from 'remark-gfm';
@@ -17,7 +16,7 @@ const mdxOptions = {
mdxOptions: {
remarkPlugins: [remarkGfm],
remarkRehypeOptions: { allowDangerousHtml: true },
rehypePlugins: [rehypeRaw, rehypeIgnore, rehypeSlug],
rehypePlugins: [rehypeRaw, rehypeSlug],
format: 'md',
},
} satisfies SerializeOptions;
@@ -28,7 +27,7 @@ export default async function Page({ params }: { params: VersionRouteParams }) {
return (
<div className="max-w-none prose">
{/* @ts-expect-error async component */}
{/* @ts-expect-error SyntaxHighlighter is assignable */}
<MDXRemote components={{ pre: SyntaxHighlighter }} options={mdxOptions} source={readmeSource} />
</div>
);

View File

@@ -3,6 +3,7 @@ import { VscArrowRight } from '@react-icons/all-files/vsc/VscArrowRight';
import { VscVersions } from '@react-icons/all-files/vsc/VscVersions';
import Link from 'next/link';
import { notFound } from 'next/navigation';
import { buttonVariants } from '~/styles/Button';
import { PACKAGES } from '~/util/constants';
export const runtime = 'edge';
@@ -31,11 +32,11 @@ export default async function Page({ params }: { params: { package: string } })
<div className="flex flex-col gap-4">
{data.map((version, idx) => (
<Link
className="h-11 flex flex-col transform-gpu cursor-pointer select-none appearance-none place-content-center border border-neutral-300 rounded bg-white p-4 text-base font-semibold leading-none text-black outline-none active:translate-y-px dark:border-dark-100 active:bg-neutral-200 dark:bg-dark-400 hover:bg-neutral-100 dark:text-white focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-200 dark:hover:bg-dark-300"
className={buttonVariants({ variant: 'secondary' })}
href={`/docs/packages/${params.package}/${version}`}
key={`${version}-${idx}`}
>
<div className="flex flex-row place-content-between place-items-center gap-4">
<div className="flex grow flex-row place-content-between place-items-center gap-4">
<div className="flex flex-row place-content-between place-items-center gap-4">
<VscVersions size={25} />
<h2 className="font-semibold">{version}</h2>
@@ -45,10 +46,7 @@ export default async function Page({ params }: { params: { package: string } })
</Link>
)) ?? null}
</div>
<Link
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center place-self-center gap-2 border-0 rounded bg-blurple px-4 text-base font-semibold leading-none text-white no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-white"
href="/docs/packages"
>
<Link className={buttonVariants({ className: 'place-self-center' })} href="/docs/packages">
<VscArrowLeft size={20} /> Go back
</Link>
</div>

View File

@@ -3,6 +3,7 @@ import { VscArrowLeft } from '@react-icons/all-files/vsc/VscArrowLeft';
import { VscArrowRight } from '@react-icons/all-files/vsc/VscArrowRight';
import { VscPackage } from '@react-icons/all-files/vsc/VscPackage';
import Link from 'next/link';
import { buttonVariants } from '~/styles/Button';
import { PACKAGES } from '~/util/constants';
export const runtime = 'edge';
@@ -12,64 +13,49 @@ export default function Page() {
<div className="mx-auto min-h-screen min-w-xs flex flex-col gap-8 px-4 py-6 sm:w-md lg:px-6 lg:py-6">
<h1 className="text-2xl font-semibold">Select a package:</h1>
<div className="flex flex-col gap-4">
<a
className="h-11 flex transform-gpu cursor-pointer select-none appearance-none place-content-between border border-neutral-300 rounded bg-white p-4 text-base font-semibold leading-none text-black outline-none active:translate-y-px dark:border-dark-100 active:bg-neutral-200 dark:bg-dark-400 hover:bg-neutral-100 dark:text-white focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-200 dark:hover:bg-dark-300"
href="https://old.discordjs.dev/#/docs/discord.js"
>
<a className={buttonVariants({ variant: 'secondary' })} href="https://old.discordjs.dev/#/docs/discord.js">
<div className="flex grow flex-row place-content-between place-items-center gap-4">
<div className="flex grow flex-row place-content-between place-items-center gap-4">
<div className="flex flex-row place-content-between place-items-center gap-4">
<VscPackage size={25} />
<h2 className="font-semibold">discord.js</h2>
</div>
<div className="flex flex-row place-content-between place-items-center gap-4">
<VscPackage size={25} />
<h2 className="font-semibold">discord.js</h2>
</div>
<VscArrowRight size={20} />
</div>
</a>
{PACKAGES.map((pkg, idx) => (
<Link
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-content-between border border-neutral-300 rounded bg-white p-4 text-base font-semibold leading-none text-black outline-none active:translate-y-px dark:border-dark-100 active:bg-neutral-200 dark:bg-dark-400 hover:bg-neutral-100 dark:text-white focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-200 dark:hover:bg-dark-300"
className={buttonVariants({ variant: 'secondary' })}
href={`/docs/packages/${pkg}`}
key={`${pkg}-${idx}`}
>
<div className="flex grow flex-row place-content-between place-items-center gap-4">
<div className="flex grow flex-row place-content-between place-items-center gap-4">
<div className="flex flex-row place-content-between place-items-center gap-4">
<VscPackage size={25} />
<h2 className="font-semibold">{pkg}</h2>
</div>
{/* <Link href={`/docs/packages/${pkg}`}>
<div
className="bg-blurple focus:ring-width-2 flex h-6 transform-gpu cursor-pointer select-none appearance-none flex-row place-content-center place-items-center rounded border-0 px-2 text-xs font-semibold leading-none text-white outline-none focus:ring focus:ring-white active:translate-y-px"
role="link"
>
Select version
</div>
</Link> */}
<div className="flex flex-row place-content-between place-items-center gap-4">
<VscPackage size={25} />
<h2 className="font-semibold">{pkg}</h2>
</div>
{/* <Link href={`/docs/packages/${pkg}`}>
<div
className="bg-blurple focus:ring-width-2 flex h-6 transform-gpu cursor-pointer select-none appearance-none flex-row place-content-center place-items-center rounded border-0 px-2 text-xs font-semibold leading-none text-white outline-none focus:ring focus:ring-white active:translate-y-px"
role="link"
>
Select version
</div>
</Link> */}
<VscArrowRight size={20} />
</div>
</Link>
))}
<a
className="h-11 flex transform-gpu cursor-pointer select-none appearance-none place-content-between border border-neutral-300 rounded bg-white p-4 text-base font-semibold leading-none text-black outline-none active:translate-y-px dark:border-dark-100 active:bg-neutral-200 dark:bg-dark-400 hover:bg-neutral-100 dark:text-white focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-200 dark:hover:bg-dark-300"
href="https://discord-api-types.dev/"
>
<a className={buttonVariants({ variant: 'secondary' })} href="https://discord-api-types.dev/">
<div className="flex grow flex-row place-content-between place-items-center gap-4">
<div className="flex grow flex-row place-content-between place-items-center gap-4">
<div className="flex flex-row place-content-between place-items-center gap-4">
<VscPackage size={25} />
<h2 className="font-semibold">discord-api-types</h2>
</div>
<div className="flex flex-row place-content-between place-items-center gap-4">
<VscPackage size={25} />
<h2 className="font-semibold">discord-api-types</h2>
</div>
<FiExternalLink size={20} />
</div>
</a>
</div>
<Link
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center place-self-center gap-2 border-0 rounded bg-blurple px-4 text-base font-semibold leading-none text-white no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-white"
href="/"
>
<Link className={buttonVariants({ className: 'place-self-center' })} href="/">
<VscArrowLeft size={20} /> Go back
</Link>
</div>

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