Compare commits
595 Commits
@discordjs
...
@discordjs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d92695cdd6 | ||
|
|
1d27b3bde0 | ||
|
|
903a7d3404 | ||
|
|
840dc565cd | ||
|
|
a84086194e | ||
|
|
b66c067dd7 | ||
|
|
e9d560f128 | ||
|
|
6f986886c5 | ||
|
|
7b913b674f | ||
|
|
7dc51aa935 | ||
|
|
16df4f3c38 | ||
|
|
6239f24f19 | ||
|
|
92501ae343 | ||
|
|
35f2b3a8c9 | ||
|
|
23ed447ec2 | ||
|
|
5d61197ca3 | ||
|
|
a6b9f1b37e | ||
|
|
cb961f5be3 | ||
|
|
96169add6d | ||
|
|
728164ed86 | ||
|
|
6cf094c282 | ||
|
|
997887069a | ||
|
|
a1aeaeb9d8 | ||
|
|
c7adce351a | ||
|
|
7ea3638dbc | ||
|
|
798f28cb9b | ||
|
|
14f9ff7412 | ||
|
|
c2e68ceaad | ||
|
|
dc8f14967c | ||
|
|
b79b7068e9 | ||
|
|
dae897bd09 | ||
|
|
4ad285804b | ||
|
|
6759f5b9c5 | ||
|
|
c6721d9aa7 | ||
|
|
1c5de21a29 | ||
|
|
7baa9e4333 | ||
|
|
afb97fbd00 | ||
|
|
7dc5bdfef5 | ||
|
|
de14c92c11 | ||
|
|
464d627f1d | ||
|
|
bfc3b100da | ||
|
|
f1f2683dc7 | ||
|
|
26af3868a5 | ||
|
|
b6bdd578b9 | ||
|
|
ba6476d07e | ||
|
|
980a2b71c7 | ||
|
|
654f1a48b9 | ||
|
|
a1a3a95c94 | ||
|
|
ddc927fabd | ||
|
|
f500ad6e2e | ||
|
|
6cc5fa28e6 | ||
|
|
93c174bc82 | ||
|
|
0d4c26ba4c | ||
|
|
5f2095b76c | ||
|
|
a66fc65742 | ||
|
|
a1010c61f5 | ||
|
|
8de8371204 | ||
|
|
0efd1bea46 | ||
|
|
9fa115df86 | ||
|
|
79fbda3aac | ||
|
|
2848591e21 | ||
|
|
3fd36f745f | ||
|
|
34936a0312 | ||
|
|
e401841f63 | ||
|
|
d6bf0fe43e | ||
|
|
59f4db3e1d | ||
|
|
71bba547b6 | ||
|
|
18cce83d80 | ||
|
|
0f9017ef95 | ||
|
|
efa3cac6f2 | ||
|
|
bfbd62e3e0 | ||
|
|
c8bbdb70f2 | ||
|
|
f67da74a5a | ||
|
|
bcd4c2cb23 | ||
|
|
8c2ababa78 | ||
|
|
1d565443b0 | ||
|
|
906ade9cc5 | ||
|
|
c89c343b0a | ||
|
|
992aa67841 | ||
|
|
e9d654772d | ||
|
|
543d61737e | ||
|
|
f48cb2a357 | ||
|
|
54106dbd81 | ||
|
|
ce6b2b74bc | ||
|
|
36db77f107 | ||
|
|
597340f288 | ||
|
|
ae57d7facb | ||
|
|
3755e66d41 | ||
|
|
c878b65ef5 | ||
|
|
f401cff3f4 | ||
|
|
bff1caebd1 | ||
|
|
ef83bc3e41 | ||
|
|
bc9b487eb1 | ||
|
|
56943a72f4 | ||
|
|
9f8d7fe7b4 | ||
|
|
42bc5d2c74 | ||
|
|
f69165883f | ||
|
|
d4472f85a5 | ||
|
|
b16647e6cc | ||
|
|
278396e815 | ||
|
|
fc1f8ae374 | ||
|
|
bfc7bb5564 | ||
|
|
a0c83a254c | ||
|
|
fed7f341be | ||
|
|
f48787eef1 | ||
|
|
1431c18769 | ||
|
|
33674be85e | ||
|
|
7e12bee337 | ||
|
|
7b8e0debeb | ||
|
|
136c66c213 | ||
|
|
ce84d3efee | ||
|
|
4824ac154d | ||
|
|
0f1e02b3dd | ||
|
|
c4fcee3ef6 | ||
|
|
520d6f64dd | ||
|
|
1c3211a5fc | ||
|
|
891a67ac4d | ||
|
|
7b5c31b2bc | ||
|
|
d869d9b3fe | ||
|
|
171cb182ed | ||
|
|
db56324624 | ||
|
|
a000df624c | ||
|
|
6cd6e3baaf | ||
|
|
e43f96cec5 | ||
|
|
461948c07d | ||
|
|
1acc9abae2 | ||
|
|
18c2dccd0e | ||
|
|
adfd9cd3b3 | ||
|
|
f2138bb5a8 | ||
|
|
651ffc2caf | ||
|
|
319ef9a70b | ||
|
|
8ace6face8 | ||
|
|
e245a390e7 | ||
|
|
2b8ac35e56 | ||
|
|
62e31cb9ee | ||
|
|
941642ad2f | ||
|
|
54453b04e5 | ||
|
|
b992019a78 | ||
|
|
17a6f5d3c9 | ||
|
|
a44ada661f | ||
|
|
b229240731 | ||
|
|
1ec2901f56 | ||
|
|
179af387d0 | ||
|
|
e412a22ceb | ||
|
|
30f6a5fc56 | ||
|
|
cbbbfb9823 | ||
|
|
fe90487974 | ||
|
|
d0aa8d25e2 | ||
|
|
bc2ecef73d | ||
|
|
9fdbf0ad65 | ||
|
|
57c414be21 | ||
|
|
e9ff99101b | ||
|
|
3d1c884926 | ||
|
|
ce0be392d8 | ||
|
|
802ec63a48 | ||
|
|
5a715706df | ||
|
|
ae58053dc8 | ||
|
|
5da5be2bc9 | ||
|
|
55be1c901a | ||
|
|
492f86af39 | ||
|
|
9868772b64 | ||
|
|
6df233de14 | ||
|
|
0aa7dc1b86 | ||
|
|
cab60142ff | ||
|
|
4b88306dcb | ||
|
|
f9177be61b | ||
|
|
75137bac6f | ||
|
|
00063912ee | ||
|
|
8f432400d8 | ||
|
|
75fc7f2454 | ||
|
|
01c63d2e0f | ||
|
|
ffc3ea5c3f | ||
|
|
1d2d01e1f5 | ||
|
|
0063dae43b | ||
|
|
69c949ab28 | ||
|
|
25d552b318 | ||
|
|
c4767bacde | ||
|
|
9a8110047e | ||
|
|
d4ebc369ca | ||
|
|
b150d4ac27 | ||
|
|
81a892e27f | ||
|
|
bc8f83368a | ||
|
|
7c935dc84b | ||
|
|
8d04cbc203 | ||
|
|
356cadb382 | ||
|
|
978a39f6d3 | ||
|
|
516be87a87 | ||
|
|
b79351ba99 | ||
|
|
2d63d93558 | ||
|
|
b305194841 | ||
|
|
2550c7931d | ||
|
|
40726db722 | ||
|
|
1e4ef35436 | ||
|
|
4bc1dae36f | ||
|
|
cc07a28f12 | ||
|
|
f93abf7e35 | ||
|
|
f0ec70dfda | ||
|
|
8a6045f600 | ||
|
|
418032ae1f | ||
|
|
dbb92e5bc4 | ||
|
|
975d5f18ae | ||
|
|
4ff3ea4a1b | ||
|
|
b5e23ec2ec | ||
|
|
81e7866903 | ||
|
|
62e6573296 | ||
|
|
cf49f405b0 | ||
|
|
2a25eeaf13 | ||
|
|
5a4c9755c3 | ||
|
|
5b0aa92c81 | ||
|
|
d28814d869 | ||
|
|
ff24bc5729 | ||
|
|
81334a2a2c | ||
|
|
6dca8013af | ||
|
|
a87b267fba | ||
|
|
aad82f088b | ||
|
|
e5f3f3130e | ||
|
|
4a5e9fc1de | ||
|
|
e72b552ae2 | ||
|
|
34d0224b68 | ||
|
|
fcfe5cf142 | ||
|
|
fb2e7a0aac | ||
|
|
4575e49ea1 | ||
|
|
2bda883a0f | ||
|
|
fffe70a039 | ||
|
|
413d6d67c5 | ||
|
|
da455bceea | ||
|
|
f713e47b0a | ||
|
|
defeee5eec | ||
|
|
67a2538b4d | ||
|
|
7208d69c05 | ||
|
|
6b9f906b03 | ||
|
|
347df4e083 | ||
|
|
ee2afb0aae | ||
|
|
5c0fad3b2d | ||
|
|
95c0b1a59f | ||
|
|
65966ae6ec | ||
|
|
3b8df63a5a | ||
|
|
054eaec7d7 | ||
|
|
c8227ca533 | ||
|
|
1eee9dc257 | ||
|
|
6a6c4bdcae | ||
|
|
c39c94374a | ||
|
|
792840bae6 | ||
|
|
009c0a3bae | ||
|
|
344a3f9344 | ||
|
|
a1abc6b273 | ||
|
|
4f4dde2a09 | ||
|
|
0ca0254d83 | ||
|
|
6cd3adf030 | ||
|
|
637e1a4ddb | ||
|
|
ac645084f0 | ||
|
|
c051ed9427 | ||
|
|
c2349d4be4 | ||
|
|
31d914e44b | ||
|
|
efbcda70fc | ||
|
|
b6c762cb84 | ||
|
|
85a78f96d4 | ||
|
|
85753a9d6f | ||
|
|
a9e1f6026c | ||
|
|
e80194e3fb | ||
|
|
0e0b85b766 | ||
|
|
5b4a51945c | ||
|
|
7671a836f4 | ||
|
|
e307581442 | ||
|
|
77be0f0f2d | ||
|
|
e38d03fbe7 | ||
|
|
6a63c441fe | ||
|
|
8964504d07 | ||
|
|
7276e7c4b7 | ||
|
|
8cfadb6953 | ||
|
|
7422d9f172 | ||
|
|
1fe7247528 | ||
|
|
44a3cbf39e | ||
|
|
332b624aed | ||
|
|
699b232922 | ||
|
|
11f6955ed9 | ||
|
|
2aa3250584 | ||
|
|
ec47e72b41 | ||
|
|
e1edba17c4 | ||
|
|
0529b2af95 | ||
|
|
f58628385c | ||
|
|
a674989fe2 | ||
|
|
cd987b592b | ||
|
|
ec63818bcf | ||
|
|
3e516e6e9b | ||
|
|
5aeb6e1ae6 | ||
|
|
93e5bed87d | ||
|
|
3c043d83a9 | ||
|
|
931c3ed593 | ||
|
|
eaabcdfda6 | ||
|
|
19ea0baa00 | ||
|
|
1e5c14b741 | ||
|
|
ec3bcb93a5 | ||
|
|
5a7b5b56ae | ||
|
|
5bdb34b3be | ||
|
|
571aedd58a | ||
|
|
e02a59bbb6 | ||
|
|
32d614ccd3 | ||
|
|
4588e075c3 | ||
|
|
b6a2441819 | ||
|
|
310979808e | ||
|
|
ed14135844 | ||
|
|
85b24988a5 | ||
|
|
7cdfc6c72a | ||
|
|
c66636da11 | ||
|
|
9dbc9542c4 | ||
|
|
e68ab167c2 | ||
|
|
6404c013e7 | ||
|
|
c50809e206 | ||
|
|
8d97e2d2c5 | ||
|
|
ff1fda7b59 | ||
|
|
ba31972d86 | ||
|
|
778df45166 | ||
|
|
23a6424261 | ||
|
|
7157748fe3 | ||
|
|
85d03a3000 | ||
|
|
e1ae7b4d57 | ||
|
|
e96a8a977f | ||
|
|
8325fa6540 | ||
|
|
485dd718c5 | ||
|
|
566d5e2c81 | ||
|
|
0d787e9f79 | ||
|
|
50106c77db | ||
|
|
22011575cd | ||
|
|
e3127a0735 | ||
|
|
9811514deb | ||
|
|
6ea63d000b | ||
|
|
fb81f7befb | ||
|
|
788888ab9a | ||
|
|
f6c7c14c9e | ||
|
|
d37632da05 | ||
|
|
2e40a05adf | ||
|
|
1e3aed0d97 | ||
|
|
5d7c59c301 | ||
|
|
899bc5f78b | ||
|
|
dd5e7453e8 | ||
|
|
8eb978d32c | ||
|
|
5d1b233047 | ||
|
|
4ab05e994e | ||
|
|
4bc2667a5f | ||
|
|
84ead6e554 | ||
|
|
0220fb7278 | ||
|
|
6da456767e | ||
|
|
67b30355ea | ||
|
|
58d9024245 | ||
|
|
5cd6382d1d | ||
|
|
fffe625a0c | ||
|
|
7321c291da | ||
|
|
ab3efb0211 | ||
|
|
7fa9ea3f0f | ||
|
|
af0f1aa765 | ||
|
|
99cadfd82b | ||
|
|
8f572a6bad | ||
|
|
d90ba8dce8 | ||
|
|
24fbb11ba2 | ||
|
|
727dc094d5 | ||
|
|
d5be4242c6 | ||
|
|
c1ff545bf1 | ||
|
|
188877c50a | ||
|
|
a1605b3a5e | ||
|
|
9ed1b59df6 | ||
|
|
0a9a3ede29 | ||
|
|
346fa57f95 | ||
|
|
b3c85d34a6 | ||
|
|
d8e37551ce | ||
|
|
a30d46c5f5 | ||
|
|
632a9b4965 | ||
|
|
3b18e5b08d | ||
|
|
692f0fc96d | ||
|
|
0de071d0a5 | ||
|
|
99194fc270 | ||
|
|
cf72d42ea5 | ||
|
|
7295a3a94a | ||
|
|
1af7e5a0bb | ||
|
|
3bc0dce04a | ||
|
|
c40f7540cd | ||
|
|
1fc9308fef | ||
|
|
43d3b48565 | ||
|
|
8d50f2056d | ||
|
|
acc560204f | ||
|
|
ccf65c2d09 | ||
|
|
d04987f09b | ||
|
|
ca5a2626b4 | ||
|
|
3c5a4dc7a7 | ||
|
|
447652ec8a | ||
|
|
488aa58b29 | ||
|
|
a92d19212d | ||
|
|
fd8197fe5b | ||
|
|
8f4256db8a | ||
|
|
6307f81385 | ||
|
|
d26e022afc | ||
|
|
afa98793cd | ||
|
|
386f206caf | ||
|
|
351a18bc35 | ||
|
|
84f1b1890d | ||
|
|
e5effb6f6a | ||
|
|
125405f1cf | ||
|
|
bc83cabfda | ||
|
|
20268ac0c4 | ||
|
|
3c85fb21e6 | ||
|
|
4141405dcd | ||
|
|
8c782bfd52 | ||
|
|
8d97017458 | ||
|
|
ceab07bec8 | ||
|
|
7fb91c57f7 | ||
|
|
09b0382c45 | ||
|
|
719e54a921 | ||
|
|
ede9f4e5e2 | ||
|
|
df40dcdb85 | ||
|
|
53c17e00c0 | ||
|
|
dc73c938ff | ||
|
|
a48d0efb09 | ||
|
|
a73d54e43a | ||
|
|
cf8012c200 | ||
|
|
0803eb562b | ||
|
|
78381a56cf | ||
|
|
6d5840c61e | ||
|
|
75d91b52b3 | ||
|
|
766be94b93 | ||
|
|
b61e4fb0dc | ||
|
|
d64330a157 | ||
|
|
2818d7cc1d | ||
|
|
a6dbe163dd | ||
|
|
73c2f8aa17 | ||
|
|
a873ec1e85 | ||
|
|
db925fa269 | ||
|
|
8f3bd38072 | ||
|
|
8f014c65ef | ||
|
|
1716202f44 | ||
|
|
1cab79f6fd | ||
|
|
7b494aca5b | ||
|
|
cd6986854f | ||
|
|
6c2242f4f9 | ||
|
|
75308f2669 | ||
|
|
bc2798b8ee | ||
|
|
a531dc9f1c | ||
|
|
911e6eff75 | ||
|
|
1ab60f9da4 | ||
|
|
df8b6e9934 | ||
|
|
24a61495b9 | ||
|
|
e1b6eeed0e | ||
|
|
53aa24d418 | ||
|
|
9b06734445 | ||
|
|
9345d1b1ac | ||
|
|
47843493a5 | ||
|
|
6c7a5ed1e7 | ||
|
|
985def3f25 | ||
|
|
1c4a12c7d6 | ||
|
|
8107c5c0e7 | ||
|
|
b85a3f2dde | ||
|
|
3535321b98 | ||
|
|
46167a79d7 | ||
|
|
cffa5d19c9 | ||
|
|
5d6eed6414 | ||
|
|
90e243d350 | ||
|
|
590f5bc38e | ||
|
|
7196fe36e8 | ||
|
|
5351ab429b | ||
|
|
d033f925e0 | ||
|
|
13073acefc | ||
|
|
7c8849fae5 | ||
|
|
d01e8aa8af | ||
|
|
4dcc9c50f8 | ||
|
|
a51c48e743 | ||
|
|
1edd01a7a4 | ||
|
|
7b617bd22e | ||
|
|
0c175c02e9 | ||
|
|
460ac7dfe2 | ||
|
|
64324a8be1 | ||
|
|
b6162bc5b5 | ||
|
|
280ed0ce08 | ||
|
|
5b8d535fd6 | ||
|
|
c4a3120354 | ||
|
|
8482e3c95d | ||
|
|
12482b70ed | ||
|
|
fa746b079d | ||
|
|
fc3f0756a4 | ||
|
|
3bfb6197a0 | ||
|
|
5ecc4f13b5 | ||
|
|
f83a8a58c9 | ||
|
|
9ddb3ff71d | ||
|
|
7ce6bb99e9 | ||
|
|
da6de92e8c | ||
|
|
bbf80efbb0 | ||
|
|
227232112d | ||
|
|
37181ab232 | ||
|
|
cdaa0a36f5 | ||
|
|
fc5b9c523b | ||
|
|
256b68bf9f | ||
|
|
75195cc539 | ||
|
|
8d5ce32080 | ||
|
|
5bfcf93e29 | ||
|
|
28f51791a2 | ||
|
|
e6274681fd | ||
|
|
00da44a120 | ||
|
|
ab39683a50 | ||
|
|
3e80f0b384 | ||
|
|
e53b94c979 | ||
|
|
6b2c3fb9d0 | ||
|
|
d8d21cf370 | ||
|
|
8e9a2b4630 | ||
|
|
d4f12e0a0c | ||
|
|
d7b18f0681 | ||
|
|
166c961261 | ||
|
|
506f7fcd3b | ||
|
|
51d53f5117 | ||
|
|
aa32d67619 | ||
|
|
4b077e679c | ||
|
|
c190c57d64 | ||
|
|
423bfce286 | ||
|
|
5d05b7230a | ||
|
|
7e875f6919 | ||
|
|
c429763be8 | ||
|
|
1b7981e469 | ||
|
|
f2ad0762c5 | ||
|
|
6d7f3b0f5c | ||
|
|
201b002ad4 | ||
|
|
b327f4925f | ||
|
|
e262df9117 | ||
|
|
7a761d4cfb | ||
|
|
ac9bf3ac06 | ||
|
|
133a6bbf49 | ||
|
|
63fc9f8504 | ||
|
|
a9f2bff82a | ||
|
|
54ceedf6c5 | ||
|
|
36b7fb2f00 | ||
|
|
9a56edaf8c | ||
|
|
be81c8bf3b | ||
|
|
c77a1765c4 | ||
|
|
3665ad65d0 | ||
|
|
d4de1cd09e | ||
|
|
481bbc924f | ||
|
|
ca81e61b4c | ||
|
|
eb603c0d11 | ||
|
|
072c9da5df | ||
|
|
cd591327d6 | ||
|
|
71e67d8c97 | ||
|
|
a9e0de4288 | ||
|
|
c5b4423a5a | ||
|
|
47b08be509 | ||
|
|
2e0804ae92 | ||
|
|
71983cc5d1 | ||
|
|
0f3d870ce6 | ||
|
|
ce4e1fc2c6 | ||
|
|
0762ddb3f4 | ||
|
|
80af9696d3 | ||
|
|
48cab844d4 | ||
|
|
8b71f44b1f | ||
|
|
f27631175a | ||
|
|
ad217cc760 | ||
|
|
1ba7394a7d | ||
|
|
260c46d51c | ||
|
|
6229597db2 | ||
|
|
8d212e4859 | ||
|
|
51e8f02ef7 | ||
|
|
496c4e2884 | ||
|
|
77191a2e7b | ||
|
|
375483b0f9 | ||
|
|
17cd4b610a | ||
|
|
988f8c7546 | ||
|
|
217e5d8100 | ||
|
|
40d07fbbbd | ||
|
|
1b15d31b5a | ||
|
|
d26df5faba | ||
|
|
78fe247fc4 | ||
|
|
36216c0e1a | ||
|
|
6212bffa30 | ||
|
|
b2e1bb92d0 | ||
|
|
2e09cb4c1f | ||
|
|
1b4d34cebf | ||
|
|
8153ca99ee | ||
|
|
43e205b840 | ||
|
|
c6ca5a83e7 | ||
|
|
5c52bb9590 | ||
|
|
a49ed0a2d5 | ||
|
|
3e01f91bbb | ||
|
|
7d341000d4 | ||
|
|
b49c5c9f48 | ||
|
|
ce287f21d1 | ||
|
|
d6bca9bb4d | ||
|
|
a2ab2b8717 | ||
|
|
ecd1b5da11 | ||
|
|
0eb866357b | ||
|
|
d2d27ce734 | ||
|
|
603446a692 | ||
|
|
f1fdd5b010 | ||
|
|
5d1a4c27d5 | ||
|
|
98a76db482 | ||
|
|
516c46117c | ||
|
|
7d7972d239 | ||
|
|
7b10e8cb63 | ||
|
|
2f18323d2c | ||
|
|
e04801bba1 | ||
|
|
09a9fb57e0 | ||
|
|
025b7aea29 |
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/commitlintrc.json",
|
||||
"extends": ["@commitlint/config-angular"],
|
||||
"rules": {
|
||||
"type-enum": [
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"extends": ["neon/common", "neon/node", "neon/typescript", "neon/prettier"],
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.eslint.json"
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/consistent-type-definitions": ["error", "interface"]
|
||||
},
|
||||
"ignorePatterns": ["**/dist/*"]
|
||||
}
|
||||
1
.gitattributes
vendored
@@ -1 +1,2 @@
|
||||
* text=auto eol=lf
|
||||
pnpm-lock.yaml linguist-generated=true text=auto eol=lf
|
||||
|
||||
2
.github/.kodiak.toml
vendored
@@ -2,7 +2,7 @@ version = 1
|
||||
|
||||
[merge]
|
||||
require_automerge_label = false
|
||||
blocking_labels = ['blocked']
|
||||
blocking_labels = ['blocked', 'in review']
|
||||
method = 'squash'
|
||||
|
||||
[merge.message]
|
||||
|
||||
1
.github/CODEOWNERS
vendored
@@ -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
|
||||
|
||||
27
.github/CONTRIBUTING.md
vendored
@@ -11,22 +11,20 @@ is a great boon to your development process.
|
||||
To get ready to work on the codebase, please do the following:
|
||||
|
||||
1. Fork & clone the repository, and make sure you're on the **main** branch
|
||||
2. Run `yarn --immutable` ([install](https://yarnpkg.com/getting-started/install))
|
||||
3. Run `yarn build` to build local packages
|
||||
2. Run `pnpm install --frozen-lockfile` ([install](https://pnpm.io/installation))
|
||||
3. Run `pnpm run build` to build local packages
|
||||
4. Code your heart out!
|
||||
5. Run `yarn test` to run ESLint and ensure any JSDoc changes are valid
|
||||
5. Run `pnpm run test` to run ESLint and ensure any JSDoc changes are valid
|
||||
6. [Submit a pull request](https://github.com/discordjs/discord.js/compare) (Make sure you follow the [conventional commit format](https://github.com/discordjs/discord.js/blob/main/.github/COMMIT_CONVENTION.md))
|
||||
|
||||
## Testing changes locally
|
||||
|
||||
If you want to test changes you've made locally, you can do so by using `yarn link`. This will create a symlink to your local copy of the discord.js libraries.
|
||||
If you want to test changes you've made locally, you can do so by using `pnpm link <package-you-want-to-link-to-your-current-package>`. This will create a symlink to your local copy of the discord.js libraries.
|
||||
|
||||
1. Create a new directory `mkdir discordjs-test` and move into it `cd discordjs-test`
|
||||
2. Initialize a new yarn 3 project `yarn init -2`
|
||||
3. Disable pnp `yarn config set nodeLinker node-modules`
|
||||
4. Now link the local discord.js project you cloned earlier `yarn link -A {PATH_TO_DISCORDJS_REPO}`
|
||||
5. Install packages you'd like to test locally `yarn add discord.js@latest`, `yarn add @discordjs/rest@latest`, etc. **Note: Make sure you use `latest` tag or else yarn will try to install the remote package from npm**
|
||||
6. Import the package in your source code and test them out!
|
||||
2. Initialize a new pnpm project `pnpm init`
|
||||
3. Now link the discord.js package from the directory you cloned earlier `pnpm link {PATH_TO_DISCORDJS_REPO}/packages/<package>`. (e.g. `pnpm link ~/discord.js/packages/rest`)
|
||||
4. Import the package in your source code and test them out!
|
||||
|
||||
### Working with TypeScript packages
|
||||
|
||||
@@ -34,15 +32,18 @@ When testing local changes, you may notice you need to manually recompile TypeSc
|
||||
|
||||
To avoid this you can use the `--watch` parameter in the package build script to automatically recompile the project when changes are detected.
|
||||
|
||||
For example, to automatically recompile the `@discordjs/rest` project when changes are detected, run `yarn turbo run build --filter=@discordjs/rest -- --watch` in the root folder of where you cloned the discord.js repo.
|
||||
For example, to automatically recompile the `@discordjs/rest` project when changes are detected, run `pnpm turbo run build --filter='@discordjs/rest' -- --watch` in the root folder of where you cloned the discord.js repo.
|
||||
|
||||
## Adding new packages
|
||||
|
||||
If you'd like to create another package under the `@discordjs` organization run the following command:
|
||||
|
||||
```sh
|
||||
yarn create-package <package-name> [package-description]
|
||||
pnpm run 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.
|
||||
This will create new package directory under `packages/` with the required configuration files. You may begin
|
||||
to make changes within the `src/` directory. You may also need to:
|
||||
|
||||
- Update workflows that utilize packages
|
||||
- Update the CODEOWNERS 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
|
||||
@@ -137,11 +123,13 @@ body:
|
||||
- GuildScheduledEvents
|
||||
- AutoModerationConfiguration
|
||||
- AutoModerationExecution
|
||||
- GuildMessagePolls
|
||||
- DirectMessagePolls
|
||||
multiple: true
|
||||
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)
|
||||
58
.github/ISSUE_TEMPLATE/02-application_bug_report.yml
vendored
Normal 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
|
||||
@@ -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.
|
||||
51
.github/issue-labeler.yml
vendored
@@ -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"
|
||||
|
||||
15
.github/labeler.yml
vendored
@@ -4,6 +4,12 @@ apps:guide:
|
||||
apps:website:
|
||||
- apps/website/*
|
||||
- apps/website/**/*
|
||||
packages:api-extractor:
|
||||
- packages/api-extractor/*
|
||||
- packages/api-extractor/**/*
|
||||
packages:api-extractor-model:
|
||||
- packages/api-extractor-model/*
|
||||
- packages/api-extractor-model/**/*
|
||||
packages:brokers:
|
||||
- packages/brokers/*
|
||||
- packages/brokers/**/*
|
||||
@@ -16,18 +22,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/**/*
|
||||
|
||||
10
.github/labels.yml
vendored
@@ -52,6 +52,10 @@
|
||||
color: e4e669
|
||||
- name: need repro
|
||||
color: c66037
|
||||
- name: packages:api-extractor
|
||||
color: fbca04
|
||||
- name: packages:api-extractor-model
|
||||
color: fbca04
|
||||
- name: packages:brokers
|
||||
color: fbca04
|
||||
- name: packages:builders
|
||||
@@ -60,14 +64,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
|
After Width: | Height: | Size: 16 KiB |
35
.github/workflows/cleanup-cache.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#force-deleting-cache-entries
|
||||
name: Cleanup caches
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
cleanup:
|
||||
name: Cleanup caches
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Cleanup caches
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
|
||||
REPO=${{ github.repository }}
|
||||
BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge"
|
||||
|
||||
echo "Fetching list of cache key"
|
||||
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 )
|
||||
|
||||
## Setting this to not fail the workflow while deleting cache keys.
|
||||
set +e
|
||||
echo "Deleting caches..."
|
||||
for cacheKey in $cacheKeysForPR
|
||||
do
|
||||
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
|
||||
done
|
||||
echo "Done"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
34
.github/workflows/deploy-website.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: Deploy website
|
||||
on:
|
||||
workflow_dispatch:
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
deploy-website:
|
||||
name: Deploy website
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
|
||||
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
|
||||
if: github.repository_owner == 'discordjs'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/pnpmCache
|
||||
|
||||
- name: Pull vercel production environment
|
||||
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
|
||||
|
||||
- name: Build website artifacts
|
||||
run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
|
||||
|
||||
- name: Deploy website artifacts to vercel
|
||||
run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}
|
||||
50
.github/workflows/deprecate-version.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
name: Deprecate version
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
package:
|
||||
description: Package
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- '@discordjs/brokers'
|
||||
- '@discordjs/builders'
|
||||
- '@discordjs/collection'
|
||||
- '@discordjs/core'
|
||||
- 'create-discord-bot'
|
||||
- '@discordjs/formatters'
|
||||
- 'discord.js'
|
||||
- '@discordjs/next'
|
||||
- '@discordjs/proxy'
|
||||
- '@discordjs/rest'
|
||||
- '@discordjs/util'
|
||||
- '@discordjs/voice'
|
||||
- '@discordjs/ws'
|
||||
version:
|
||||
description: Version(s)
|
||||
required: true
|
||||
type: string
|
||||
message:
|
||||
description: Deprecation message
|
||||
required: false
|
||||
type: string
|
||||
jobs:
|
||||
deprecate:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'discordjs'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/pnpmCache
|
||||
|
||||
- name: Deprecate
|
||||
run: pnpm exec npm-deprecate --name "${{inputs.version}}" --message "${{inputs.message || 'This version is deprecated. Please use a newer version.'}}" --package ${{inputs.package}}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
||||
247
.github/workflows/documentation.yml
vendored
@@ -5,6 +5,9 @@ on:
|
||||
- 'main'
|
||||
paths:
|
||||
- 'packages/*/src/**'
|
||||
- '!packages/create-discord-bot/**'
|
||||
- '!packages/proxy-container/**'
|
||||
- '!packages/ui/**'
|
||||
tags:
|
||||
- '**'
|
||||
workflow_dispatch:
|
||||
@@ -23,12 +26,13 @@ concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
build:
|
||||
name: Build documentation
|
||||
build-docs:
|
||||
name: Build & upload documentation
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
REF_TYPE: ${{ inputs.ref_type || github.ref_type }}
|
||||
if: github.repository_owner == 'discordjs'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -36,84 +40,57 @@ 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
|
||||
uses: ./packages/actions/src/pnpmCache
|
||||
|
||||
- name: Build dependencies
|
||||
run: yarn build
|
||||
run: pnpm run build
|
||||
|
||||
- name: Checkout main repository
|
||||
if: ${{ inputs.ref && inputs.ref != 'main' }}
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: 'main'
|
||||
|
||||
- name: Build main
|
||||
if: ${{ inputs.ref && inputs.ref != 'main' }}
|
||||
shell: bash
|
||||
run: |
|
||||
cd main
|
||||
pnpm install --frozen-lockfile --prefer-offline --loglevel error
|
||||
pnpm run build
|
||||
cd ..
|
||||
|
||||
- name: Extract package and semver from tag
|
||||
if: ${{ env.REF_TYPE == 'tag' }}
|
||||
id: extract-tag
|
||||
uses: ./packages/actions/src/formatTag
|
||||
with:
|
||||
tag: ${{ inputs.ref || github.ref_name }}
|
||||
|
||||
- name: Apply tag to api-extractor config
|
||||
if: ${{ env.REF_TYPE == 'tag' && !inputs.ref }}
|
||||
run: sed -i 's!https://github.com/discordjs/discord.js/tree/main!https://github.com/discordjs/discord.js/tree/${{ steps.extract-tag.outputs.semver }}!' "packages/${{ steps.extract-tag.outputs.package}}/"
|
||||
|
||||
- name: Build docs
|
||||
run: yarn docs
|
||||
run: pnpm run 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: Build docs with main api-extractor
|
||||
if: ${{ inputs.ref && inputs.ref != 'main' }}
|
||||
run: |
|
||||
declare -a PACKAGES=("brokers" "builders" "collection" "core" "discord.js" "formatters" "next" "proxy" "rest" "util" "voice" "ws")
|
||||
for PACKAGE in "${PACKAGES[@]}"; do
|
||||
cd "packages/${PACKAGE}"
|
||||
sed -i 's!https://github.com/discordjs/discord.js/tree/main!https://github.com/discordjs/discord.js/tree/${{ inputs.ref }}!' api-extractor.json
|
||||
../../main/packages/api-extractor/bin/api-extractor run --local --minify
|
||||
../../main/packages/scripts/bin/generateSplitDocumentation.js
|
||||
cd ../..
|
||||
done
|
||||
|
||||
- name: Checkout docs repository
|
||||
uses: actions/checkout@v3
|
||||
@@ -122,56 +99,98 @@ jobs:
|
||||
token: ${{ secrets.DJS_DOCS }}
|
||||
path: 'out'
|
||||
|
||||
- name: Extract package and semver from tag
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
id: extract-tag
|
||||
uses: ./packages/actions/src/formatTag
|
||||
with:
|
||||
tag: ${{ github.ref_name }}
|
||||
|
||||
- name: Upload documentation to database
|
||||
if: ${{ github.ref_type == 'tag' && matrix.package == steps.extract-tag.outputs.package }}
|
||||
if: ${{ env.REF_TYPE == 'tag' && (!inputs.ref || inputs.ref == 'main') }}
|
||||
env:
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
|
||||
uses: ./packages/actions/src/uploadDocumentation
|
||||
with:
|
||||
package: ${{ steps.extract-tag.outputs.package }}
|
||||
version: ${{ steps.extract-tag.outputs.semver }}
|
||||
|
||||
- name: Upload documentation to database
|
||||
if: ${{ env.REF_TYPE == 'tag' && inputs.ref && inputs.ref != 'main' }}
|
||||
env:
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
|
||||
uses: ./main/packages/actions/src/uploadDocumentation
|
||||
with:
|
||||
package: ${{ steps.extract-tag.outputs.package }}
|
||||
version: ${{ steps.extract-tag.outputs.semver }}
|
||||
|
||||
- name: Upload split documentation to blob storage
|
||||
if: ${{ env.REF_TYPE == 'tag' && (!inputs.ref || inputs.ref == 'main') }}
|
||||
env:
|
||||
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
|
||||
uses: ./packages/actions/src/uploadSplitDocumentation
|
||||
with:
|
||||
package: ${{ steps.extract-tag.outputs.package }}
|
||||
version: ${{ steps.extract-tag.outputs.semver }}
|
||||
|
||||
- name: Upload split documentation to blob storage
|
||||
if: ${{ env.REF_TYPE == 'tag' && inputs.ref && inputs.ref != 'main' }}
|
||||
env:
|
||||
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
|
||||
uses: ./main/packages/actions/src/uploadSplitDocumentation
|
||||
with:
|
||||
package: ${{ steps.extract-tag.outputs.package }}
|
||||
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: ${{ env.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"
|
||||
mv "packages/${PACKAGE}/docs/docs.api.json" "out/${PACKAGE}/${SEMVER}.api.json"
|
||||
else
|
||||
mv "packages/${PACKAGE}/docs/docs.api.json" "out/${PACKAGE}/${SEMVER}.api.json"
|
||||
fi
|
||||
|
||||
- name: Upload documentation to database
|
||||
if: ${{ github.ref_type == 'branch' }}
|
||||
if: ${{ env.REF_TYPE == 'branch' && (!inputs.ref || inputs.ref == 'main') }}
|
||||
env:
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
|
||||
uses: ./packages/actions/src/uploadDocumentation
|
||||
with:
|
||||
package: ${{ matrix.package }}
|
||||
|
||||
- name: Upload documentation to database
|
||||
if: ${{ env.REF_TYPE == 'branch' && inputs.ref && inputs.ref != 'main' }}
|
||||
env:
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
|
||||
uses: ./main/packages/actions/src/uploadDocumentation
|
||||
|
||||
- name: Upload split documentation to blob storage
|
||||
if: ${{ env.REF_TYPE == 'branch' && (!inputs.ref || inputs.ref == 'main') }}
|
||||
env:
|
||||
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
|
||||
uses: ./packages/actions/src/uploadSplitDocumentation
|
||||
|
||||
- name: Upload split documentation to blob storage
|
||||
if: ${{ env.REF_TYPE == 'branch' && inputs.ref && inputs.ref != 'main' }}
|
||||
env:
|
||||
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
|
||||
uses: ./main/packages/actions/src/uploadSplitDocumentation
|
||||
|
||||
- name: Move docs to correct directory
|
||||
if: ${{ github.ref_type == 'branch' }}
|
||||
env:
|
||||
PACKAGE: ${{ matrix.package }}
|
||||
if: ${{ env.REF_TYPE == 'branch' }}
|
||||
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" "formatters" "next" "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"
|
||||
mv "packages/${PACKAGE}/docs/docs.api.json" "out/${PACKAGE}/${GITHUB_REF_NAME}.api.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: |
|
||||
@@ -181,3 +200,33 @@ jobs:
|
||||
git add .
|
||||
git commit -m "Docs build for ${GITHUB_REF_TYPE} ${GITHUB_REF_NAME}: ${GITHUB_SHA}" || true
|
||||
git push
|
||||
|
||||
build-indices:
|
||||
needs: build-docs
|
||||
name: Build & upload search indices
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
if: github.repository_owner == 'discordjs'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/pnpmCache
|
||||
|
||||
- name: Build dependencies
|
||||
run: pnpm run build
|
||||
|
||||
- name: Upload search indices to meilisearch
|
||||
env:
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
SEARCH_API_URL: ${{ secrets.SEARCH_API_URL }}
|
||||
SEARCH_API_KEY: ${{ secrets.SEARCH_API_KEY }}
|
||||
uses: ./packages/actions/src/uploadSearchIndices
|
||||
|
||||
2
.github/workflows/issue-triage.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
||||
issue-triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: github/issue-labeler@v2.5
|
||||
- uses: github/issue-labeler@v3.2
|
||||
with:
|
||||
repo-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
configuration-path: .github/issue-labeler.yml
|
||||
|
||||
20
.github/workflows/lighthouse-main.yml
vendored
@@ -1,20 +0,0 @@
|
||||
name: 'Lighthouse Audit (main)'
|
||||
on:
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
lighthouse_audit_main:
|
||||
name: 'Lighthouse Audit (main)'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Audit production URLs with Lighthouse
|
||||
id: lighthouse_audit
|
||||
uses: treosh/lighthouse-ci-action@v9
|
||||
with:
|
||||
urls: |
|
||||
https://discordjs.dev
|
||||
https://guide.discordjs.dev
|
||||
uploadArtifacts: true
|
||||
temporaryPublicStorage: true
|
||||
88
.github/workflows/lighthouse.yml
vendored
@@ -1,88 +0,0 @@
|
||||
name: 'Lighthouse Audit'
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
jobs:
|
||||
lighthouse_audit:
|
||||
name: 'Lighthouse Audit'
|
||||
if: ${{ github.event.issue.pull_request }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get Vercel preview URL
|
||||
id: get_preview_url
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
script: |
|
||||
const comment = context.payload.comment;
|
||||
const regex = /https:\/\/[a-z0-9-]+\.vercel\.app/g;
|
||||
const matches = comment.body.match(regex);
|
||||
let previewUrl = "";
|
||||
if (matches && matches.length) {
|
||||
previewUrl = matches[0];
|
||||
console.log('Preview url found:', previewUrl);
|
||||
}
|
||||
console.log("No preview url found.");
|
||||
core.setOutput('vercel_preview_url', previewUrl);
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Add comment to PR
|
||||
if: ${{ steps.get_preview_url.outputs.vercel_preview_url != '' }}
|
||||
id: loading_comment_to_pr
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
number: ${{ github.event.issue.number }}
|
||||
header: lighthouse
|
||||
message: |
|
||||
Running Lighthouse audit...
|
||||
|
||||
- name: Checkout repository
|
||||
if: ${{ steps.get_preview_url.outputs.vercel_preview_url != '' }}
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Audit preview URL with Lighthouse
|
||||
if: ${{ steps.get_preview_url.outputs.vercel_preview_url != '' }}
|
||||
id: lighthouse_audit
|
||||
uses: treosh/lighthouse-ci-action@v9
|
||||
with:
|
||||
urls: |
|
||||
${{ steps.get_preview_url.outputs.vercel_preview_url }}
|
||||
uploadArtifacts: true
|
||||
temporaryPublicStorage: true
|
||||
|
||||
- name: Format lighthouse score
|
||||
if: ${{ steps.get_preview_url.outputs.vercel_preview_url != '' }}
|
||||
id: format_lighthouse_score
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
script: |
|
||||
const result = ${{ steps.lighthouse_audit.outputs.manifest }}[0].summary
|
||||
const links = ${{ steps.lighthouse_audit.outputs.links }}
|
||||
const formatResult = (res) => Math.round((res * 100))
|
||||
Object.keys(result).forEach(key => result[key] = formatResult(result[key]))
|
||||
const score = res => res >= 90 ? '🟢' : res >= 50 ? '🟠' : '🔴'
|
||||
const comment = [
|
||||
`⚡️ [Lighthouse report](${Object.values(links)[0]}) for the changes in this PR:`,
|
||||
'| Category | Score |',
|
||||
'| --- | --- |',
|
||||
`| ${score(result.performance)} Performance | ${result.performance} |`,
|
||||
`| ${score(result.accessibility)} Accessibility | ${result.accessibility} |`,
|
||||
`| ${score(result['best-practices'])} Best practices | ${result['best-practices']} |`,
|
||||
`| ${score(result.seo)} SEO | ${result.seo} |`,
|
||||
`| ${score(result.pwa)} PWA | ${result.pwa} |`,
|
||||
' ',
|
||||
`*Lighthouse ran on [${Object.keys(links)[0]}](${Object.keys(links)[0]})*`
|
||||
].join('\n')
|
||||
core.setOutput("comment", comment);
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Add comment to PR
|
||||
if: ${{ steps.get_preview_url.outputs.vercel_preview_url != '' }}
|
||||
id: comment_to_pr
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
with:
|
||||
number: ${{ github.event.issue.number }}
|
||||
header: lighthouse
|
||||
message: |
|
||||
${{ steps.format_lighthouse_score.outputs.comment }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
4
.github/workflows/lock.yml
vendored
@@ -3,13 +3,13 @@ on:
|
||||
schedule:
|
||||
- cron: '0 16 * * *'
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
issues: write
|
||||
concurrency:
|
||||
group: lock
|
||||
jobs:
|
||||
action:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v4
|
||||
with:
|
||||
|
||||
26
.github/workflows/npm-auto-deprecate.yml
vendored
@@ -1,26 +0,0 @@
|
||||
name: npm auto deprecate
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 1 * * *'
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
npm-auto-deprecate:
|
||||
name: npm auto deprecate
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'discordjs'
|
||||
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: Deprecate versions
|
||||
run: 'yarn npm-deprecate --name "*dev*" --package @discordjs/brokers @discordjs/builders @discordjs/collection @discordjs/core @discordjs/formatters discord.js @discordjs/next @discordjs/proxy @discordjs/rest @discordjs/util @discordjs/voice @discordjs/ws'
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
||||
16
.github/workflows/publish-dev-docker.yml
vendored
@@ -12,16 +12,13 @@ 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
|
||||
|
||||
- name: Build dependencies
|
||||
run: yarn build
|
||||
uses: ./packages/actions/src/pnpmCache
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
@@ -29,8 +26,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: docker build -f packages/proxy-container/Dockerfile -t discordjs/proxy:latest --push .
|
||||
|
||||
35
.github/workflows/publish-dev.yml
vendored
@@ -35,6 +35,8 @@ jobs:
|
||||
- package: '@discordjs/ws'
|
||||
folder: 'ws'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
@@ -42,22 +44,41 @@ 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: Check the current development version
|
||||
id: release-check
|
||||
run: |
|
||||
if [[ $(npm view ${{ matrix.package }}@dev version | grep -e "$(git rev-parse --short HEAD)") ]]; \
|
||||
then echo "RELEASE=0" >> "$GITHUB_OUTPUT"; \
|
||||
else echo "RELEASE=1" >> "$GITHUB_OUTPUT"; \
|
||||
fi
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/yarnCache
|
||||
if: steps.release-check.outputs.release == '1'
|
||||
uses: ./packages/actions/src/pnpmCache
|
||||
|
||||
- name: Build dependencies
|
||||
run: yarn build
|
||||
if: steps.release-check.outputs.release == '1'
|
||||
run: pnpm run build
|
||||
|
||||
- name: Publish package
|
||||
if: steps.release-check.outputs.release == '1'
|
||||
run: |
|
||||
yarn workspace ${{ matrix.package }} release --preid "dev.$(date +%s)-$(git rev-parse --short HEAD)"
|
||||
yarn workspace ${{ matrix.package }} npm publish --tag dev || true
|
||||
pnpm --filter=${{ matrix.package }} run release --preid "dev.$(date +%s)-$(git rev-parse --short HEAD)"
|
||||
pnpm --filter=${{ matrix.package }} publish --provenance --no-git-checks --tag dev || true
|
||||
env:
|
||||
YARN_NPM_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
||||
|
||||
- name: Deprecate prior development releases
|
||||
if: steps.release-check.outputs.release == '1'
|
||||
run: pnpm exec npm-deprecate --name "*dev*" --message "This version is deprecated. Please use a newer version." --package ${{ matrix.package }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
||||
|
||||
19
.github/workflows/publish-docker.yml
vendored
@@ -9,16 +9,13 @@ 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
|
||||
|
||||
- name: Build dependencies
|
||||
run: yarn build
|
||||
uses: ./packages/actions/src/pnpmCache
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
@@ -26,11 +23,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: docker build -f packages/proxy-container/Dockerfile -t discordjs/proxy:$(cut -d '.' -f1 <<< $(jq --raw-output '.version' packages/proxy-container/package.json)) --push .
|
||||
|
||||
14
.github/workflows/publish-release.yml
vendored
@@ -6,6 +6,8 @@ jobs:
|
||||
npm-publish:
|
||||
name: npm publish
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
@@ -14,17 +16,17 @@ 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
|
||||
uses: ./packages/actions/src/yarnCache
|
||||
uses: ./packages/actions/src/pnpmCache
|
||||
|
||||
- name: Build dependencies
|
||||
run: yarn build
|
||||
run: pnpm run build
|
||||
|
||||
- name: Extract package and semver from tag
|
||||
id: extract-tag
|
||||
@@ -34,6 +36,6 @@ jobs:
|
||||
|
||||
- name: Publish package
|
||||
run: |
|
||||
yarn workspace ${{ steps.extract-tag.outputs.subpackage == 'true' && '@discordjs/' || '' }}${{ steps.extract-tag.outputs.package }} npm publish
|
||||
pnpm --filter=${{ steps.extract-tag.outputs.subpackage == 'true' && '@discordjs/' || '' }}${{ steps.extract-tag.outputs.package }} publish --provenance --no-git-checks
|
||||
env:
|
||||
YARN_NPM_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
||||
|
||||
34
.github/workflows/tests.yml
vendored
@@ -19,45 +19,45 @@ 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
|
||||
uses: ./packages/actions/src/pnpmCache
|
||||
|
||||
- name: Build dependencies (PR)
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
run: yarn build --filter="...[origin/${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || 'main' }}]"
|
||||
run: pnpm exec turbo run build --filter="...[origin/${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || 'main' }}]" --concurrency=4
|
||||
|
||||
- name: Build dependencies (Push)
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
run: yarn build --filter="...[HEAD^1]"
|
||||
|
||||
- name: ESLint (PR)
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
run: yarn lint --filter="...[origin/${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || 'main' }}]" -- --format=compact
|
||||
|
||||
- name: ESLint (Push)
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
run: yarn lint --filter="...[HEAD^1]" -- --format=compact
|
||||
run: pnpm exec turbo run build --filter="...[HEAD^1]" --concurrency=4
|
||||
|
||||
- name: Tests (PR)
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
run: yarn test --filter="...[origin/${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || 'main' }}]"
|
||||
run: pnpm exec turbo run test --filter="...[origin/${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || 'main' }}]" --concurrency=4
|
||||
|
||||
- name: Tests (Push)
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
run: yarn test --filter="...[HEAD^1]"
|
||||
run: pnpm exec turbo run test --filter="...[HEAD^1]" --concurrency=4
|
||||
|
||||
- name: ESLint (PR)
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
run: pnpm exec turbo run lint --filter="...[origin/${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || 'main' }}]" --concurrency=4 -- --format=compact
|
||||
|
||||
- name: ESLint (Push)
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
run: pnpm exec turbo run lint --filter="...[HEAD^1]" --concurrency=4 -- --format=compact
|
||||
|
||||
- name: Docs (PR)
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
run: yarn docs --filter="...[origin/${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || 'main' }}]"
|
||||
run: pnpm exec turbo run docs --filter="...[origin/${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || 'main' }}]" --concurrency=4
|
||||
|
||||
- name: Docs (Push)
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
run: yarn docs --filter="...[HEAD^1]"
|
||||
run: pnpm exec turbo run docs --filter="...[HEAD^1]" --concurrency=4
|
||||
|
||||
- name: Upload Coverage
|
||||
if: github.repository_owner == 'discordjs'
|
||||
|
||||
25
.gitignore
vendored
@@ -1,8 +1,8 @@
|
||||
# Packages
|
||||
node_modules/
|
||||
node_modules
|
||||
|
||||
# Log files
|
||||
logs/
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
@@ -15,20 +15,29 @@ pids
|
||||
.env
|
||||
|
||||
# Dist
|
||||
dist/
|
||||
dist-docs/
|
||||
dist
|
||||
dist-docs
|
||||
|
||||
# Miscellaneous
|
||||
.tmp/
|
||||
.tmp
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
!.vscode/settings.json
|
||||
.idea/
|
||||
.idea
|
||||
.DS_Store
|
||||
.turbo
|
||||
tsconfig.tsbuildinfo
|
||||
coverage/
|
||||
out/
|
||||
coverage
|
||||
out
|
||||
package.tgz
|
||||
tsup.config.bundled*
|
||||
vitest.config.ts.timestamp*
|
||||
|
||||
# Deno
|
||||
deno.lock
|
||||
|
||||
# Bun
|
||||
bun.lockb
|
||||
|
||||
# yarn
|
||||
.pnp.*
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
yarn commitlint --edit $1
|
||||
pnpm exec commitlint --edit $1
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
yarn build:affected && yarn lint-staged
|
||||
pnpm run build:affected && pnpm exec lint-staged
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/lintstagedrc.schema.json",
|
||||
"*": "prettier --ignore-unknown --write",
|
||||
"{src/**,__tests__/**}.{mjs,js,cjs,ts,tsx}": "eslint --ext .mjs,.js,.cjs,.ts,.tsx --fix",
|
||||
"{src/**,__tests__/**}.{mjs,js,cjs,ts,tsx}": "eslint --fix",
|
||||
"src/**.ts": "vitest related --run --config ../../vitest.config.ts"
|
||||
}
|
||||
|
||||
6
.npmrc
Normal file
@@ -0,0 +1,6 @@
|
||||
auto-install-peers=false
|
||||
resolution-mode=highest
|
||||
public-hoist-pattern[]=*eslint*
|
||||
public-hoist-pattern[]=*prettier*
|
||||
public-hoist-pattern[]=*@rushstack/node-core-library*
|
||||
public-hoist-pattern[]=*jju*
|
||||
@@ -1 +1,5 @@
|
||||
CODEOWNERS
|
||||
CHANGELOG.md
|
||||
tsup.config.bundled*
|
||||
vitest.config.ts.timestamp*
|
||||
pnpm-lock.yaml
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc.json",
|
||||
"printWidth": 120,
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
|
||||
1
.vscode/extensions.json
vendored
@@ -8,7 +8,6 @@
|
||||
"eamodio.gitlens",
|
||||
"christian-kohler.npm-intellisense",
|
||||
"christian-kohler.path-intellisense",
|
||||
"antfu.unocss",
|
||||
"unifiedjs.vscode-mdx"
|
||||
]
|
||||
}
|
||||
|
||||
29
.vscode/settings.json
vendored
@@ -1,18 +1,35 @@
|
||||
{
|
||||
"eslint.workingDirectories": [{ "pattern": "./apps/*" }, { "pattern": "./packages/*" }],
|
||||
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
|
||||
"eslint.experimental.useFlatConfig": true,
|
||||
"eslint.workingDirectories": [
|
||||
{ "directory": "${workspaceFolder}" },
|
||||
{ "pattern": "./apps/*/" },
|
||||
{ "pattern": "./packages/*/" }
|
||||
],
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": true,
|
||||
"source.organizeImports": false
|
||||
"source.organizeImports": "never",
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.fixAll": "explicit"
|
||||
},
|
||||
"editor.trimAutoWhitespace": false,
|
||||
"files.associations": {
|
||||
"*.mdx": "markdown"
|
||||
"api-extractor.json": "jsonc",
|
||||
"api-extractor-docs.json": "jsonc",
|
||||
"tsconfig.json": "jsonc",
|
||||
"tsconfig.eslint.json": "jsonc",
|
||||
"tsconfig.docs.json": "jsonc"
|
||||
},
|
||||
"files.insertFinalNewline": true,
|
||||
"files.eol": "\n",
|
||||
"npm.packageManager": "yarn",
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
"npm.packageManager": "pnpm",
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"unocss.disable": true,
|
||||
"deno.enable": false,
|
||||
"deno.enablePaths": ["./packages/create-discord-bot/template/Deno"],
|
||||
"deno.lint": false,
|
||||
"deno.unstable": false,
|
||||
"deno.config": "./packages/create-discord-bot/template/Deno/deno.jsonc"
|
||||
}
|
||||
|
||||
257
.yarn/plugins/@yarnpkg/plugin-docker-build.cjs
vendored
@@ -1,257 +0,0 @@
|
||||
/* eslint-disable */
|
||||
module.exports = {
|
||||
name: '@yarnpkg/plugin-docker-build',
|
||||
factory: function (require) {
|
||||
var plugin;
|
||||
(() => {
|
||||
'use strict';
|
||||
var t = {
|
||||
d: (e, o) => {
|
||||
for (var r in o) t.o(o, r) && !t.o(e, r) && Object.defineProperty(e, r, { enumerable: !0, get: o[r] });
|
||||
},
|
||||
o: (t, e) => Object.prototype.hasOwnProperty.call(t, e),
|
||||
r: (t) => {
|
||||
'undefined' != typeof Symbol &&
|
||||
Symbol.toStringTag &&
|
||||
Object.defineProperty(t, Symbol.toStringTag, { value: 'Module' }),
|
||||
Object.defineProperty(t, '__esModule', { value: !0 });
|
||||
},
|
||||
},
|
||||
e = {};
|
||||
t.r(e), t.d(e, { default: () => u });
|
||||
const o = require('@yarnpkg/cli'),
|
||||
r = require('clipanion'),
|
||||
i = require('@yarnpkg/core'),
|
||||
a = require('@yarnpkg/plugin-patch'),
|
||||
n = require('@yarnpkg/fslib');
|
||||
const s = require('@yarnpkg/plugin-pack');
|
||||
async function c({ workspace: t, destination: e, report: o }) {
|
||||
await s.packUtils.prepareForPack(t, { report: o }, async () => {
|
||||
const r = await s.packUtils.genPackList(t),
|
||||
a = i.Report.progressViaCounter(r.length),
|
||||
c = o.reportProgress(a);
|
||||
try {
|
||||
for (const i of r) {
|
||||
const r = n.ppath.join(t.cwd, i),
|
||||
s = n.ppath.join(e, t.relativeCwd, i);
|
||||
o.reportInfo(null, i), await n.xfs.copyPromise(s, r, { overwrite: !0 }), a.tick();
|
||||
}
|
||||
} finally {
|
||||
c.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
function p(t, e) {
|
||||
const o = (0, n.toFilename)(e);
|
||||
return n.ppath.isAbsolute(o) ? n.ppath.relative(t, o) : o;
|
||||
}
|
||||
const l = /^builtin<([^>]+)>$/;
|
||||
var d = function (t, e, o, r) {
|
||||
var i,
|
||||
a = arguments.length,
|
||||
n = a < 3 ? e : null === r ? (r = Object.getOwnPropertyDescriptor(e, o)) : r;
|
||||
if ('object' == typeof Reflect && 'function' == typeof Reflect.decorate) n = Reflect.decorate(t, e, o, r);
|
||||
else
|
||||
for (var s = t.length - 1; s >= 0; s--)
|
||||
(i = t[s]) && (n = (a < 3 ? i(n) : a > 3 ? i(e, o, n) : i(e, o)) || n);
|
||||
return a > 3 && n && Object.defineProperty(e, o, n), n;
|
||||
};
|
||||
class f extends o.BaseCommand {
|
||||
constructor() {
|
||||
super(...arguments), (this.args = []);
|
||||
}
|
||||
async execute() {
|
||||
const t = await i.Configuration.find(this.context.cwd, this.context.plugins),
|
||||
{ project: e } = await i.Project.find(t, this.context.cwd),
|
||||
o = e.getWorkspaceByIdent(i.structUtils.parseIdent(this.workspaceName)),
|
||||
r = (function ({
|
||||
project: t,
|
||||
workspaces: e,
|
||||
production: o = !1,
|
||||
scopes: r = o ? ['dependencies'] : i.Manifest.hardDependencies,
|
||||
}) {
|
||||
const a = new Set([...e]);
|
||||
for (const e of a)
|
||||
for (const o of r) {
|
||||
const r = e.manifest.getForScope(o).values();
|
||||
for (const e of r) {
|
||||
const o = t.tryWorkspaceByDescriptor(e);
|
||||
o && a.add(o);
|
||||
}
|
||||
}
|
||||
for (const e of t.workspaces)
|
||||
a.has(e)
|
||||
? o && e.manifest.devDependencies.clear()
|
||||
: (e.manifest.dependencies.clear(),
|
||||
e.manifest.devDependencies.clear(),
|
||||
e.manifest.peerDependencies.clear());
|
||||
return a;
|
||||
})({ project: e, workspaces: [o], production: this.production }),
|
||||
s = await (async function (t, e = 'Dockerfile') {
|
||||
const o = (0, n.toFilename)(e);
|
||||
if (n.ppath.isAbsolute(o)) return o;
|
||||
const r = [n.ppath.join(t.cwd, o), n.ppath.join(t.project.cwd, o)];
|
||||
for (const t of r) if (await n.xfs.existsPromise(t)) return t;
|
||||
throw new Error('Dockerfile is required');
|
||||
})(o, this.dockerFilePath),
|
||||
d = await i.Cache.find(t);
|
||||
return (
|
||||
await i.StreamReport.start(
|
||||
{ configuration: t, stdout: this.context.stdout, includeLogs: !this.context.quiet },
|
||||
async (t) => {
|
||||
await t.startTimerPromise('Resolution Step', async () => {
|
||||
await e.resolveEverything({ report: t, cache: d });
|
||||
}),
|
||||
await t.startTimerPromise('Fetch Step', async () => {
|
||||
await e.fetchEverything({ report: t, cache: d });
|
||||
}),
|
||||
await n.xfs.mktempPromise(async (o) => {
|
||||
const f = n.ppath.join(o, (0, n.toFilename)('manifests')),
|
||||
u = n.ppath.join(o, (0, n.toFilename)('packs'));
|
||||
await t.startTimerPromise('Copy files', async () => {
|
||||
await (async function ({ destination: t, project: e, report: o }) {
|
||||
const r = e.configuration.get('rcFilename');
|
||||
o.reportInfo(null, r),
|
||||
await n.xfs.copyPromise(n.ppath.join(t, r), n.ppath.join(e.cwd, r), { overwrite: !0 });
|
||||
})({ destination: f, project: e, report: t }),
|
||||
await (async function ({ destination: t, project: e, report: o }) {
|
||||
const r = n.ppath.join((0, n.toFilename)('.yarn'), (0, n.toFilename)('plugins'));
|
||||
o.reportInfo(null, r),
|
||||
await n.xfs.copyPromise(n.ppath.join(t, r), n.ppath.join(e.cwd, r), { overwrite: !0 });
|
||||
})({ destination: f, project: e, report: t }),
|
||||
await (async function ({ destination: t, project: e, report: o }) {
|
||||
const r = e.configuration.get('yarnPath'),
|
||||
i = n.ppath.relative(e.cwd, r),
|
||||
a = n.ppath.join(t, i);
|
||||
o.reportInfo(null, i), await n.xfs.copyPromise(a, r, { overwrite: !0 });
|
||||
})({ destination: f, project: e, report: t }),
|
||||
await (async function ({ destination: t, workspaces: e, report: o }) {
|
||||
for (const r of e) {
|
||||
const e = n.ppath.join(r.relativeCwd, i.Manifest.fileName),
|
||||
a = n.ppath.join(t, e),
|
||||
s = {};
|
||||
r.manifest.exportTo(s),
|
||||
o.reportInfo(null, e),
|
||||
await n.xfs.mkdirpPromise(n.ppath.dirname(a)),
|
||||
await n.xfs.writeJsonPromise(a, s);
|
||||
}
|
||||
})({ destination: f, workspaces: e.workspaces, report: t }),
|
||||
await (async function ({ destination: t, report: e, project: o, parseDescriptor: r }) {
|
||||
const a = new Set();
|
||||
for (const s of o.storedDescriptors.values()) {
|
||||
const c = r(
|
||||
i.structUtils.isVirtualDescriptor(s) ? i.structUtils.devirtualizeDescriptor(s) : s,
|
||||
);
|
||||
if (!c) continue;
|
||||
const { parentLocator: p, paths: d } = c;
|
||||
for (const r of d) {
|
||||
if (l.test(r)) continue;
|
||||
if (n.ppath.isAbsolute(r)) continue;
|
||||
const i = o.getWorkspaceByLocator(p),
|
||||
s = n.ppath.join(i.relativeCwd, r);
|
||||
if (a.has(s)) continue;
|
||||
a.add(s);
|
||||
const c = n.ppath.join(i.cwd, r),
|
||||
d = n.ppath.join(t, s);
|
||||
e.reportInfo(null, s),
|
||||
await n.xfs.mkdirpPromise(n.ppath.dirname(d)),
|
||||
await n.xfs.copyFilePromise(c, d);
|
||||
}
|
||||
}
|
||||
})({
|
||||
destination: f,
|
||||
report: t,
|
||||
project: e,
|
||||
parseDescriptor: (t) => {
|
||||
if (t.range.startsWith('exec:')) {
|
||||
const e = (function (t) {
|
||||
const { params: e, selector: o } = i.structUtils.parseRange(t),
|
||||
r = n.npath.toPortablePath(o);
|
||||
return {
|
||||
parentLocator:
|
||||
e && 'string' == typeof e.locator ? i.structUtils.parseLocator(e.locator) : null,
|
||||
path: r,
|
||||
};
|
||||
})(t.range);
|
||||
if (!e || !e.parentLocator) return;
|
||||
return { parentLocator: e.parentLocator, paths: [e.path] };
|
||||
}
|
||||
if (t.range.startsWith('patch:')) {
|
||||
const { parentLocator: e, patchPaths: o } = a.patchUtils.parseDescriptor(t);
|
||||
if (!e) return;
|
||||
return { parentLocator: e, paths: o };
|
||||
}
|
||||
},
|
||||
}),
|
||||
await (async function ({ destination: t, project: e, cache: o, report: r }) {
|
||||
for (const i of o.markedFiles) {
|
||||
const o = n.ppath.relative(e.cwd, i);
|
||||
(await n.xfs.existsPromise(i)) &&
|
||||
(r.reportInfo(null, o), await n.xfs.copyPromise(n.ppath.join(t, o), i));
|
||||
}
|
||||
})({ destination: f, project: e, cache: d, report: t }),
|
||||
await (async function ({ destination: t, project: e, report: o }) {
|
||||
const r = (0, n.toFilename)(e.configuration.get('lockfileFilename')),
|
||||
i = n.ppath.join(t, r);
|
||||
o.reportInfo(null, r),
|
||||
await n.xfs.mkdirpPromise(n.ppath.dirname(i)),
|
||||
await n.xfs.writeFilePromise(i, e.generateLockfile());
|
||||
})({ destination: f, project: e, report: t }),
|
||||
this.copyFiles &&
|
||||
this.copyFiles.length &&
|
||||
(await (async function ({ destination: t, files: e, dockerFilePath: o, report: r }) {
|
||||
const i = n.ppath.dirname(o);
|
||||
for (const o of e) {
|
||||
const e = p(i, o),
|
||||
a = n.ppath.join(i, e),
|
||||
s = n.ppath.join(t, e);
|
||||
r.reportInfo(null, e), await n.xfs.copyPromise(s, a);
|
||||
}
|
||||
})({ destination: f, files: this.copyFiles, dockerFilePath: s, report: t }));
|
||||
});
|
||||
for (const e of r) {
|
||||
const o = e.manifest.name ? i.structUtils.stringifyIdent(e.manifest.name) : '';
|
||||
await t.startTimerPromise('Pack workspace ' + o, async () => {
|
||||
await c({ workspace: e, report: t, destination: u });
|
||||
});
|
||||
}
|
||||
await i.execUtils.pipevp('docker', ['build', ...this.args, '-f', s, '.'], {
|
||||
cwd: o,
|
||||
strict: !0,
|
||||
stdin: this.context.stdin,
|
||||
stdout: this.context.stdout,
|
||||
stderr: this.context.stderr,
|
||||
});
|
||||
});
|
||||
},
|
||||
)
|
||||
).exitCode();
|
||||
}
|
||||
}
|
||||
(f.usage = r.Command.Usage({
|
||||
category: 'Docker-related commands',
|
||||
description: 'Build a Docker image for a workspace',
|
||||
details:
|
||||
'\n This command will build a efficient Docker image which only contains necessary dependencies for the specified workspace.\n\n You have to create a Dockerfile in your workspace or your project. You can also specify the path to Dockerfile using the "-f, --file" option.\n\n Additional arguments can be passed to "docker build" directly, please check the Docker docs for more info: https://docs.docker.com/engine/reference/commandline/build/\n\n You can copy additional files or folders to a Docker image using the "--copy" option. This is useful for secret keys or configuration files. The files will be copied to "manifests" folder. The path can be either a path relative to the Dockerfile or an absolute path.\n ',
|
||||
examples: [
|
||||
['Build a Docker image for a workspace', 'yarn docker build @foo/bar'],
|
||||
['Pass additional arguments to docker build command', 'yarn docker build @foo/bar -t image-tag'],
|
||||
[
|
||||
'Copy additional files to a Docker image',
|
||||
'yarn docker build --copy secret.key --copy config.json @foo/bar',
|
||||
],
|
||||
['Install production dependencies only', 'yarn docker build --production @foo/bar'],
|
||||
],
|
||||
})),
|
||||
d([r.Command.String()], f.prototype, 'workspaceName', void 0),
|
||||
d([r.Command.Proxy()], f.prototype, 'args', void 0),
|
||||
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.Path('docker', 'build')], f.prototype, 'execute', null);
|
||||
const u = { commands: [f] };
|
||||
plugin = e;
|
||||
})();
|
||||
return plugin;
|
||||
},
|
||||
};
|
||||
541
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
550
.yarn/plugins/@yarnpkg/plugin-version.cjs
vendored
873
.yarn/releases/yarn-3.5.0.cjs
vendored
18
.yarnrc.yml
@@ -1,18 +0,0 @@
|
||||
nodeLinker: node-modules
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: '@yarnpkg/plugin-interactive-tools'
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
|
||||
spec: '@yarnpkg/plugin-workspace-tools'
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-version.cjs
|
||||
spec: '@yarnpkg/plugin-version'
|
||||
- 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'
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -88,9 +88,52 @@
|
||||
*
|
||||
* DEFAULT VALUE: no overrideTsconfig section
|
||||
*/
|
||||
// "overrideTsconfig": {
|
||||
// . . .
|
||||
// }
|
||||
"overrideTsconfig": {
|
||||
"compilerOptions": {
|
||||
// Type Checking
|
||||
"allowUnreachableCode": false,
|
||||
"allowUnusedLabels": false,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitOverride": true,
|
||||
"noImplicitReturns": true,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"strict": true,
|
||||
|
||||
// Modules
|
||||
"allowArbitraryExtensions": false,
|
||||
"allowImportingTsExtensions": false,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "nodenext",
|
||||
"resolveJsonModule": true,
|
||||
"resolvePackageJsonExports": false,
|
||||
"resolvePackageJsonImports": false,
|
||||
|
||||
// Emit
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"importHelpers": false,
|
||||
"newLine": "lf",
|
||||
"noEmitHelpers": true,
|
||||
"outDir": "dist",
|
||||
"removeComments": false,
|
||||
"sourceMap": true,
|
||||
|
||||
// Interop Constraints
|
||||
"esModuleInterop": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"isolatedModules": true,
|
||||
|
||||
// Language and Environment
|
||||
"experimentalDecorators": true,
|
||||
"lib": ["ESNext"],
|
||||
"target": "ES2022",
|
||||
"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 +224,7 @@
|
||||
/**
|
||||
* (REQUIRED) Whether to generate the .d.ts rollup file.
|
||||
*/
|
||||
"enabled": false
|
||||
"enabled": false,
|
||||
|
||||
/**
|
||||
* Specifies the output path for a .d.ts rollup file to be generated without any trimming.
|
||||
@@ -195,7 +238,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/.env.development
Normal file
@@ -0,0 +1 @@
|
||||
METADATA_BASE_URL=http://localhost:3000
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"extends": ["../../.eslintrc.json", "neon/react", "neon/next", "neon/edge", "@unocss", "neon/prettier"],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"react/react-in-jsx-scope": 0,
|
||||
"react/jsx-filename-extension": [1, { "extensions": [".tsx"] }]
|
||||
}
|
||||
}
|
||||
21
apps/guide/.gitignore
vendored
@@ -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
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
/** @type {import('lint-staged').Config} */
|
||||
module.exports = require('../../.lintstagedrc.json');
|
||||
@@ -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
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
/** @type {import('prettier').Config} */
|
||||
module.exports = require('../../.prettierrc.json');
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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)),
|
||||
],
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -13,12 +13,13 @@ module.exports = withBundleAnalyzer(
|
||||
withContentlayer({
|
||||
reactStrictMode: true,
|
||||
experimental: {
|
||||
appDir: true,
|
||||
typedRoutes: true,
|
||||
},
|
||||
images: {
|
||||
dangerouslyAllowSVG: true,
|
||||
contentDispositionType: 'attachment',
|
||||
contentSecurityPolicy: "default-src 'self'; frame-src 'none'; sandbox;",
|
||||
},
|
||||
poweredByHeader: false,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@discordjs/guide",
|
||||
"version": "0.1.0",
|
||||
"description": "Imagine a guide... that explores the many possibilities for your discord.js bot",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"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:next": "next build",
|
||||
"build:css": "yarn generate:css",
|
||||
"build:analyze": "cross-env-shell ANALYZE=true yarn build:prod",
|
||||
"build:check": "tsc --noEmit",
|
||||
"build:local": "pnpm run build:prod",
|
||||
"build:prod": "next build",
|
||||
"build:analyze": "cross-env ANALYZE=true pnpm run build:prod",
|
||||
"preview": "next start",
|
||||
"dev": "concurrently 'yarn dev:contentlayer' '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",
|
||||
"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"
|
||||
"dev": "next dev",
|
||||
"generate:contentlayer": "contentlayer build",
|
||||
"lint": "pnpm run build:check && prettier --check . && cross-env TIMING=1 eslint --format=pretty src",
|
||||
"format": "pnpm run build:check && prettier --write . && cross-env TIMING=1 eslint --fix --format=pretty src",
|
||||
"fmt": "pnpm run format"
|
||||
},
|
||||
"type": "commonjs",
|
||||
"directories": {
|
||||
"lib": "src"
|
||||
},
|
||||
"contributors": [
|
||||
"Crawl <icrawltogo@gmail.com>"
|
||||
],
|
||||
@@ -37,66 +36,65 @@
|
||||
],
|
||||
"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",
|
||||
"funding": "https://github.com/discordjs/discord.js?sponsor",
|
||||
"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",
|
||||
"ariakit": "^2.0.0-next.44",
|
||||
"cmdk": "^0.2.0",
|
||||
"contentlayer": "^0.3.1",
|
||||
"next": "^13.3.0",
|
||||
"next-contentlayer": "^0.3.1",
|
||||
"next-themes": "^0.2.1",
|
||||
"@vercel/analytics": "^1.2.2",
|
||||
"@vercel/edge-config": "^1.1.0",
|
||||
"@vercel/og": "^0.6.2",
|
||||
"ariakit": "2.0.0-next.44",
|
||||
"cmdk": "^1.0.0",
|
||||
"contentlayer": "^0.3.4",
|
||||
"next": "14.2.1",
|
||||
"next-contentlayer": "^0.3.4",
|
||||
"next-themes": "^0.3.0",
|
||||
"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.33.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "^13.3.0",
|
||||
"@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",
|
||||
"@next/bundle-analyzer": "14.2.1",
|
||||
"@testing-library/react": "^15.0.2",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/html-escaper": "^3.0.2",
|
||||
"@types/node": "18.18.8",
|
||||
"@types/react": "^18.2.79",
|
||||
"@types/react-dom": "^18.2.25",
|
||||
"@unocss/eslint-plugin": "^0.59.3",
|
||||
"@unocss/postcss": "^0.58.5",
|
||||
"@unocss/reset": "^0.59.3",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"@vitest/coverage-v8": "^1.5.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.38.0",
|
||||
"eslint-config-neon": "^0.1.42",
|
||||
"eslint-formatter-pretty": "^5.0.0",
|
||||
"happy-dom": "^9.7.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-neon": "^0.1.62",
|
||||
"eslint-formatter-pretty": "^6.0.1",
|
||||
"happy-dom": "^14.7.1",
|
||||
"hast-util-to-string": "^2.0.0",
|
||||
"hastscript": "^7.2.0",
|
||||
"hastscript": "^8.0.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"
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.2.5",
|
||||
"turbo": "^1.13.2",
|
||||
"typescript": "^5.4.5",
|
||||
"unocss": "^0.59.3",
|
||||
"vercel": "^34.0.0",
|
||||
"vitest": "^1.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.13.0"
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
|
||||
5
apps/guide/postcss.config.cjs
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
'@unocss/postcss': {},
|
||||
},
|
||||
};
|
||||
BIN
apps/guide/public/assets/after-sorting.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
apps/guide/public/assets/before-sorting.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
apps/guide/public/assets/bot-auth-page.png
Normal file
|
After Width: | Height: | Size: 182 KiB |
BIN
apps/guide/public/assets/bot-user.png
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
apps/guide/public/assets/create-app.png
Normal file
|
After Width: | Height: | Size: 290 KiB |
BIN
apps/guide/public/assets/integrations-tab.png
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
apps/guide/public/assets/integrations-view-tab.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
apps/guide/public/assets/snek-bot.jpeg
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
apps/guide/public/assets/webhook.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
@@ -1,9 +1,12 @@
|
||||
'use client';
|
||||
|
||||
import { Providers } from './providers';
|
||||
import { inter } from '~/util/fonts';
|
||||
import { Providers } from './providers';
|
||||
|
||||
export default function GlobalError({ error }: { error: Error }) {
|
||||
import '~/styles/cmdk.css';
|
||||
import '~/styles/main.css';
|
||||
|
||||
export default function GlobalError({ error }: { readonly error: Error }) {
|
||||
console.error(error);
|
||||
|
||||
return (
|
||||
@@ -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 (
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { allContents } from 'contentlayer/generated';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { allContents } from 'contentlayer/generated';
|
||||
import { Mdx } from '~/components/Mdx';
|
||||
|
||||
export async function generateStaticParams() {
|
||||
return allContents.map((content) => ({ slug: [content.slug] }));
|
||||
}
|
||||
|
||||
export default function Page({ params }: { 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>
|
||||
);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { Providers } from './providers';
|
||||
import Footer from '~/components/Footer';
|
||||
import Header from '~/components/Header';
|
||||
import { Nav } from '~/components/Nav';
|
||||
import { Providers } from './providers';
|
||||
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return (
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
import { Analytics } from '@vercel/analytics/react';
|
||||
import type { Metadata } from 'next';
|
||||
import type { Metadata, Viewport } from 'next';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { Providers } from './providers';
|
||||
import { DESCRIPTION } from '~/util/constants';
|
||||
import { inter, jetBrainsMono } from '~/util/fonts';
|
||||
import { Providers } from './providers';
|
||||
|
||||
import '@unocss/reset/tailwind-compat.css';
|
||||
import '~/styles/unocss.css';
|
||||
import '~/styles/cmdk.css';
|
||||
import '@code-hike/mdx/styles.css';
|
||||
import '~/styles/ch.css';
|
||||
import '~/styles/main.css';
|
||||
|
||||
export const viewport: Viewport = {
|
||||
themeColor: [
|
||||
{ media: '(prefers-color-scheme: light)', color: '#f1f3f5' },
|
||||
{ media: '(prefers-color-scheme: dark)', color: '#181818' },
|
||||
],
|
||||
colorScheme: 'light dark',
|
||||
};
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL(
|
||||
process.env.METADATA_BASE_URL ? process.env.METADATA_BASE_URL : `http://localhost:${process.env.PORT ?? 3_000}`,
|
||||
),
|
||||
title: 'discord.js',
|
||||
description: DESCRIPTION,
|
||||
viewport: {
|
||||
minimumScale: 1,
|
||||
initialScale: 1,
|
||||
width: 'device-width',
|
||||
},
|
||||
icons: {
|
||||
other: [
|
||||
{
|
||||
@@ -44,12 +48,6 @@ export const metadata: Metadata = {
|
||||
|
||||
manifest: '/site.webmanifest',
|
||||
|
||||
themeColor: [
|
||||
{ media: '(prefers-color-scheme: light)', color: '#f1f3f5' },
|
||||
{ media: '(prefers-color-scheme: dark)', color: '#181818' },
|
||||
],
|
||||
colorScheme: 'light dark',
|
||||
|
||||
appleWebApp: {
|
||||
title: 'discord.js',
|
||||
},
|
||||
|
||||
@@ -6,7 +6,7 @@ export default function NotFound() {
|
||||
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">404</h1>
|
||||
<h2 className="text-[2rem] md:text-[3rem]">Not found.</h2>
|
||||
<Link
|
||||
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded bg-blurple px-6 text-base font-semibold leading-none text-white no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-white"
|
||||
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded bg-blurple px-6 text-base text-white font-semibold leading-none no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-white"
|
||||
href="/guide"
|
||||
>
|
||||
Take me back
|
||||
|
||||
BIN
apps/guide/src/assets/powered-by-workers.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
91
apps/guide/src/components/DiscordAPITypesLink.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
|
||||
9
apps/guide/src/components/H1.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
9
apps/guide/src/components/H2.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
9
apps/guide/src/components/H3.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
9
apps/guide/src/components/H4.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
import { VscGithubInverted } from '@react-icons/all-files/vsc/VscGithubInverted';
|
||||
import { VscMenu } from '@react-icons/all-files/vsc/VscMenu';
|
||||
import { Button } from 'ariakit/button';
|
||||
import type { Route } from 'next';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
@@ -23,7 +24,7 @@ export default function Header() {
|
||||
.map((path, idx, original) => (
|
||||
<Link
|
||||
className="rounded outline-none hover:underline focus:ring focus:ring-width-2 focus:ring-blurple"
|
||||
href={`/${original.slice(0, idx + 1).join('/')}`}
|
||||
href={`/${original.slice(0, idx + 1).join('/')}` as Route}
|
||||
key={`${path}-${idx}`}
|
||||
>
|
||||
{path}
|
||||
|
||||
@@ -4,13 +4,31 @@ import { Alert, Section, DiscordMessages, DiscordMessage, DiscordMessageEmbed }
|
||||
import { useMDXComponent } from 'next-contentlayer/hooks';
|
||||
import { DocsLink } from '~/components/DocsLink';
|
||||
import { ResultingCode } from '~/components/ResultingCode';
|
||||
import { DiscordAPITypesLink } from './DiscordAPITypesLink';
|
||||
import { H1 } from './H1';
|
||||
import { H2 } from './H2';
|
||||
import { H3 } from './H3';
|
||||
import { H4 } from './H4';
|
||||
|
||||
export function Mdx({ code }: { 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,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { Scrollbars } from 'react-custom-scrollbars-2';
|
||||
import { Sidebar } from './Sidebar';
|
||||
import { useNav } from '~/contexts/nav';
|
||||
import { Sidebar } from './Sidebar';
|
||||
|
||||
export function Nav() {
|
||||
const { opened } = useNav();
|
||||
@@ -23,7 +23,9 @@ export function Nav() {
|
||||
)}
|
||||
universal
|
||||
>
|
||||
<Sidebar />
|
||||
<div className="flex flex-col gap-4 p-3">
|
||||
<Sidebar />
|
||||
</div>
|
||||
</Scrollbars>
|
||||
</nav>
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
'use client';
|
||||
|
||||
import { allContents } from 'contentlayer/generated';
|
||||
import type { Route } from 'next';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { Section } from './Section';
|
||||
import { allContents } from 'contentlayer/generated';
|
||||
import { useNav } from '~/contexts/nav';
|
||||
import { Section } from './Section';
|
||||
|
||||
const items = allContents.map((content) => ({
|
||||
title: content.title,
|
||||
@@ -31,21 +32,21 @@ export function Sidebar() {
|
||||
const { setOpened } = useNav();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3 p-3">
|
||||
<div className="flex flex-col gap-4">
|
||||
{Object.keys(itemsByCategory).map((category, idx) => (
|
||||
<Section
|
||||
buttonClassName="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-400 dark:hover:bg-dark-300 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple rounded p-3 outline-none focus:ring"
|
||||
buttonClassName="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-400 dark:hover:bg-dark-300 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple rounded p-3 outline-none focus:ring z-10"
|
||||
key={`${category}-${idx}`}
|
||||
title={category}
|
||||
>
|
||||
{itemsByCategory[category]?.map((member, index) => (
|
||||
<Link
|
||||
className={`dark:border-dark-100 border-light-800 focus:ring-width-2 focus:ring-blurple ml-5 flex flex-col border-l p-[5px] pl-6 outline-none focus:rounded focus:border-0 focus:ring ${
|
||||
className={`dark:border-dark-100 border-light-800 focus:ring-width-2 focus:ring-blurple ml-5 flex flex-col border-l first:mt-1 p-[5px] pl-6 outline-none focus:rounded focus:border-0 focus:ring ${
|
||||
decodeURIComponent(pathname ?? '') === member.href
|
||||
? 'bg-blurple text-white'
|
||||
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
|
||||
}`}
|
||||
href={member.href}
|
||||
href={member.href as Route}
|
||||
key={`${member.title}-${index}`}
|
||||
onClick={() => setOpened(false)}
|
||||
title={member.title}
|
||||
|
||||
@@ -8,15 +8,12 @@ category: Home
|
||||
If you're reading this, it probably means you want to learn how to make a bot with discord.js. Awesome! You've come to the right place.
|
||||
This guide will teach you things such as:
|
||||
|
||||
- How to get a bot [up and running](/preparations/) from scratch;
|
||||
- How to properly [create](/creating-your-bot/), [organize](/creating-your-bot/command-handling.md), and expand on your commands;
|
||||
- In-depth explanations and examples regarding popular topics (e.g. [reactions](/popular-topics/reactions.md), [embeds](/popular-topics/embeds.md), [canvas](/popular-topics/canvas.md));
|
||||
- Working with databases (e.g. [sequelize](/sequelize/) and [keyv](/keyv/));
|
||||
- Getting started with [sharding](/sharding/);
|
||||
- How to get a bot [up and running](../getting-started/starting-out) from scratch;
|
||||
- In-depth explanations regarding features and concepts of the API (e.g. [intents](../topics/intents), [threads](../topics/threads), [webhooks](../topics/webhooks));
|
||||
- And much more.
|
||||
|
||||
This guide will also cover subjects like common errors and how to solve them, keeping your code clean, setting up a proper development environment, etc.
|
||||
Sounds good? Great! Let's get started, then.
|
||||
Sounds good? Great! Let's get started.
|
||||
|
||||
## Before you begin...
|
||||
|
||||
@@ -25,11 +22,11 @@ While you _can_ make a bot with very little JavaScript and programming knowledge
|
||||
|
||||
If you don't know JavaScript but would like to learn about it, here are a few links to help get you started:
|
||||
|
||||
- [Eloquent JavaScript, a free online book](http://eloquentjavascript.net/)
|
||||
- [JavaScript.info, a modern javascript tutorial](https://javascript.info/)
|
||||
- [Codecademy's interactive JavaScript course](https://www.codecademy.com/learn/introduction-to-javascript)
|
||||
- [Nodeschool, for both JavaScript and Node.js lessons](https://nodeschool.io/)
|
||||
- [MDN's JavaScript guide and full documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
|
||||
- [Eloquent JavaScript, a free online book](http://eloquentjavascript.net)
|
||||
- [JavaScript.info, a modern javascript tutorial](https://javascript.info)
|
||||
- [Codecademy's interactive JavaScript course](https://codecademy.com/learn/introduction-to-javascript)
|
||||
- [Nodeschool, for both JavaScript and Node.js lessons](https://nodeschool.io)
|
||||
- [MDN's JavaScript guide and full documentation](https://developer.mozilla.org/docs/Web/JavaScript)
|
||||
- [Google, your best friend](https://google.com)
|
||||
|
||||
Take your pick, learn some JavaScript, and once you feel like you're confident enough to make a bot, come back and get started!
|
||||
|
||||
@@ -21,9 +21,7 @@ category: Home
|
||||
time: 'Today at 21:00',
|
||||
}}
|
||||
>
|
||||
discord.js v14 has released and the guide has been updated!
|
||||
<br />
|
||||
This includes additions and changes made in Discord, such as slash commands and message components.
|
||||
This website is new! We will no longer be updating the old guide website.
|
||||
</DiscordMessage>
|
||||
</DiscordMessages>
|
||||
|
||||
@@ -33,25 +31,9 @@ We have moved from VuePress to [Next.js](https://nextjs.org/)! The source can be
|
||||
|
||||
## Pages
|
||||
|
||||
All content has been updated to use discord.js v14 syntax. The v13 version of the guide can be found at https://v13.discordjs.guide.
|
||||
|
||||
### New
|
||||
|
||||
- [Updating from v13 to v14](/additional-info/changes-in-v14.md): A list of the changes from discord.js v13 to v14
|
||||
- [Slash commands](/interactions/slash-commands.md): Registering, replying to slash commands and permissions
|
||||
- [Buttons](/interactions/buttons.md): Building, sending, and receiving buttons
|
||||
- [Select menus](/interactions/select-menus.md): Building, sending, and receiving select menus
|
||||
- [Threads](/popular-topics/threads.md): Creating and managing threads
|
||||
- [Builders](/popular-topics/builders.md): A collection of builders to use with your bot
|
||||
|
||||
### Updated
|
||||
|
||||
- Commando: Replaced with [Sapphire](https://sapphirejs.dev/docs/Guide/getting-started/getting-started-with-sapphire)
|
||||
- [Voice](/voice/): Rewritten to use the [_`@discordjs/voice`_](https://github.com/discordjs/discord.js/tree/main/packages/voice) package
|
||||
- [Command handling](/creating-your-bot/command-handling.md/): Updated to use slash commands
|
||||
- Obsolete sections removed
|
||||
- _`client.on('message')`_ snippets updated to _`client.on(Events.InteractionCreate)`_
|
||||
- [Message content became a privileged intent on August 31, 2022](https://support-dev.discord.com/hc/articles/4404772028055)
|
||||
- Pages have been revamped to account for our new [create-discord-bot](https://github.com/discordjs/discord.js/tree/main/packages/create-discord-bot) command-line interface.
|
||||
- Popular topic are now simply "topics" that detail usage of a particular concept of the API.
|
||||
- Focus is primarily on discord.js, so irrelevant topics have been removed. It may be better to visit the documentation of the package you are using to learn how to use them.
|
||||
|
||||
<DiscordMessages rounded>
|
||||
<DiscordMessage
|
||||
|
||||
@@ -7,16 +7,16 @@ category: Home
|
||||
|
||||
Since this guide is made specifically for the discord.js community, we want to be sure to provide the most relevant and up-to-date content. We will, of course, make additions to the current pages and add new ones as we see fit, but fulfilling requests is how we know we're providing content you all want the most.
|
||||
|
||||
Requests may be as simple as "add an example to the [frequently asked questions](/popular-topics/faq.html) page", or as elaborate as "add a page regarding [sharding](/sharding/)". We'll do our best to fulfill all requests, as long as they're reasonable.
|
||||
Requests may be as simple as "add an example to the [frequently asked questions](../topics/frequently-asked-questions) page", or as elaborate as "add a page regarding [sharding](../topics/sharding)". We'll do our best to fulfill all requests, as long as they're reasonable.
|
||||
|
||||
To make a request, simply head over to [the repository's issue tracker](https://github.com/discordjs/discord.js/issues) and [create a new issue](https://github.com/discordjs/discord.js/issues/new)! Title it appropriately, and let us know exactly what you mean inside the issue description. Make sure that you've looked around the site before making a request; what you want to request might already exist!
|
||||
|
||||
<Alert title="Tip" type="success">
|
||||
<Alert title="Tip" type="info">
|
||||
Remember that you can always [fork the repository](https://github.com/discordjs/discord.js/fork) and [make a pull
|
||||
request](https://github.com/discordjs/discord.js/pulls) if you want to add anything to the guide yourself!
|
||||
</Alert>
|
||||
|
||||
We'll also get into some of the more advanced features this guide does below.
|
||||
We'll also get into some of the more advanced features this guide uses below. We recommended you have a look at the [source](https://github.com/discordjs/discord.js/blob/main/apps/guide/src/content/01-home/03-how-to-contribute.mdx) of this page to see exactly how they work.
|
||||
|
||||
## Components
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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 />
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
title: Starting out
|
||||
category: Getting started
|
||||
---
|
||||
|
||||
# Starting out
|
||||
|
||||
Our [create-discord-bot](https://github.com/discordjs/discord.js/tree/main/packages/create-discord-bot) command-line interface sets up a basic Discord bot to help you get started on your journey.
|
||||
|
||||
## Creating your bot
|
||||
|
||||
To use discord.js, you'll need to install [Node.js](https://nodejs.org), [Deno](https://deno.com), or [Bun](https://bun.sh). discord.js v14 requires Node.js v16.11.0 or higher, but the long-term support (LTS) version is always recommended. For the purposes of this guide, we will be using Node.js.
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
To check if you already have Node.js installed, run _`node --version`_ in your terminal. If it outputs _`v16.11.0`_ or
|
||||
higher, then you're good to go!
|
||||
</Alert>
|
||||
|
||||
### Windows
|
||||
|
||||
- Download from the [Node.js website](https://nodejs.org).
|
||||
- Use [fnm](https://github.com/Schniz/fnm).
|
||||
- Use [Volta](https://volta.sh).
|
||||
|
||||
### macOS
|
||||
|
||||
- Download from the [Node.js website](https://nodejs.org/).
|
||||
- Use [fnm](https://github.com/Schniz/fnm).
|
||||
- Use [Homebrew](https://formulae.brew.sh/formula/node).
|
||||
- Use [nvm](https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updating).
|
||||
- Use [Volta](https://volta.sh).
|
||||
|
||||
### Linux
|
||||
|
||||
- Visit [this page](https://nodejs.org/en/download/package-manager) to determine how you should install Node.js.
|
||||
- Use [fnm](https://github.com/Schniz/fnm).
|
||||
- Use [nvm](https://github.com/nvm-sh/nvm).
|
||||
- Use [Volta](https://volta.sh).
|
||||
|
||||
After installing Node.js, you'll be able to create a new application from your desired package manager. If you're starting out fresh, installing Node.js will also install npm, a package manager for Node.js.
|
||||
|
||||
<CH.Code lineNumbers={false} showCopyButton={true}>
|
||||
|
||||
```sh npm
|
||||
npm create discord-bot
|
||||
```
|
||||
|
||||
```sh yarn
|
||||
yarn create discord-bot
|
||||
```
|
||||
|
||||
```sh pnpm
|
||||
pnpm create discord-bot
|
||||
```
|
||||
|
||||
```sh bun
|
||||
bun create discord-bot
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
You'll be asked the directory to create the application in, as well as whether TypeScript should be used. Dependencies will automatically be installed for you. After this, you've just got your startup Discord bot template _nearly_ ready!
|
||||
|
||||
In the next section, we will explain how to create an application to interact with Discord's API.
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
title: Setting up an application
|
||||
category: Getting started
|
||||
---
|
||||
|
||||
# Setting up an application
|
||||
|
||||
You'll need to create an application on Discord's developer portal so your bot has a token to interact with Discord's API.
|
||||
|
||||
## Creating the application
|
||||
|
||||
Follow these steps:
|
||||
|
||||
1. Open the [Discord developer portal](https://discord.com/developers/applications). You'll need to be logged in.
|
||||
2. Click on the "New Application" button.
|
||||
3. Enter a name and confirm the pop-up window by clicking the "Create" button.
|
||||
- You'll need to agree to the [Developer Terms of Service](https://discord.com/developers/docs/policies-and-agreements/terms-of-service) and [Developer Policy](https://discord.com/developers/docs/policies-and-agreements/developer-policy).
|
||||
|
||||
You should see a page like this:
|
||||
|
||||

|
||||
|
||||
You can edit your application's name, description, and avatar here. Copy the application id and paste it in the .env file after _`APPLICATION_ID=`_.
|
||||
|
||||
Once you've saved your changes, move on by selecting the "Bot" tab in the left pane.
|
||||
|
||||
## Your bot's token
|
||||
|
||||
<Alert title="Important" type="danger">
|
||||
This section is critical, so pay close attention. It explains what your bot token is, as well as the security aspects
|
||||
of it.
|
||||
</Alert>
|
||||
|
||||
On the bot tab, you'll see a section like this:
|
||||
|
||||

|
||||
|
||||
In this panel, you can give your bot a snazzy avatar, set its username, and make it public or private. Your bot's token will be revealed when you press the "Reset Token" button and confirm. Once you've done this, copy it and paste it in the .env file after _`DISCORD_TOKEN=`_.
|
||||
|
||||
If you happen to lose this token at some point, you will need to come back to this page and reset it, which will reveal the new token, invalidating all old ones.
|
||||
|
||||
### Bot token explanation
|
||||
|
||||
A token is essentially your bot's password; it's what your bot uses to login to Discord. With that said, **it is vital that you do not ever share this token with anybody, purposely or accidentally**. If someone does manage to get a hold of your bot's token, they can use your bot as if it were theirs—this means they can perform malicious acts with it.
|
||||
|
||||
Tokens look like this: _`NzkyNzE1NDU0MTk2MDg4ODQy.X-hvzA.Ovy4MCQywSkoMRRclStW4xAYK7I`_ (don't worry, we immediately reset this token before even posting it here!). If it's any shorter and looks more like this: _`kxbsDRU5UfAaiO7ar9GFMHSlmTwYaIYn`_, you copied your client secret instead. Make sure to copy the token if you want your bot to work!
|
||||
|
||||
### Token leak scenario
|
||||
|
||||
Let's imagine that you have a bot on over 1,000 servers, and it took you many, many months of coding and patience to get it on that amount. Your bot's token gets leaked somewhere, and now someone else has it. That person can:
|
||||
|
||||
- Spam every server your bot is on;
|
||||
- DM spam as many users as possible;
|
||||
- Delete as many channels as possible;
|
||||
- Kick or ban as many server members as possible;
|
||||
- Make your bot leave all of the servers it has joined;
|
||||
|
||||
All that and much, much more. Sounds pretty terrible, right? So make sure to keep your bot's token as safe as possible!
|
||||
|
||||
<Alert title="Compromised tokens" type="danger">
|
||||
If your bot token has been compromised by committing it to a public repository, posting it in discord.js support etc.
|
||||
or otherwise see your bot's token in danger, return to this page and press "Reset Token". This will invalidate all old
|
||||
tokens belonging to your bot. Keep in mind that you will need to update your bot's token where you used it before.
|
||||
</Alert>
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: Adding your bot to a server
|
||||
category: Getting started
|
||||
---
|
||||
|
||||
# Adding your bot to a server
|
||||
|
||||
After you [set up an application](./setting-up-an-application), you'll notice it's not in any servers yet. So, how does that work?
|
||||
|
||||
Before you're able to see your bot in a server, you will need to add it by using an invite link.
|
||||
|
||||
## Bot invite links
|
||||
|
||||
The basic version of one such link looks like this:
|
||||
|
||||
<CH.Code lineNumbers={false}>
|
||||
|
||||
```
|
||||
https://discord.com/api/oauth2/authorize?client_id=123456789012345678&permissions=0&scope=bot
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
The structure of the URL is quite simple:
|
||||
|
||||
- _`https://discord.com/api/oauth2/authorize`_ is Discord's standard structure for authorizing an OAuth2 application (such as your bot application) for entry to a Discord server.
|
||||
- _`client_id=...`_ is to specify _which_ application you want to authorize. You'll need to replace this part with your client's id to create a valid invite link.
|
||||
- _`permissions=...`_ describes the permissions that your bot will request to be granted by default upon joining the server you are adding it to.
|
||||
- _`scope=bot`_ specifies that you want to add this application as a Discord bot with the ability to create slash commands.
|
||||
|
||||
<Alert title="Warning" type="warning">
|
||||
If you get an error message saying "Bot requires a code grant", head over to your application's settings and disable
|
||||
the "Requires OAuth2 Code Grant" option. You shouldn't enable this option unless you know why you need to.
|
||||
</Alert>
|
||||
|
||||
## Creating and using your invite link
|
||||
|
||||
To create an invite link, head back to the [developer portal](https://discord.com/developers/applications), click on your bot application, and open the OAuth2 page.
|
||||
|
||||
In the sidebar, you'll find the URL generator. Select the _`bot`_ option. Once you select the _`bot`_ option, a list of permissions will appear, allowing you to configure the permissions your bot needs.
|
||||
|
||||
Grab the link via the "Copy" button and send it in a channel in Discord. Click on the link you just sent which should reveal this:
|
||||
|
||||

|
||||
|
||||
Choose the server you want to add the bot to and click "Authorize". Congratulations! You've successfully added your bot to your Discord server.
|
||||
|
||||
At this point, you should have a Discord bot you created with [create-discord-bot](https://github.com/discordjs/discord.js/tree/main/packages/create-discord-bot) with your .env file populated and your Discord bot in a server. You are now ready to do what you like.
|
||||
@@ -0,0 +1,496 @@
|
||||
---
|
||||
title: Frequently asked questions
|
||||
category: Topics
|
||||
---
|
||||
|
||||
# Frequently asked questions
|
||||
|
||||
## Legend
|
||||
|
||||
- _`client`_ is a placeholder for the <DocsLink type="class" parent="Client" /> object:
|
||||
_`const client = new Client({ intents: [GatewayIntentBits.Guilds] });`_.
|
||||
|
||||
- _`interaction`_ is a placeholder for the <DocsLink type="class" parent="BaseInteraction" />:
|
||||
_`client.on(Events.InteractionCreate, interaction => { ... });`_.
|
||||
|
||||
- _`guild`_ is a placeholder for the <DocsLink type="class" parent="Guild" /> object:
|
||||
_`interaction.guild`_ or _`client.guilds.cache.get('id')`_
|
||||
|
||||
- _`voiceChannel`_ is a placeholder for the <DocsLink type="class" parent="VoiceChannel" />:
|
||||
_`interaction.member.voice.channel`_.
|
||||
|
||||
For a more detailed explanation of the notations commonly used in this guide, the docs, and the support server, see [here](/additional-info/notation.md).
|
||||
|
||||
## Administrative
|
||||
|
||||
### How do I ban a user?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
const user = interaction.options.getUser('target');
|
||||
await guild.members.ban(user);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
### How do I unban a user?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
const user = interaction.options.getUser('target');
|
||||
await guild.members.unban(user);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
Discord validates and resolves user ids for users not on the server in user slash command options. To retrieve and use
|
||||
the full structure from the resulting interaction, you can use the{' '}
|
||||
<DocsLink type="class" parent="CommandInteractionOptionResolver" symbol="getUser" brackets /> method.
|
||||
</Alert>
|
||||
|
||||
### How do I kick a guild member?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
const member = interaction.options.getMember('target');
|
||||
await member.kick();
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
### How do I timeout a guild member?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
const member = interaction.options.getMember('target');
|
||||
await member.timeout(60_000); // Timeout for one minute
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
Timeout durations are measured by the millisecond. The maximum timeout duration you can set is 28 days. To remove a
|
||||
timeout set on a member, pass _`null`_ instead of a timeout duration.
|
||||
</Alert>
|
||||
|
||||
### How do I add a role to a guild member?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
const role = interaction.options.getRole('role');
|
||||
const member = interaction.options.getMember('target');
|
||||
await member.roles.add(role);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
### How do I check if a guild member has a specific role?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
const role = interaction.options.getRole('role');
|
||||
const member = interaction.options.getMember('target');
|
||||
|
||||
if (member.roles.cache.has(role.id) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
### How do I limit a command to a single user?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
if (interaction.user.id === 'id') {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
## Bot Configuration and Utility
|
||||
|
||||
### How do I set my bot's username?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
await client.user.setUsername('username');
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
### How do I set my bot's avatar?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
await client.user.setAvatar('URL or path');
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
### How do I set my playing status?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
client.user.setActivity('activity');
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
### How do I set my status to "Watching/Listening to/Competing in ..."?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
import { ActivityType } from 'discord.js';
|
||||
|
||||
client.user.setActivity('activity', { type: ActivityType.Watching });
|
||||
client.user.setActivity('activity', { type: ActivityType.Listening });
|
||||
client.user.setActivity('activity', { type: ActivityType.Competing });
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
If you would like to set your activity upon startup, you can use the{' '}
|
||||
<DocsLink type="typedef" parent="ClientOptions" /> object to set the appropriate
|
||||
<DocsLink type="typedef" parent="PresenceData" />.
|
||||
</Alert>
|
||||
|
||||
### How do I make my bot display online/idle/dnd/invisible?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
client.user.setStatus('online');
|
||||
client.user.setStatus('idle');
|
||||
client.user.setStatus('dnd');
|
||||
client.user.setStatus('invisible');
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
### How do I set both status and activity in one go?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
client.user.setPresence({ activities: [{ name: 'activity' }], status: 'idle' });
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
## Miscellaneous
|
||||
|
||||
### How do I send a message to a specific channel?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
const channel = client.channels.cache.get('id');
|
||||
await channel.send('content');
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
### How do I create a post in a forum channel?
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
Currently, the only way to get tag ids is programmatically through{' '}
|
||||
<DocsLink type="class" parent="ForumChannel" symbol="availableTags" />.
|
||||
</Alert>
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
const channel = client.channels.cache.get('id');
|
||||
|
||||
await channel.threads.create({
|
||||
name: 'Post name',
|
||||
message: { content: 'Message content' },
|
||||
appliedTags: ['tagId', 'anotherTagId'],
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
### How do I DM a specific user?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
await client.users.send('id', 'content');
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
If you want to send a direct message to the user who sent the interaction, you can use _`interaction.user.send()`_.
|
||||
</Alert>
|
||||
|
||||
### How do I mention a specific user in a message?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
const user = interaction.options.getUser('target');
|
||||
await interaction.reply(`Hi, ${user}.`);
|
||||
await interaction.followUp(`Hi, <@${user.id}>.`);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
Mentions in embeds may resolve correctly in embed titles, descriptions and field values but will never notify the
|
||||
user. Other areas do not support mentions at all.
|
||||
</Alert>
|
||||
|
||||
### How do I control which users and/or roles are mentioned in a message?
|
||||
|
||||
Controlling which mentions will send a ping is done via the _`allowedMentions`_ option, which replaces _`disableMentions`_.
|
||||
|
||||
This can be set as a default in <DocsLink type="typedef" parent="ClientOptions" />, and controlled per-message sent by your bot.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
new Client({ allowedMentions: { parse: ['users', 'roles'] } });
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
Even more control can be achieved by listing specific _`users`_ or _`roles`_ to be mentioned by id, e.g.:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
await channel.send({
|
||||
content: '<@123456789012345678> <@987654321098765432> <@&102938475665748392>',
|
||||
allowedMentions: { users: ['123456789012345678'], roles: ['102938475665748392'] },
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
### How do I prompt the user for additional input?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
await interaction.reply('Please enter more input.');
|
||||
const filter = (m) => interaction.user.id === m.author.id;
|
||||
|
||||
try {
|
||||
const messages = await interaction.channel.awaitMessages({ filter, time: 60000, max: 1, errors: ['time'] });
|
||||
await interaction.followUp(`You've entered: ${messages.first().content}`);
|
||||
} catch {
|
||||
await interaction.followUp('You did not enter any input!');
|
||||
}
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
If you want to learn more about this syntax or other types of collectors, check out [this dedicated guide page for
|
||||
collectors](/popular-topics/collectors.md)!
|
||||
</Alert>
|
||||
|
||||
### How do I block a user from using my bot?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
const blockedUsers = ['id1', 'id2'];
|
||||
|
||||
client.on(Events.InteractionCreate, (interaction) => {
|
||||
if (blockedUsers.includes(interaction.user.id)) return;
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
You do not need to have a constant local variable like _`blockedUsers`_ above. If you have a database system that you
|
||||
use to store ids of blocked users, you can query the database instead.
|
||||
</Alert>
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
client.on(Events.InteractionCreate, async (interaction) => {
|
||||
const blockedUsers = await database.query('SELECT user_id FROM blocked_users;');
|
||||
if (blockedUsers.includes(interaction.user.id)) return;
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
Note that this is just a showcase of how you could do such a check.
|
||||
|
||||
### How do I react to the message my bot sent?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
const sentMessage = await interaction.channel.send('My message to react to.');
|
||||
// Unicode emoji
|
||||
await sentMessage.react('👍');
|
||||
|
||||
// Custom emoji
|
||||
await sentMessage.react('123456789012345678');
|
||||
await sentMessage.react('<emoji:123456789012345678>');
|
||||
await sentMessage.react('<a:emoji:123456789012345678>');
|
||||
await sentMessage.react('emoji:123456789012345678');
|
||||
await sentMessage.react('a:emoji:123456789012345678');
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
If you want to learn more about reactions, check out [this dedicated guide on
|
||||
reactions](/popular-topics/reactions.md)!
|
||||
</Alert>
|
||||
|
||||
### How do I restart my bot with a command?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
process.exit();
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Warning" type="warning">
|
||||
_`process.exit()`_ will only kill your Node process, but when using [PM2](https://pm2.keymetrics.io/), it will restart
|
||||
the process whenever it gets killed. You can read our guide on PM2 [here](/improving-dev-environment/pm2.md).
|
||||
</Alert>
|
||||
|
||||
### What is the difference between a User and a GuildMember?
|
||||
|
||||
A User represents a global Discord user, and a GuildMember represents a Discord user on a specific server. That means only GuildMembers can have permissions, roles, and nicknames, for example, because all of these things are server-bound information that could be different on each server that the user is in.
|
||||
|
||||
### How do I find all online members of a guild?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
// First use guild.members.fetch to make sure all members are cached
|
||||
const fetchedMembers = await guild.members.fetch({ withPresences: true });
|
||||
const totalOnline = fetchedMembers.filter((member) => member.presence?.status === 'online');
|
||||
// Now you have a collection with all online member objects in the totalOnline variable
|
||||
console.log(`There are currently ${totalOnline.size} members online in this guild!`);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Warning" type="warning">
|
||||
This only works correctly if you have the _`GuildPresences`_ intent enabled for your application and client. If you
|
||||
want to learn more about intents, check out [this dedicated guide on intents](/popular-topics/intents.md)!
|
||||
</Alert>
|
||||
|
||||
### How do I check which role was added/removed and for which member?
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
// Start by declaring a guildMemberUpdate listener
|
||||
// This code should be placed outside of any other listener callbacks to prevent listener nesting
|
||||
client.on(Events.GuildMemberUpdate, (oldMember, newMember) => {
|
||||
// If the role(s) are present on the old member object but no longer on the new one (i.e role(s) were removed)
|
||||
const removedRoles = oldMember.roles.cache.filter((role) => !newMember.roles.cache.has(role.id));
|
||||
|
||||
if (removedRoles.size > 0) {
|
||||
console.log(`The roles ${removedRoles.map((r) => r.name)} were removed from ${oldMember.displayName}.`);
|
||||
}
|
||||
|
||||
// If the role(s) are present on the new member object but are not on the old one (i.e role(s) were added)
|
||||
const addedRoles = newMember.roles.cache.filter((role) => !oldMember.roles.cache.has(role.id));
|
||||
|
||||
if (addedRoles.size > 0) {
|
||||
console.log(`The roles ${addedRoles.map((r) => r.name)} were added to ${oldMember.displayName}.`);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
### How do I check the bot's ping?
|
||||
|
||||
There are two common measurements for bot pings. The first, **websocket heartbeat**, is the average interval of a regularly sent signal indicating the healthy operation of the websocket connection the library receives events over:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
await interaction.reply(`Websocket heartbeat: ${client.ws.ping}ms.`);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
If you're using [sharding](/sharding/), a specific shard's heartbeat can be found on the WebSocketShard instance,
|
||||
accessible at _`client.ws.shards.get(id).ping`_.
|
||||
</Alert>
|
||||
|
||||
The second, **Roundtrip Latency**, describes the amount of time a full API roundtrip (from the creation of the command message to the creation of the response message) takes. You then edit the response to the respective value to avoid needing to send yet another message:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
const sent = await interaction.reply({ content: 'Pinging...', fetchReply: true });
|
||||
await interaction.editReply(`Roundtrip latency: ${sent.createdTimestamp - interaction.createdTimestamp}ms`);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
### Why do some emojis behave weirdly?
|
||||
|
||||
If you've tried using [the usual method of retrieving unicode emojis](./reactions.md#unicode-emojis), you may have noticed that some characters don't provide the expected results. Here's a short snippet that'll help with that issue. You can toss this into a file of its own and use it anywhere you need! Alternatively feel free to simply copy-paste the characters from below:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js index.js
|
||||
import { emojiCharacters } from './emojiCharacters.js';
|
||||
|
||||
console.log(emojiCharacters.a); // 🇦
|
||||
console.log(emojiCharacters[10]); // 🔟
|
||||
console.log(emojiCharacters['!']); // ❗
|
||||
```
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```js emojiCharacters.js
|
||||
export const emojiCharacters = {
|
||||
a: '🇦', b: '🇧', c: '🇨', d: '🇩',
|
||||
e: '🇪', f: '🇫', g: '🇬', h: '🇭',
|
||||
i: '🇮', j: '🇯', k: '🇰', l: '🇱',
|
||||
m: '🇲', n: '🇳', o: '🇴', p: '🇵',
|
||||
q: '🇶', r: '🇷', s: '🇸', t: '🇹',
|
||||
u: '🇺', v: '🇻', w: '🇼', x: '🇽',
|
||||
y: '🇾', z: '🇿', 0: '0️⃣', 1: '1️⃣',
|
||||
2: '2️⃣', 3: '3️⃣', 4: '4️⃣', 5: '5️⃣',
|
||||
6: '6️⃣', 7: '7️⃣', 8: '8️⃣', 9: '9️⃣',
|
||||
10: '🔟', '#': '#️⃣', '*': '*️⃣',
|
||||
'!': '❗', '?': '❓',
|
||||
};
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
You can use the <kbd>⌃ Control</kbd> <kbd>⌘ Command</kbd> <kbd>Space</kbd> keyboard shortcut to open up an emoji picker that can be used for quick, easy access to all the Unicode emojis available to you.
|
||||
|
||||
On Windows, the shortcut is <kbd>⊞</kbd> <kbd>.</kbd>.
|
||||
|
||||
</Alert>
|
||||
165
apps/guide/src/content/03-topics/02-audit-logs.mdx
Normal file
@@ -0,0 +1,165 @@
|
||||
---
|
||||
title: Audit logs
|
||||
category: Topics
|
||||
---
|
||||
|
||||
# Audit logs
|
||||
|
||||
## A Quick Background
|
||||
|
||||
Audit logs are an excellent moderation tool offered by Discord to know what happened in a server and usually by whom. Making use of audit logs requires the _`ViewAuditLog`_ permission. Audit logs may be fetched on a server, or they may be received via the gateway event <DocsLink type="class" parent="Client" symbol="e-guildAuditLogEntryCreate"/> which requires the _`GuildModeration`_ intent.
|
||||
|
||||
There are quite a few cases where you may use audit logs. This guide will limit itself to the most common use cases. Feel free to consult the [relevant Discord API page](https://discord.com/developers/docs/resources/audit-log) for more information.
|
||||
|
||||
Keep in mind that these examples explore a straightforward case and are by no means exhaustive. Their purpose is to teach you how audit logs work, and expansion of these examples is likely needed to suit your specific use case.
|
||||
|
||||
## Fetching Audit Logs
|
||||
|
||||
Let's start by glancing at the <DocsLink type="class" parent="Guild" symbol="fetchAuditLogs" brackets /> method and how to work with it. Like many discord.js methods, it returns a [Promise](../additional-info/understanding-async-await) containing the <DocsLink type="class" parent="GuildAuditLogs"/> object. This object has one property, _`entries`_, which holds a [Collection](../additional-info/collections) of <DocsLink type="class" parent="GuildAuditLogsEntry"/> objects, and consequently, the information you want to retrieve.
|
||||
|
||||
Here is the most basic fetch to look at some entries.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
const fetchedLogs = await guild.fetchAuditLogs();
|
||||
const firstEntry = fetchedLogs.entries.first();
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
Simple, right? Now, let's look at utilizing its options:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
import { AuditLogEvent } from 'discord.js';
|
||||
|
||||
const fetchedLogs = await guild.fetchAuditLogs({
|
||||
type: AuditLogEvent.InviteCreate,
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
const firstEntry = fetchedLogs.entries.first();
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
This will return the first entry where an invite was created. You used _`limit: 1`_ here to specify only one entry.
|
||||
|
||||
## Receiving Audit Logs
|
||||
|
||||
Audit logs may be received via the gateway event <DocsLink type="class" parent="Client" symbol="e-guildAuditLogEntryCreate"/>.
|
||||
This is the best way to receive audit logs if you want to monitor them. As soon as an audit log entry is created,
|
||||
your application will receive an instance of this event. A common use case is to find out _who_ did the action that
|
||||
caused the audit log event to happen.
|
||||
|
||||
### Who deleted a message?
|
||||
|
||||
One of the most common use cases for audit logs is understanding who deleted a message in a Discord server. If a user deleted another user's message, you can find out who did that as soon as you receive the corresponding audit log event.
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
Messages deleted by their author or bots (excluding bulk deletes) do not generate audit log entries.
|
||||
</Alert>
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js JavaScript
|
||||
import { AuditLogEvent, Events } from 'discord.js';
|
||||
|
||||
client.on(Events.GuildAuditLogEntryCreate, async (auditLog) => {
|
||||
// Define your variables.
|
||||
// The extra information here will be the channel.
|
||||
const { action, extra: channel, executorId, targetId } = auditLog;
|
||||
|
||||
// Check only for deleted messages.
|
||||
if (action !== AuditLogEvent.MessageDelete) return;
|
||||
|
||||
// Ensure the executor is cached.
|
||||
const executor = await client.users.fetch(executorId);
|
||||
|
||||
// Ensure the author whose message was deleted is cached.
|
||||
const target = await client.users.fetch(targetId);
|
||||
|
||||
// Log the output.
|
||||
console.log(`A message by ${target.tag} was deleted by ${executor.tag} in ${channel}.`);
|
||||
});
|
||||
```
|
||||
|
||||
```ts TypeScript
|
||||
import { AuditLogEvent, Events } from 'discord.js';
|
||||
|
||||
client.on(Events.GuildAuditLogEntryCreate, async (auditLog) => {
|
||||
// Define your variables.
|
||||
// The extra information here will be the channel.
|
||||
const { action, extra: channel, executorId, targetId } = auditLog;
|
||||
|
||||
// Check only for deleted messages.
|
||||
if (action !== AuditLogEvent.MessageDelete) return;
|
||||
|
||||
// Ensure the executor is cached. The id definitely exists.
|
||||
const executor = await client.users.fetch(executorId!);
|
||||
|
||||
// Ensure the author whose message was deleted is cached. The id definitely exists.
|
||||
const target = await client.users.fetch(targetId!);
|
||||
|
||||
// Log the output.
|
||||
console.log(`A message by ${target.tag} was deleted by ${executor.tag} in ${channel}.`);
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
With this, you now have a very simple logger telling you who deleted a message authored by another person.
|
||||
|
||||
### Who kicked a user?
|
||||
|
||||
This is very similar to the example above.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js JavaScript
|
||||
import { AuditLogEvent, Events } from 'discord.js';
|
||||
|
||||
client.on(Events.GuildAuditLogEntryCreate, async (auditLog) => {
|
||||
// Define your variables.
|
||||
const { action, executorId, targetId } = auditLog;
|
||||
|
||||
// Check only for kicked users.
|
||||
if (action !== AuditLogEvent.MemberKick) return;
|
||||
|
||||
// Ensure the executor is cached.
|
||||
const executor = await client.users.fetch(executorId);
|
||||
|
||||
// Ensure the kicked guild member is cached.
|
||||
const kickedUser = await client.users.fetch(targetId);
|
||||
|
||||
// Now you can log the output!
|
||||
console.log(`${kickedUser.tag} was kicked by ${executor.tag}.`);
|
||||
});
|
||||
```
|
||||
|
||||
```ts TypeScript
|
||||
import { AuditLogEvent, Events } from 'discord.js';
|
||||
|
||||
client.on(Events.GuildAuditLogEntryCreate, async (auditLog) => {
|
||||
// Define your variables.
|
||||
const { action, executorId, targetId } = auditLog;
|
||||
|
||||
// Check only for kicked users.
|
||||
if (action !== AuditLogEvent.MemberKick) return;
|
||||
|
||||
// Ensure the executor is cached. The id definitely exists.
|
||||
const executor = await client.users.fetch(executorId!);
|
||||
|
||||
// Ensure the kicked guild member is cached. The id definitely exists.
|
||||
const kickedUser = await client.users.fetch(targetId!);
|
||||
|
||||
// Now you can log the output!
|
||||
console.log(`${kickedUser.tag} was kicked by ${executor.tag}.`);
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
If you want to check who banned a user, it's the same example as above except the _`action`_ should be <DiscordAPITypesLink type="enum" parent="AuditLogEvent" symbol="MemberBanAdd" />. You can check the rest of the possible actions on this page.
|
||||
223
apps/guide/src/content/03-topics/03-collectors.mdx
Normal file
@@ -0,0 +1,223 @@
|
||||
---
|
||||
title: Collectors
|
||||
category: Topics
|
||||
---
|
||||
|
||||
# Collectors
|
||||
|
||||
## Message collectors
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<DocsLink type="class" parent="Collector">Collectors</DocsLink> are useful to enable your bot to obtain _additional_ input after the first command was sent. An example would be initiating a quiz, where the bot will "await" a correct response from somebody.
|
||||
|
||||
### Basic message collector
|
||||
|
||||
Let's take a look at a basic message collector:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
const collectorFilter = (message) => message.content.includes('discord');
|
||||
const collector = interaction.channel.createMessageCollector({ filter: collectorFilter, time: 15_000 });
|
||||
|
||||
collector.on('collect', (message) => {
|
||||
console.log(`Collected ${message.content}`);
|
||||
});
|
||||
|
||||
collector.on('end', (collected) => {
|
||||
console.log(`Collected ${collected.size} messages`);
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
You can provide a _`filter`_ key to the object parameter of <DocsLink type="class" parent="TextChannel" symbol="createMessageCollector" brackets />. The value to this key should be a function that returns a boolean value to indicate if this message should be collected or not. To check for multiple conditions in your filter you can connect them using [logical operators](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Expressions_and_Operators#logical_operators). If you don't provide a filter all messages in the channel the collector was started on will be collected.
|
||||
|
||||
Note that the above example uses [implicit return](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions#function_body) for the filter function and passes it to the options object using the [object property shorthand](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Object_initializer#property_definitions) notation.
|
||||
|
||||
If a message passes through the filter, it will trigger the <DocsLink type="class" parent="Collector" symbol="e-collect" /> event for the _`collector`_ you've created. This message is then passed into the event listener as _`collected`_ and the provided function is executed. In the above example, you simply log the message. Once the collector finishes collecting based on the provided end conditions the <DocsLink type="class" parent="Collector" symbol="e-end" /> event emits.
|
||||
|
||||
You can control when a collector ends by supplying additional option keys when creating a collector:
|
||||
|
||||
- _`time`_: Amount of time in milliseconds the collector should run for
|
||||
- _`max`_: Number of messages to successfully pass the filter
|
||||
- _`maxProcessed`_: Number of messages encountered (no matter the filter result)
|
||||
|
||||
The benefit of using an event-based collector over _`awaitMessages()`_ (its promise-based counterpart) is that you can do something directly after each message is collected, rather than just after the collector ended. You can also stop the collector manually by calling <DocsLink type="class" parent="Collector" symbol="stop" brackets />.
|
||||
|
||||
### Await messages
|
||||
|
||||
Using <DocsLink type="class" parent="TextChannel" symbol="awaitMessages" brackets /> can be easier if you understand [Promises](../additional-info/understanding-async-await), and it allows you to have cleaner code overall. It is essentially identical to <DocsLink type="class" parent="TextChannel" symbol="createMessageCollector" brackets />, except promisified. However, the drawback of using this method is that you cannot do things before the Promise is resolved or rejected, either by an error or completion. However, it should do for most purposes, such as awaiting the correct response in a quiz. Instead of taking their example, let's set up a basic quiz command using the _`.awaitMessages()`_ feature.
|
||||
|
||||
First, you'll need some questions and answers to choose from, so here's a basic set:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"question": "What color is the sky?",
|
||||
"answers": ["blue"]
|
||||
},
|
||||
{
|
||||
"question": "How many letters are there in the alphabet?",
|
||||
"answers": ["26", "twenty-six", "twenty six", "twentysix"]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
The provided set allows for responder error with an array of answers permitted. Ideally, it would be best to place this in a JSON file, which you can call _`quiz.json`_ for simplicity.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
import quiz from './quiz.json' assert { type: 'json' };
|
||||
|
||||
// ...
|
||||
|
||||
const item = quiz[Math.floor(Math.random() * quiz.length)];
|
||||
|
||||
const collectorFilter = (response) => {
|
||||
return item.answers.some((answer) => answer.toLowerCase() === response.content.toLowerCase());
|
||||
};
|
||||
|
||||
await interaction.reply({ content: item.question });
|
||||
|
||||
try {
|
||||
const collected = await interaction.channel.awaitMessages({
|
||||
filter: collectorFilter,
|
||||
max: 1,
|
||||
time: 30_000,
|
||||
errors: ['time'],
|
||||
});
|
||||
|
||||
await interaction.followUp(`${collected.first().author} got the correct answer!`);
|
||||
} catch {
|
||||
await interaction.followUp('Looks like nobody got the answer this time.');
|
||||
}
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
If you don't understand how _`.some()`_ works, you can read about it in more detail
|
||||
[here](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/some).
|
||||
</Alert>
|
||||
|
||||
In this filter, you iterate through the answers to find what you want. You would like to ignore the case because simple typos can happen, so you convert each answer to its lowercase form and check if it's equal to the response in lowercase form as well. In the options section, you only want to allow one answer to pass through, hence the _`max: 1`_ setting.
|
||||
|
||||
The filter looks for messages that match one of the answers in the array of possible answers to pass through the collector. The _`max`_ option (the second parameter) specifies that only a maximum of one message can go through the filter successfully before the Promise successfully resolves. The _`errors`_ section specifies that time will cause it to error out, which will cause the Promise to reject if one correct answer is not received within the time limit of one minute. As you can see, there is no _`collect`_ event, so you are limited in that regard.
|
||||
|
||||
## Reaction collectors
|
||||
|
||||
### Basic reaction collector
|
||||
|
||||
These work quite similarly to message collectors, except that you apply them on a message rather than a channel. This example uses the <DocsLink type="class" parent="Message" symbol="createReactionCollector" brackets /> method. The filter will check for the 👍 emoji–in 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).
|
||||
95
apps/guide/src/content/03-topics/04-formatters.mdx
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: Formatters
|
||||
category: Topics
|
||||
---
|
||||
|
||||
# Formatters
|
||||
|
||||
discord.js provides the <DocsLink package="formatters" /> package which contains a variety of utilities you can use when writing your Discord bot.
|
||||
|
||||
## Basic Markdown
|
||||
|
||||
These functions format strings into all the different markdown styles supported by Discord.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
import { bold, italic, strikethrough, underscore, spoiler, quote, blockQuote } from 'discord.js';
|
||||
|
||||
const string = 'Hello!';
|
||||
const boldString = bold(string);
|
||||
const italicString = italic(string);
|
||||
const strikethroughString = strikethrough(string);
|
||||
const underscoreString = underscore(string);
|
||||
const spoilerString = spoiler(string);
|
||||
const quoteString = quote(string);
|
||||
const blockquoteString = blockQuote(string);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
## Links
|
||||
|
||||
There are also two functions to format hyperlinks. _`hyperlink()`_ will format the URL into a masked markdown link, and _`hideLinkEmbed()`_ will wrap the URL in _`<>`_, preventing it from embedding.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
import { hyperlink, hideLinkEmbed } from 'discord.js';
|
||||
|
||||
const url = 'https://discord.js.org/';
|
||||
const link = hyperlink('discord.js', url);
|
||||
const hiddenEmbed = hideLinkEmbed(url);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
## Code blocks
|
||||
|
||||
You can use _`inlineCode()`_ and _`codeBlock()`_ to turn a string into an inline code block or a regular code block with or without syntax highlighting.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
import { inlineCode, codeBlock } from 'discord.js';
|
||||
|
||||
const jsString = 'const value = true;';
|
||||
const inline = inlineCode(jsString);
|
||||
const codeblock = codeBlock(jsString);
|
||||
const highlighted = codeBlock('js', jsString);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
## Timestamps
|
||||
|
||||
With _`time()`_, you can format Unix timestamps and dates into a Discord time string.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
import { time, TimestampStyles } from 'discord.js';
|
||||
|
||||
const date = new Date();
|
||||
const timeString = time(date);
|
||||
const relative = time(date, TimestampStyles.RelativeTime);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
## Mentions
|
||||
|
||||
_`userMention()`_, _`channelMention()`_, and _`roleMention()`_ all exist to format Snowflakes into mentions.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
import { channelMention, roleMention, userMention } from 'discord.js';
|
||||
|
||||
const id = '123456789012345678';
|
||||
const channel = channelMention(id);
|
||||
const role = roleMention(id);
|
||||
const user = userMention(id);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
68
apps/guide/src/content/03-topics/05-intents.mdx
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: Intents
|
||||
category: Topics
|
||||
---
|
||||
|
||||
# Intents
|
||||
|
||||
Intents are an important part of establishing a WebSocket connection, as they define behavior regarding gateway events and impact received data via the REST API.
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import { Client, GatewayIntentBits } from 'discord.js';
|
||||
|
||||
const client = new Client({
|
||||
intents: [GatewayIntentBits.Guilds],
|
||||
});
|
||||
```
|
||||
|
||||
This is the most basic usage of intents for discord.js. By specifying _`GatewayIntentBits.Guilds`_, your bot will receive gateway events regarding guilds. This includes receiving initial information about guilds it is in at startup, such as role data.
|
||||
|
||||
You can find the full list of _`GatewayIntentBits`_ <DiscordAPITypesLink type="enum" parent="GatewayIntentBits">on the documentation</DiscordAPITypesLink> and an explanation of what each intent does [on Discord's API documentation](https://discord.com/developers/docs/topics/gateway#list-of-intents).
|
||||
|
||||
## Considerations
|
||||
|
||||
In discord.js, some intents require an extra bit of consideration.
|
||||
|
||||
### _`GatewayIntentBits.Guilds`_
|
||||
|
||||
discord.js relies heavily on caching in the library. We recommend you set at least the _`GatewayIntentBits.Guilds`_ intent to avoid these pitfalls.
|
||||
|
||||
### _`GatewayIntentBits.GuildMembers`_
|
||||
|
||||
Fetching members in a guild via <DocsLink type="class" parent="GuildMemberManager" symbol="fetch" brackets /> requests them over the gateway. As such, this intent is required and you may receive a timeout error if this intent is not specified.
|
||||
|
||||
<Alert title="Info" type="info">
|
||||
This is a privileged intent. Read on for more information.
|
||||
</Alert>
|
||||
|
||||
### _`GatewayIntentBits.DirectMessages`_
|
||||
|
||||
This intent is required to receive direct messages. In discord.js however, you **must** specify partials as well. See the partials topic on how this is done.
|
||||
|
||||
### _`GatewayIntentBits.MessageContent`_
|
||||
|
||||
Unlike other intents, this only populates user-generated fields. See [Discord's documentation](https://discord.com/developers/docs/topics/gateway#message-content-intent) on what exactly this intent unveils.
|
||||
|
||||
It is a common mistake to not see the message content in a message—this is usually because this intent is not specified.
|
||||
|
||||
<Alert title="Info" type="info">
|
||||
This is a privileged intent. Read on for more information.
|
||||
</Alert>
|
||||
|
||||
## Privileged intents
|
||||
|
||||
Some gateway events are considered privileged. Currently, these are:
|
||||
|
||||
- _`GatewayIntentBits.GuildPresences`_
|
||||
- _`GatewayIntentBits.GuildMembers`_
|
||||
- _`GatewayIntentBits.MessageContent`_
|
||||
|
||||
To use these intents, you will need to enable them in the developer portal. If your bot is in over 75 guilds, you will need to verify it and request usage of your desired intents.
|
||||
|
||||
Carefully think if you need these intents. They are opt-in so users across the platform can enjoy a higher level of privacy. Presences can expose some personal information, such as the games being played and overall online time. You might find that it isn't necessary for your bot to have this level of information about all guild members at all times.
|
||||
|
||||
### Disallowed intents
|
||||
|
||||
Should you receive an error stating you are using disallowed intents, please review your developer dashboard settings for all privileged intents you use. Check the Discord API documentation for up-to-date information.
|
||||
202
apps/guide/src/content/03-topics/06-threads.mdx
Normal file
@@ -0,0 +1,202 @@
|
||||
---
|
||||
title: Threads
|
||||
category: Topics
|
||||
---
|
||||
|
||||
# Threads
|
||||
|
||||
Threads can be thought of as temporary sub-channels inside an existing channel to help better organize conversations in a busy channel.
|
||||
|
||||
## Thread related gateway events
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
You can use the <DocsLink type="class" parent="BaseChannel" symbol="isThread" brackets /> type guard to make sure a
|
||||
channel is a <DocsLink type="class" parent="ThreadChannel" />!
|
||||
</Alert>
|
||||
|
||||
Threads introduce a number of new gateway events, which are listed below:
|
||||
|
||||
- <DocsLink type="class" parent="Client" symbol="e-threadCreate" />: Emitted whenever a thread is created or when the
|
||||
client user is added to a thread.
|
||||
- <DocsLink type="class" parent="Client" symbol="e-threadDelete" />: Emitted whenever a thread is deleted.
|
||||
- <DocsLink type="class" parent="Client" symbol="e-threadUpdate" />: Emitted whenever a thread is updated (e.g. name
|
||||
change, archive state change, locked state change).
|
||||
- <DocsLink type="class" parent="Client" symbol="e-threadListSync" />: Emitted whenever the client user gains access to
|
||||
a text or announcement channel that contains threads.
|
||||
- <DocsLink type="class" parent="Client" symbol="e-threadMembersUpdate" />: Emitted whenever members are added or
|
||||
removed from a thread. Requires <code>GuildMembers</code> privileged intent.
|
||||
- <DocsLink type="class" parent="Client" symbol="e-threadMemberUpdate" />: Emitted whenever the client user's thread
|
||||
member is updated.
|
||||
|
||||
## Creating and deleting threads
|
||||
|
||||
Threads are created and deleted using the <DocsLink type="class" parent="GuildTextThreadManager" /> of a text or announcement channel.
|
||||
To create a thread, you call the <DocsLink type="class" parent="GuildTextThreadManager" symbol="create" brackets /> method:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
import { ThreadAutoArchiveDuration } from 'discord.js';
|
||||
|
||||
const thread = await channel.threads.create({
|
||||
name: 'food-talk',
|
||||
autoArchiveDuration: ThreadAutoArchiveDuration.OneHour,
|
||||
reason: 'Needed a separate thread for food',
|
||||
});
|
||||
|
||||
console.log(`Created thread: ${thread.name}`);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
They can also be created from an existing message with the <DocsLink type="class" parent="Message" symbol="startThread" brackets /> method, but will be "orphaned" if that message is deleted.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js focus=3[22:42]
|
||||
import { ThreadAutoArchiveDuration } from 'discord.js';
|
||||
|
||||
const thread = await message.startThread({
|
||||
name: 'food-talk',
|
||||
autoArchiveDuration: ThreadAutoArchiveDuration.OneHour,
|
||||
reason: 'Needed a separate thread for food',
|
||||
});
|
||||
|
||||
console.log(`Created thread: ${thread.name}`);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
The created thread and the message it originated from will share the same id. The type of thread created matches the parent channel's type.
|
||||
|
||||
To delete a thread, use the <DocsLink type="class" parent="ThreadChannel" symbol="delete" brackets /> method:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js focus=2
|
||||
const thread = channel.threads.cache.find((x) => x.name === 'food-talk');
|
||||
if (thread.manageable) await thread.delete();
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
## Joining and leaving threads
|
||||
|
||||
To subscribe your client to a thread, use the <DocsLink type="class" parent="ThreadChannel" symbol="join" brackets /> method:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js focus=2
|
||||
const thread = channel.threads.cache.find((x) => x.name === 'food-talk');
|
||||
if (thread.joinable) await thread.join();
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
And to leave one, use the <DocsLink type="class" parent="ThreadChannel" symbol="leave" brackets /> method:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js focus=2
|
||||
const thread = channel.threads.cache.find((x) => x.name === 'food-talk');
|
||||
await thread.leave();
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
## Archiving, unarchiving, and locking threads
|
||||
|
||||
A thread can be either active or archived. Changing a thread from archived to active is referred to as unarchiving the thread. Threads that have _`locked`_ set to _`true`_ can only be unarchived by a member with the _`ManageThreads`_ permission.
|
||||
|
||||
Threads are automatically archived after inactivity. "Activity" is defined as sending a message, unarchiving a thread, or changing the auto-archive time.
|
||||
|
||||
To archive or unarchive a thread, use the <DocsLink type="class" parent="ThreadChannel" symbol="setArchived" brackets /> method and pass in a boolean parameter:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js focus=2,3
|
||||
const thread = channel.threads.cache.find((x) => x.name === 'food-talk');
|
||||
await thread.setArchived(true); // Archived.
|
||||
await thread.setArchived(false); // Unarchived.
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
This same principle applies to locking and unlocking a thread via the <DocsLink type="class" parent="ThreadChannel" symbol="setLocked" brackets /> method:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js focus=2,3
|
||||
const thread = channel.threads.cache.find((x) => x.name === 'food-talk');
|
||||
await thread.setLocked(true); // Locked.
|
||||
await thread.setLocked(false); // Unlocked.
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
## Private threads
|
||||
|
||||
Public threads are viewable by everyone who can view the parent channel of the thread. Private threads, however, are only viewable to those who are invited or have the _`ManageThreads`_ permission. Private threads can only be created on text channels.
|
||||
|
||||
To create a private thread, use the <DocsLink type="class" parent="GuildTextThreadManager" symbol="create" brackets /> method and pass in _`ChannelType.PrivateThread`_ as the _`type`_:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js focus=1[10:21],6
|
||||
import { ChannelType, ThreadAutoArchiveDuration } from 'discord.js';
|
||||
|
||||
const thread = await channel.threads.create({
|
||||
name: 'mod-talk',
|
||||
autoArchiveDuration: ThreadAutoArchiveDuration.OneHour,
|
||||
type: ChannelType.PrivateThread,
|
||||
reason: 'Needed a separate thread for moderation',
|
||||
});
|
||||
|
||||
console.log(`Created thread: ${thread.name}`);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
## Adding and removing members
|
||||
|
||||
You can add members to a thread with the <DocsLink type="class" parent="ThreadMemberManager" symbol="add" brackets /> method. The thread must be unarchived and you must be able to send messages in it.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js focus=2
|
||||
const thread = channel.threads.cache.find((x) => x.name === 'food-talk');
|
||||
await thread.members.add('12345678901234567');
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
You can remove members from a thread with the <DocsLink type="class" parent="ThreadMemberManager" symbol="remove" brackets /> method. The thread must be unarchived and you must have the _`ManageThreads`_ permission unless the thread is private and you are the owner of it.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js focus=2
|
||||
const thread = channel.threads.cache.find((x) => x.name === 'food-talk');
|
||||
await thread.members.remove('12345678901234567');
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
## Sending messages to threads with webhooks
|
||||
|
||||
It is possible for a webhook built on the parent channel to send messages to the channel's threads. For the purpose of this example, it is assumed a single webhook already exists for that channel. If you wish to learn more about webhooks, see our [webhook guide](./webhooks).
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js focus=4:7
|
||||
const webhooks = await channel.fetchWebhooks();
|
||||
const webhook = webhooks.first();
|
||||
|
||||
await webhook.send({
|
||||
content: "Look ma! I'm in a thread!",
|
||||
threadId: '123456789012345678',
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
And that's it! Now you know all there is to know on working with threads using discord.js!
|
||||
227
apps/guide/src/content/03-topics/07-webhooks.mdx
Normal file
@@ -0,0 +1,227 @@
|
||||
---
|
||||
title: Webhooks
|
||||
category: Topics
|
||||
---
|
||||
|
||||
# Webhooks
|
||||
|
||||
Webhooks can send messages to a text channel without having to log in as a bot. They can also fetch, edit, and delete their own messages. There are a variety of methods in discord.js to interact with webhooks. In this section, you will learn how to create, fetch, edit, and use webhooks.
|
||||
|
||||
## What is a webhook
|
||||
|
||||
Webhooks are a utility used to send messages to text channels without needing a Discord application. Webhooks are useful for allowing something to send messages without requiring a Discord application. You can also directly edit or delete messages you sent through the webhook. There are two structures to make use of this functionality: <DocsLink type="class" parent="Webhook" /> and <DocsLink type="class" parent="WebhookClient" />. _`WebhookClient`_ is an extended version of a _`Webhook`_, which allows you to send messages through it without needing a bot client.
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
If you would like to read about using webhooks through the API without discord.js, you can read about them
|
||||
[here](https://discord.com/developers/docs/resources/webhook).
|
||||
</Alert>
|
||||
|
||||
## Detecting webhook messages
|
||||
|
||||
Bots receive webhook messages in a text channel as usual. You can detect if a webhook sent the message by checking if the _`Message.webhookId`_ is not _`null`_. In this example, we return if a webhook sent the message.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
if (message.webhookId) return;
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
If you would like to get the webhook object that sent the message, you can use <DocsLink type="class" parent="Message" symbol="fetchWebhook" brackets />.
|
||||
|
||||
## Fetching webhooks
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
Webhook fetching will always make use of collections and promises. If you do not understand either concept, revise
|
||||
them, and then come back to this section. You can read about collections [here](../additional-info/collections), and
|
||||
promises [here](../additional-info/understanding-async-await) and
|
||||
[here](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Using_promises).
|
||||
</Alert>
|
||||
|
||||
### Fetching all webhooks of a guild
|
||||
|
||||
If you would like to get all webhooks of a guild, you can use the <DocsLink type="class" parent="Guild" symbol="fetchWebhooks" brackets /> method. This will return a promise which will resolve into a collection of webhooks.
|
||||
|
||||
### Fetching webhooks of a channel
|
||||
|
||||
Webhooks belonging to a channel can be fetched using the <DocsLink type="class" parent="TextChannel" symbol="fetchWebhooks" brackets /> method. This will return a promise which will resolve into a collection of webhooks. A collection will be returned even if the channel contains a single webhook. If you are certain the channel contains a single webhook, you can use the <DocsLink package="collection" type="Class" parent="Collection" symbol="first" brackets /> method on the collection to get the webhook.
|
||||
|
||||
### Fetching a single webhook
|
||||
|
||||
#### Using client
|
||||
|
||||
You can fetch a specific webhook using its _`id`_ with the <DocsLink type="class" parent="Client" symbol="fetchWebhook" brackets /> method. You can obtain the webhook id by looking at its URL: the number after _`https://discord.com/api/webhooks/`_ is the _`id`_ and the part after that is the _`token`_.
|
||||
|
||||
#### Using the WebhookClient constructor
|
||||
|
||||
If you are not using a bot client, you can get a webhook by creating a new instance of _`WebhookClient`_ and passing the _`id`_ and _`token`_ into the constructor. These credentials do not require you to have a bot application, but it also offers limited information instead of fetching it using an authorized client.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
const webhookClient = new WebhookClient({ id: 'id', token: 'token' });
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
You can also pass in just a _`url`_:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
const webhookClient = new WebhookClient({ url: 'https://discord.com/api/webhooks/id/token' });
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
## Creating webhooks
|
||||
|
||||
### Creating webhooks through server settings
|
||||
|
||||
You can create webhooks directly through the Discord client. Go to Server Settings, and you will see an _`Integrations`_ tab.
|
||||
|
||||

|
||||
|
||||
If you already have created a webhook, the webhooks tab will look like this; you will need to click the _`View Webhooks`_ button.
|
||||
|
||||

|
||||
|
||||
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 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>
|
||||