mirror of
https://github.com/tiramisulabs/seyfert.git
synced 2025-07-01 20:46:08 +00:00
Seyfert 2.0 (#208)
* feat: permissible handlers Co-authored-by: MARCROCK22 <MARCROCK22@users.noreply.github.com> * feat: init handle command * feat: unifique interaction/message (not full tested) * fix: await * fix: components handler * fix: console.log * feat: init transformers * fix: xd * fix: check * chore: apply formatting * chore: frozen-lockfile * fix: use pnpm v9 * fix: use pnpm v9 * fix: guildCreate emits when bot has more than 1 shard * feat: update cache adapter * fix: types * fix: limitedAdapter messages and bans support * fix: yes * feat: transformers (huge update) * fix: pnpm * feat: transformers & handleCommand methods * feat(resolveCommandFromContent): for handle content of getCommandFrom Content and argsContent * fix: use raw * fix: consistency * fix: return await * chore: export transformers * fix: socram code * fix: handleCommand & types * fix: events --------- Co-authored-by: MARCROCK22 <MARCROCK22@users.noreply.github.com> Co-authored-by: MARCROCK22 <marcos22dev@gmail.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: douglas546899 <douglas546899@gmail.com> Co-authored-by: Aarón Rafael <69669283+Chewawi@users.noreply.github.com> Co-authored-by: MARCROCK22 <57925328+MARCROCK22@users.noreply.github.com>
This commit is contained in:
parent
017ccfc3bf
commit
026805bcb2
1
.github/workflows/check.yml
vendored
1
.github/workflows/check.yml
vendored
@ -41,3 +41,4 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
HUSKY: 0
|
||||
|
||||
|
6
.github/workflows/publish.yml
vendored
6
.github/workflows/publish.yml
vendored
@ -19,12 +19,12 @@ jobs:
|
||||
node-version: '20.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 8
|
||||
version: 9
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Create Release Pull Request
|
||||
uses: changesets/action@v1
|
||||
|
6
.github/workflows/transpile.yml
vendored
6
.github/workflows/transpile.yml
vendored
@ -22,12 +22,12 @@ jobs:
|
||||
node-version: '20.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- uses: pnpm/action-setup@v3
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 8
|
||||
version: 9
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: npx tsc
|
||||
|
10
package.json
10
package.json
@ -21,10 +21,10 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"chokidar": "^3.6.0",
|
||||
"discord-api-types": "^0.37.88",
|
||||
"discord-api-types": "^0.37.90",
|
||||
"magic-bytes.js": "^1.10.0",
|
||||
"ts-mixer": "^6.0.4",
|
||||
"ws": "^8.17.0"
|
||||
"ws": "^8.17.1"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.ts": [
|
||||
@ -35,11 +35,11 @@
|
||||
"@biomejs/biome": "1.8.1",
|
||||
"@commitlint/cli": "^19.3.0",
|
||||
"@commitlint/config-conventional": "^19.2.2",
|
||||
"@types/node": "^20.14.2",
|
||||
"@types/node": "^20.14.6",
|
||||
"@types/ws": "^8.5.10",
|
||||
"husky": "^9.0.11",
|
||||
"lint-staged": "^15.2.6",
|
||||
"typescript": "^5.4.5"
|
||||
"lint-staged": "^15.2.7",
|
||||
"typescript": "^5.5.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"ioredis": "^5.4.1",
|
||||
|
94
pnpm-lock.yaml
generated
94
pnpm-lock.yaml
generated
@ -12,8 +12,8 @@ importers:
|
||||
specifier: ^3.6.0
|
||||
version: 3.6.0
|
||||
discord-api-types:
|
||||
specifier: ^0.37.88
|
||||
version: 0.37.88
|
||||
specifier: ^0.37.90
|
||||
version: 0.37.90
|
||||
magic-bytes.js:
|
||||
specifier: ^1.10.0
|
||||
version: 1.10.0
|
||||
@ -21,8 +21,8 @@ importers:
|
||||
specifier: ^6.0.4
|
||||
version: 6.0.4
|
||||
ws:
|
||||
specifier: ^8.17.0
|
||||
version: 8.17.0
|
||||
specifier: ^8.17.1
|
||||
version: 8.17.1
|
||||
optionalDependencies:
|
||||
ioredis:
|
||||
specifier: ^5.4.1
|
||||
@ -39,13 +39,13 @@ importers:
|
||||
version: 1.8.1
|
||||
'@commitlint/cli':
|
||||
specifier: ^19.3.0
|
||||
version: 19.3.0(@types/node@20.14.2)(typescript@5.4.5)
|
||||
version: 19.3.0(@types/node@20.14.6)(typescript@5.5.2)
|
||||
'@commitlint/config-conventional':
|
||||
specifier: ^19.2.2
|
||||
version: 19.2.2
|
||||
'@types/node':
|
||||
specifier: ^20.14.2
|
||||
version: 20.14.2
|
||||
specifier: ^20.14.6
|
||||
version: 20.14.6
|
||||
'@types/ws':
|
||||
specifier: ^8.5.10
|
||||
version: 8.5.10
|
||||
@ -53,11 +53,11 @@ importers:
|
||||
specifier: ^9.0.11
|
||||
version: 9.0.11
|
||||
lint-staged:
|
||||
specifier: ^15.2.6
|
||||
version: 15.2.6
|
||||
specifier: ^15.2.7
|
||||
version: 15.2.7
|
||||
typescript:
|
||||
specifier: ^5.4.5
|
||||
version: 5.4.5
|
||||
specifier: ^5.5.2
|
||||
version: 5.5.2
|
||||
|
||||
packages:
|
||||
|
||||
@ -201,8 +201,8 @@ packages:
|
||||
'@types/conventional-commits-parser@5.0.0':
|
||||
resolution: {integrity: sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==}
|
||||
|
||||
'@types/node@20.14.2':
|
||||
resolution: {integrity: sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==}
|
||||
'@types/node@20.14.6':
|
||||
resolution: {integrity: sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==}
|
||||
|
||||
'@types/ws@8.5.10':
|
||||
resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
|
||||
@ -362,8 +362,8 @@ packages:
|
||||
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
|
||||
engines: {node: '>=0.10'}
|
||||
|
||||
discord-api-types@0.37.88:
|
||||
resolution: {integrity: sha512-Yrj5S3JXzouPc6WLA8svgXCw3Q7IkNdW/Y/IfkJF+CsgUBsgECQ8qVoOaseJJ8Atj2TEvabut4rGmzq6PRi1/Q==}
|
||||
discord-api-types@0.37.90:
|
||||
resolution: {integrity: sha512-lpOJSGrqHuXoM4FV/2HtjoaJpCClGFHpRNIdZEW8zPINlsCHNSfIwA2EQ8dxeE6k1QhhTuM9ZlOGVYXoU7FLgA==}
|
||||
|
||||
dot-prop@5.3.0:
|
||||
resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==}
|
||||
@ -540,13 +540,13 @@ packages:
|
||||
lines-and-columns@1.2.4:
|
||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
|
||||
lint-staged@15.2.6:
|
||||
resolution: {integrity: sha512-M/3PdijFXT/A5lnbSK3EQNLbIIrkE00JZaD39r7t4kfFOqT1Ly9LgSZSMMtvQ3p2/C8Nyj/ou0vkNHmEwqoB8g==}
|
||||
lint-staged@15.2.7:
|
||||
resolution: {integrity: sha512-+FdVbbCZ+yoh7E/RosSdqKJyUM2OEjTciH0TFNkawKgvFp1zbGlEC39RADg+xKBG1R4mhoH2j85myBQZ5wR+lw==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
hasBin: true
|
||||
|
||||
listr2@8.2.1:
|
||||
resolution: {integrity: sha512-irTfvpib/rNiD637xeevjO2l3Z5loZmuaRi0L0YE5LfijwVY96oyVn0DFD3o/teAok7nfobMG1THvvcHh/BP6g==}
|
||||
listr2@8.2.2:
|
||||
resolution: {integrity: sha512-sy0dq+JPS+RAFiFk2K8Nbub7khNmeeoFALNUJ4Wzk34wZKAzaOhEXqGWs4RA5aui0RaM6Hgn7VEKhCj0mlKNLA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
locate-path@7.2.0:
|
||||
@ -710,8 +710,8 @@ packages:
|
||||
resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
rfdc@1.3.1:
|
||||
resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==}
|
||||
rfdc@1.4.1:
|
||||
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
|
||||
|
||||
semver@7.6.2:
|
||||
resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==}
|
||||
@ -793,8 +793,8 @@ packages:
|
||||
tweetnacl@1.0.3:
|
||||
resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==}
|
||||
|
||||
typescript@5.4.5:
|
||||
resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==}
|
||||
typescript@5.5.2:
|
||||
resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
@ -825,8 +825,8 @@ packages:
|
||||
resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
ws@8.17.0:
|
||||
resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==}
|
||||
ws@8.17.1:
|
||||
resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
@ -909,11 +909,11 @@ snapshots:
|
||||
'@biomejs/cli-win32-x64@1.8.1':
|
||||
optional: true
|
||||
|
||||
'@commitlint/cli@19.3.0(@types/node@20.14.2)(typescript@5.4.5)':
|
||||
'@commitlint/cli@19.3.0(@types/node@20.14.6)(typescript@5.5.2)':
|
||||
dependencies:
|
||||
'@commitlint/format': 19.3.0
|
||||
'@commitlint/lint': 19.2.2
|
||||
'@commitlint/load': 19.2.0(@types/node@20.14.2)(typescript@5.4.5)
|
||||
'@commitlint/load': 19.2.0(@types/node@20.14.6)(typescript@5.5.2)
|
||||
'@commitlint/read': 19.2.1
|
||||
'@commitlint/types': 19.0.3
|
||||
execa: 8.0.1
|
||||
@ -960,15 +960,15 @@ snapshots:
|
||||
'@commitlint/rules': 19.0.3
|
||||
'@commitlint/types': 19.0.3
|
||||
|
||||
'@commitlint/load@19.2.0(@types/node@20.14.2)(typescript@5.4.5)':
|
||||
'@commitlint/load@19.2.0(@types/node@20.14.6)(typescript@5.5.2)':
|
||||
dependencies:
|
||||
'@commitlint/config-validator': 19.0.3
|
||||
'@commitlint/execute-rule': 19.0.0
|
||||
'@commitlint/resolve-extends': 19.1.0
|
||||
'@commitlint/types': 19.0.3
|
||||
chalk: 5.3.0
|
||||
cosmiconfig: 9.0.0(typescript@5.4.5)
|
||||
cosmiconfig-typescript-loader: 5.0.0(@types/node@20.14.2)(cosmiconfig@9.0.0(typescript@5.4.5))(typescript@5.4.5)
|
||||
cosmiconfig: 9.0.0(typescript@5.5.2)
|
||||
cosmiconfig-typescript-loader: 5.0.0(@types/node@20.14.6)(cosmiconfig@9.0.0(typescript@5.5.2))(typescript@5.5.2)
|
||||
lodash.isplainobject: 4.0.6
|
||||
lodash.merge: 4.6.2
|
||||
lodash.uniq: 4.5.0
|
||||
@ -1025,15 +1025,15 @@ snapshots:
|
||||
|
||||
'@types/conventional-commits-parser@5.0.0':
|
||||
dependencies:
|
||||
'@types/node': 20.14.2
|
||||
'@types/node': 20.14.6
|
||||
|
||||
'@types/node@20.14.2':
|
||||
'@types/node@20.14.6':
|
||||
dependencies:
|
||||
undici-types: 5.26.5
|
||||
|
||||
'@types/ws@8.5.10':
|
||||
dependencies:
|
||||
'@types/node': 20.14.2
|
||||
'@types/node': 20.14.6
|
||||
|
||||
JSONStream@1.3.5:
|
||||
dependencies:
|
||||
@ -1154,21 +1154,21 @@ snapshots:
|
||||
meow: 12.1.1
|
||||
split2: 4.2.0
|
||||
|
||||
cosmiconfig-typescript-loader@5.0.0(@types/node@20.14.2)(cosmiconfig@9.0.0(typescript@5.4.5))(typescript@5.4.5):
|
||||
cosmiconfig-typescript-loader@5.0.0(@types/node@20.14.6)(cosmiconfig@9.0.0(typescript@5.5.2))(typescript@5.5.2):
|
||||
dependencies:
|
||||
'@types/node': 20.14.2
|
||||
cosmiconfig: 9.0.0(typescript@5.4.5)
|
||||
'@types/node': 20.14.6
|
||||
cosmiconfig: 9.0.0(typescript@5.5.2)
|
||||
jiti: 1.21.6
|
||||
typescript: 5.4.5
|
||||
typescript: 5.5.2
|
||||
|
||||
cosmiconfig@9.0.0(typescript@5.4.5):
|
||||
cosmiconfig@9.0.0(typescript@5.5.2):
|
||||
dependencies:
|
||||
env-paths: 2.2.1
|
||||
import-fresh: 3.3.0
|
||||
js-yaml: 4.1.0
|
||||
parse-json: 5.2.0
|
||||
optionalDependencies:
|
||||
typescript: 5.4.5
|
||||
typescript: 5.5.2
|
||||
|
||||
cross-spawn@7.0.3:
|
||||
dependencies:
|
||||
@ -1185,7 +1185,7 @@ snapshots:
|
||||
denque@2.1.0:
|
||||
optional: true
|
||||
|
||||
discord-api-types@0.37.88: {}
|
||||
discord-api-types@0.37.90: {}
|
||||
|
||||
dot-prop@5.3.0:
|
||||
dependencies:
|
||||
@ -1334,14 +1334,14 @@ snapshots:
|
||||
|
||||
lines-and-columns@1.2.4: {}
|
||||
|
||||
lint-staged@15.2.6:
|
||||
lint-staged@15.2.7:
|
||||
dependencies:
|
||||
chalk: 5.3.0
|
||||
commander: 12.1.0
|
||||
debug: 4.3.5
|
||||
execa: 8.0.1
|
||||
lilconfig: 3.1.2
|
||||
listr2: 8.2.1
|
||||
listr2: 8.2.2
|
||||
micromatch: 4.0.7
|
||||
pidtree: 0.6.0
|
||||
string-argv: 0.3.2
|
||||
@ -1349,13 +1349,13 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
listr2@8.2.1:
|
||||
listr2@8.2.2:
|
||||
dependencies:
|
||||
cli-truncate: 4.0.0
|
||||
colorette: 2.0.20
|
||||
eventemitter3: 5.0.1
|
||||
log-update: 6.0.0
|
||||
rfdc: 1.3.1
|
||||
rfdc: 1.4.1
|
||||
wrap-ansi: 9.0.0
|
||||
|
||||
locate-path@7.2.0:
|
||||
@ -1485,7 +1485,7 @@ snapshots:
|
||||
onetime: 5.1.2
|
||||
signal-exit: 3.0.7
|
||||
|
||||
rfdc@1.3.1: {}
|
||||
rfdc@1.4.1: {}
|
||||
|
||||
semver@7.6.2: {}
|
||||
|
||||
@ -1555,7 +1555,7 @@ snapshots:
|
||||
tweetnacl@1.0.3:
|
||||
optional: true
|
||||
|
||||
typescript@5.4.5: {}
|
||||
typescript@5.5.2: {}
|
||||
|
||||
uWebSockets.js@https://codeload.github.com/uNetworking/uWebSockets.js/tar.gz/f40213ec0a97d0d8721d9d32d92d6eb6ddcd22e7:
|
||||
optional: true
|
||||
@ -1584,7 +1584,7 @@ snapshots:
|
||||
string-width: 7.1.0
|
||||
strip-ansi: 7.1.0
|
||||
|
||||
ws@8.17.0: {}
|
||||
ws@8.17.1: {}
|
||||
|
||||
y18n@5.0.8: {}
|
||||
|
||||
|
@ -1,26 +1,22 @@
|
||||
import {
|
||||
type APIButtonComponentWithCustomId,
|
||||
type APIButtonComponentWithURL,
|
||||
type APIMessageComponentEmoji,
|
||||
type ButtonStyle,
|
||||
ComponentType,
|
||||
type APIButtonComponent,
|
||||
} from 'discord-api-types/v10';
|
||||
import { throwError } from '..';
|
||||
import type { EmojiResolvable, When } from '../common';
|
||||
import type { EmojiResolvable } from '../common';
|
||||
import { resolvePartialEmoji } from '../structures/extra/functions';
|
||||
|
||||
export type ButtonStylesForID = Exclude<ButtonStyle, ButtonStyle.Link>;
|
||||
|
||||
/**
|
||||
* Represents a button component.
|
||||
* @template Type - The type of the button component.
|
||||
*/
|
||||
export class Button<Type extends boolean = boolean> {
|
||||
export class Button {
|
||||
/**
|
||||
* Creates a new Button instance.
|
||||
* @param data - The initial data for the button.
|
||||
*/
|
||||
constructor(public data: Partial<When<Type, APIButtonComponentWithCustomId, APIButtonComponentWithURL>> = {}) {
|
||||
constructor(public data: Partial<APIButtonComponent> = {}) {
|
||||
this.data.type = ComponentType.Button;
|
||||
}
|
||||
|
||||
@ -30,8 +26,7 @@ export class Button<Type extends boolean = boolean> {
|
||||
* @returns The modified Button instance.
|
||||
*/
|
||||
setCustomId(id: string) {
|
||||
// @ts-expect-error
|
||||
this.data.custom_id = id;
|
||||
(this.data as Extract<APIButtonComponent, { custom_id?: string }>).custom_id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -41,8 +36,7 @@ export class Button<Type extends boolean = boolean> {
|
||||
* @returns The modified Button instance.
|
||||
*/
|
||||
setURL(url: string) {
|
||||
// @ts-expect-error
|
||||
this.data.url = url;
|
||||
(this.data as Extract<APIButtonComponent, { url?: string }>).url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -52,7 +46,7 @@ export class Button<Type extends boolean = boolean> {
|
||||
* @returns The modified Button instance.
|
||||
*/
|
||||
setLabel(label: string) {
|
||||
this.data.label = label;
|
||||
(this.data as Extract<APIButtonComponent, { label?: string }>).label = label;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -63,8 +57,9 @@ export class Button<Type extends boolean = boolean> {
|
||||
*/
|
||||
setEmoji(emoji: EmojiResolvable) {
|
||||
const resolve = resolvePartialEmoji(emoji);
|
||||
if (!resolve) return throwError('Invalid Emoji');
|
||||
this.data.emoji = resolve as APIMessageComponentEmoji;
|
||||
if (!resolve) throw new Error('Invalid Emoji');
|
||||
(this.data as Extract<APIButtonComponent, { emoji?: APIMessageComponentEmoji }>).emoji =
|
||||
resolve as APIMessageComponentEmoji;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -83,11 +78,16 @@ export class Button<Type extends boolean = boolean> {
|
||||
return this;
|
||||
}
|
||||
|
||||
setSKUId(skuId: string) {
|
||||
(this.data as Extract<APIButtonComponent, { sku_id?: string }>).sku_id = skuId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the Button instance to its JSON representation.
|
||||
* @returns The JSON representation of the Button instance.
|
||||
*/
|
||||
toJSON() {
|
||||
return { ...this.data } as When<Type, APIButtonComponentWithCustomId, APIButtonComponentWithURL>;
|
||||
return { ...this.data } as Partial<APIButtonComponent>;
|
||||
}
|
||||
}
|
||||
|
45
src/cache/adapters/default.ts
vendored
45
src/cache/adapters/default.ts
vendored
@ -20,13 +20,7 @@ export class MemoryAdapter implements Adapter {
|
||||
return values;
|
||||
}
|
||||
|
||||
get(keys: string): any;
|
||||
get(keys: string[]): any[];
|
||||
get(keys: string | string[]) {
|
||||
if (!Array.isArray(keys)) {
|
||||
const data = this.storage.get(keys);
|
||||
return data ? JSON.parse(data) : null;
|
||||
}
|
||||
bulkGet(keys: string[]) {
|
||||
return keys
|
||||
.map(x => {
|
||||
const data = this.storage.get(x);
|
||||
@ -35,22 +29,22 @@ export class MemoryAdapter implements Adapter {
|
||||
.filter(x => x);
|
||||
}
|
||||
|
||||
set(keys: string, data: any): void;
|
||||
set(keys: [string, any][]): void;
|
||||
set(keys: string | [string, any][], data?: any): void {
|
||||
if (Array.isArray(keys)) {
|
||||
get(keys: string) {
|
||||
const data = this.storage.get(keys);
|
||||
return data ? JSON.parse(data) : null;
|
||||
}
|
||||
|
||||
bulkSet(keys: [string, any][]) {
|
||||
for (const [key, value] of keys) {
|
||||
this.storage.set(key, JSON.stringify(value));
|
||||
}
|
||||
} else {
|
||||
this.storage.set(keys, JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
|
||||
patch(updateOnly: boolean, keys: string, data: any): void;
|
||||
patch(updateOnly: boolean, keys: [string, any][]): void;
|
||||
patch(updateOnly: boolean, keys: string | [string, any][], data?: any): void {
|
||||
if (Array.isArray(keys)) {
|
||||
set(key: string, data: any) {
|
||||
this.storage.set(key, JSON.stringify(data));
|
||||
}
|
||||
|
||||
bulkPatch(updateOnly: boolean, keys: [string, any][]) {
|
||||
for (const [key, value] of keys) {
|
||||
const oldData = this.get(key);
|
||||
if (updateOnly && !oldData) {
|
||||
@ -61,7 +55,9 @@ export class MemoryAdapter implements Adapter {
|
||||
Array.isArray(value) ? JSON.stringify(value) : JSON.stringify({ ...(oldData ?? {}), ...value }),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
patch(updateOnly: boolean, keys: string, data: any) {
|
||||
const oldData = this.get(keys);
|
||||
if (updateOnly && !oldData) {
|
||||
return;
|
||||
@ -71,7 +67,6 @@ export class MemoryAdapter implements Adapter {
|
||||
Array.isArray(data) ? JSON.stringify(data) : JSON.stringify({ ...(oldData ?? {}), ...data }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
values(to: string) {
|
||||
const array: any[] = [];
|
||||
@ -96,14 +91,16 @@ export class MemoryAdapter implements Adapter {
|
||||
return this.getToRelationship(to).length;
|
||||
}
|
||||
|
||||
remove(keys: string): void;
|
||||
remove(keys: string[]): void;
|
||||
remove(keys: string | string[]) {
|
||||
for (const i of Array.isArray(keys) ? keys : [keys]) {
|
||||
bulkRemove(keys: string[]) {
|
||||
for (const i of keys) {
|
||||
this.storage.delete(i);
|
||||
}
|
||||
}
|
||||
|
||||
remove(key: string) {
|
||||
this.storage.delete(key);
|
||||
}
|
||||
|
||||
flush(): void {
|
||||
this.storage.clear();
|
||||
this.relationships.clear();
|
||||
|
53
src/cache/adapters/limited.ts
vendored
53
src/cache/adapters/limited.ts
vendored
@ -9,18 +9,23 @@ export interface ResourceLimitedMemoryAdapter {
|
||||
|
||||
export interface LimitedMemoryAdapterOptions {
|
||||
default?: ResourceLimitedMemoryAdapter;
|
||||
|
||||
guild?: ResourceLimitedMemoryAdapter;
|
||||
user?: ResourceLimitedMemoryAdapter;
|
||||
|
||||
ban?: ResourceLimitedMemoryAdapter;
|
||||
member?: ResourceLimitedMemoryAdapter;
|
||||
voice_state?: ResourceLimitedMemoryAdapter;
|
||||
|
||||
channel?: ResourceLimitedMemoryAdapter;
|
||||
emoji?: ResourceLimitedMemoryAdapter;
|
||||
overwrite?: ResourceLimitedMemoryAdapter;
|
||||
presence?: ResourceLimitedMemoryAdapter;
|
||||
role?: ResourceLimitedMemoryAdapter;
|
||||
stage_instance?: ResourceLimitedMemoryAdapter;
|
||||
sticker?: ResourceLimitedMemoryAdapter;
|
||||
thread?: ResourceLimitedMemoryAdapter;
|
||||
overwrite?: ResourceLimitedMemoryAdapter;
|
||||
message?: ResourceLimitedMemoryAdapter;
|
||||
}
|
||||
|
||||
export class LimitedMemoryAdapter implements Adapter {
|
||||
@ -58,13 +63,7 @@ export class LimitedMemoryAdapter implements Adapter {
|
||||
return values;
|
||||
}
|
||||
|
||||
get(keys: string): any;
|
||||
get(keys: string[]): any[];
|
||||
get(keys: string | string[]) {
|
||||
if (!Array.isArray(keys)) {
|
||||
const data = [...this.storage.values()].find(x => x.has(keys))?.get(keys);
|
||||
return data ? JSON.parse(data) : null;
|
||||
}
|
||||
bulkGet(keys: string[]) {
|
||||
const iterator = [...this.storage.values()];
|
||||
return keys
|
||||
.map(key => {
|
||||
@ -74,6 +73,11 @@ export class LimitedMemoryAdapter implements Adapter {
|
||||
.filter(x => x);
|
||||
}
|
||||
|
||||
get(keys: string) {
|
||||
const data = [...this.storage.values()].find(x => x.has(keys))?.get(keys);
|
||||
return data ? JSON.parse(data) : null;
|
||||
}
|
||||
|
||||
private __set(key: string, data: any) {
|
||||
const __guildId = Array.isArray(data) ? data[0].guild_id : data.guild_id;
|
||||
const namespace = `${key.split('.')[0]}${__guildId ? `.${__guildId}` : ''}`;
|
||||
@ -96,6 +100,7 @@ export class LimitedMemoryAdapter implements Adapter {
|
||||
case 'user':
|
||||
self.removeToRelationship(namespace, k.split('.')[1]);
|
||||
break;
|
||||
case 'ban':
|
||||
case 'member':
|
||||
case 'voice_state':
|
||||
{
|
||||
@ -124,22 +129,17 @@ export class LimitedMemoryAdapter implements Adapter {
|
||||
this.storage.get(namespace)!.set(key, JSON.stringify(data));
|
||||
}
|
||||
|
||||
set(keys: string, data: any): void;
|
||||
set(keys: [string, any][]): void;
|
||||
set(keys: string | [string, any][], data?: any): void {
|
||||
if (Array.isArray(keys)) {
|
||||
bulkSet(keys: [string, any][]) {
|
||||
for (const [key, value] of keys) {
|
||||
this.__set(key, value);
|
||||
}
|
||||
} else {
|
||||
this.__set(keys, data);
|
||||
}
|
||||
}
|
||||
|
||||
patch(updateOnly: boolean, keys: string, data: any): void;
|
||||
patch(updateOnly: boolean, keys: [string, any][]): void;
|
||||
patch(updateOnly: boolean, keys: string | [string, any][], data?: any): void {
|
||||
if (Array.isArray(keys)) {
|
||||
set(keys: string, data: any) {
|
||||
this.__set(keys, data);
|
||||
}
|
||||
|
||||
bulkPatch(updateOnly: boolean, keys: [string, any][]) {
|
||||
for (const [key, value] of keys) {
|
||||
const oldData = this.get(key);
|
||||
if (updateOnly && !oldData) {
|
||||
@ -147,14 +147,15 @@ export class LimitedMemoryAdapter implements Adapter {
|
||||
}
|
||||
this.__set(key, Array.isArray(value) ? value : { ...(oldData ?? {}), ...value });
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
patch(updateOnly: boolean, keys: string, data: any) {
|
||||
const oldData = this.get(keys);
|
||||
if (updateOnly && !oldData) {
|
||||
return;
|
||||
}
|
||||
this.__set(keys, Array.isArray(data) ? data : { ...(oldData ?? {}), ...data });
|
||||
}
|
||||
}
|
||||
|
||||
values(to: string) {
|
||||
const array: any[] = [];
|
||||
@ -179,14 +180,16 @@ export class LimitedMemoryAdapter implements Adapter {
|
||||
return this.getToRelationship(to).length;
|
||||
}
|
||||
|
||||
remove(keys: string): void;
|
||||
remove(keys: string[]): void;
|
||||
remove(keys: string | string[]) {
|
||||
for (const i of Array.isArray(keys) ? keys : [keys]) {
|
||||
bulkRemove(keys: string[]) {
|
||||
for (const i of keys) {
|
||||
this.storage.get(i.split('.')[0])?.delete(i);
|
||||
}
|
||||
}
|
||||
|
||||
remove(key: string) {
|
||||
this.storage.get(key.split('.')[0])?.delete(key);
|
||||
}
|
||||
|
||||
flush(): void {
|
||||
this.storage.clear();
|
||||
this.relationships.clear();
|
||||
|
84
src/cache/adapters/redis.ts
vendored
84
src/cache/adapters/redis.ts
vendored
@ -31,7 +31,7 @@ export class RedisAdapter implements Adapter {
|
||||
private __scanSets(query: string, returnKeys: true): Promise<string[]>;
|
||||
private __scanSets(query: string, returnKeys = false) {
|
||||
const match = this.buildKey(query);
|
||||
return new Promise<string[]>((r, j) => {
|
||||
return new Promise<string[] | any[]>((r, j) => {
|
||||
const stream = this.client.scanStream({
|
||||
match,
|
||||
type: 'set',
|
||||
@ -39,7 +39,7 @@ export class RedisAdapter implements Adapter {
|
||||
const keys: string[] = [];
|
||||
stream
|
||||
.on('data', resultKeys => keys.push(...resultKeys))
|
||||
.on('end', () => (returnKeys ? r(keys.map(x => this.buildKey(x))) : r(this.get(keys))))
|
||||
.on('end', () => (returnKeys ? r(keys.map(x => this.buildKey(x))) : r(this.bulkGet(keys))))
|
||||
.on('error', err => j(err));
|
||||
});
|
||||
}
|
||||
@ -48,7 +48,7 @@ export class RedisAdapter implements Adapter {
|
||||
scan(query: string, returnKeys: true): Promise<string[]>;
|
||||
scan(query: string, returnKeys = false) {
|
||||
const match = this.buildKey(query);
|
||||
return new Promise<string[]>((r, j) => {
|
||||
return new Promise<string[] | any[]>((r, j) => {
|
||||
const stream = this.client.scanStream({
|
||||
match,
|
||||
// omit relationships
|
||||
@ -57,22 +57,12 @@ export class RedisAdapter implements Adapter {
|
||||
const keys: string[] = [];
|
||||
stream
|
||||
.on('data', resultKeys => keys.push(...resultKeys))
|
||||
.on('end', () => (returnKeys ? r(keys.map(x => this.buildKey(x))) : r(this.get(keys))))
|
||||
.on('end', () => (returnKeys ? r(keys.map(x => this.buildKey(x))) : r(this.bulkGet(keys))))
|
||||
.on('error', err => j(err));
|
||||
});
|
||||
}
|
||||
|
||||
async get(keys: string[]): Promise<any[]>;
|
||||
async get(keys: string): Promise<any>;
|
||||
async get(keys: string | string[]) {
|
||||
if (!Array.isArray(keys)) {
|
||||
const value = await this.client.hgetall(this.buildKey(keys));
|
||||
if (value) {
|
||||
return toNormal(value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
async bulkGet(keys: string[]) {
|
||||
const pipeline = this.client.pipeline();
|
||||
|
||||
for (const key of keys) {
|
||||
@ -82,46 +72,31 @@ export class RedisAdapter implements Adapter {
|
||||
return (await pipeline.exec())?.filter(x => !!x[1]).map(x => toNormal(x[1] as Record<string, any>)) ?? [];
|
||||
}
|
||||
|
||||
async set(id: [string, any][]): Promise<void>;
|
||||
async set(id: string, data: any): Promise<void>;
|
||||
async set(id: string | [string, any][], data?: any): Promise<void> {
|
||||
if (!Array.isArray(id)) {
|
||||
await this.client.hset(this.buildKey(id), toDb(data));
|
||||
return;
|
||||
async get(keys: string): Promise<any> {
|
||||
const value = await this.client.hgetall(this.buildKey(keys));
|
||||
if (value) {
|
||||
return toNormal(value);
|
||||
}
|
||||
}
|
||||
|
||||
async bulkSet(data: [string, any][]) {
|
||||
const pipeline = this.client.pipeline();
|
||||
|
||||
for (const [k, v] of id) {
|
||||
for (const [k, v] of data) {
|
||||
pipeline.hset(this.buildKey(k), toDb(v));
|
||||
}
|
||||
|
||||
await pipeline.exec();
|
||||
}
|
||||
|
||||
async patch(updateOnly: boolean, id: [string, any][]): Promise<void>;
|
||||
async patch(updateOnly: boolean, id: string, data: any): Promise<void>;
|
||||
async patch(updateOnly: boolean, id: string | [string, any][], data?: any): Promise<void> {
|
||||
if (!Array.isArray(id)) {
|
||||
if (updateOnly) {
|
||||
await this.client.eval(
|
||||
`if redis.call('exists',KEYS[1]) == 1 then redis.call('hset', KEYS[1], ${Array.from(
|
||||
{ length: Object.keys(data).length * 2 },
|
||||
(_, i) => `ARGV[${i + 1}]`,
|
||||
)}) end`,
|
||||
1,
|
||||
this.buildKey(id),
|
||||
...Object.entries(toDb(data)).flat(),
|
||||
);
|
||||
} else {
|
||||
async set(id: string, data: any) {
|
||||
await this.client.hset(this.buildKey(id), toDb(data));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
async bulkPatch(updateOnly: boolean, data: [string, any][]) {
|
||||
const pipeline = this.client.pipeline();
|
||||
|
||||
for (const [k, v] of id) {
|
||||
for (const [k, v] of data) {
|
||||
if (updateOnly) {
|
||||
pipeline.eval(
|
||||
`if redis.call('exists',KEYS[1]) == 1 then redis.call('hset', KEYS[1], ${Array.from(
|
||||
@ -140,11 +115,27 @@ export class RedisAdapter implements Adapter {
|
||||
await pipeline.exec();
|
||||
}
|
||||
|
||||
async patch(updateOnly: boolean, id: string, data: any): Promise<void> {
|
||||
if (updateOnly) {
|
||||
await this.client.eval(
|
||||
`if redis.call('exists',KEYS[1]) == 1 then redis.call('hset', KEYS[1], ${Array.from(
|
||||
{ length: Object.keys(data).length * 2 },
|
||||
(_, i) => `ARGV[${i + 1}]`,
|
||||
)}) end`,
|
||||
1,
|
||||
this.buildKey(id),
|
||||
...Object.entries(toDb(data)).flat(),
|
||||
);
|
||||
} else {
|
||||
await this.client.hset(this.buildKey(id), toDb(data));
|
||||
}
|
||||
}
|
||||
|
||||
async values(to: string): Promise<any[]> {
|
||||
const array: unknown[] = [];
|
||||
const data = await this.keys(to);
|
||||
if (data.length) {
|
||||
const items = await this.get(data);
|
||||
const items = await this.bulkGet(data);
|
||||
for (const item of items) {
|
||||
if (item) {
|
||||
array.push(item);
|
||||
@ -164,13 +155,12 @@ export class RedisAdapter implements Adapter {
|
||||
return this.client.scard(`${this.buildKey(to)}:set`);
|
||||
}
|
||||
|
||||
async remove(keys: string | string[]): Promise<void> {
|
||||
if (!Array.isArray(keys)) {
|
||||
await this.client.del(this.buildKey(keys));
|
||||
return;
|
||||
async bulkRemove(keys: string[]) {
|
||||
await this.client.del(...keys.map(x => this.buildKey(x)));
|
||||
}
|
||||
|
||||
await this.client.del(...keys.map(x => this.buildKey(x)));
|
||||
async remove(keys: string): Promise<void> {
|
||||
await this.client.del(this.buildKey(keys));
|
||||
}
|
||||
|
||||
async flush(): Promise<void> {
|
||||
@ -179,7 +169,7 @@ export class RedisAdapter implements Adapter {
|
||||
this.__scanSets(this.buildKey('*'), true),
|
||||
]).then(x => x.flat());
|
||||
if (!keys.length) return;
|
||||
await this.remove(keys);
|
||||
await this.bulkRemove(keys);
|
||||
}
|
||||
|
||||
async contains(to: string, keys: string): Promise<boolean> {
|
||||
|
12
src/cache/adapters/types.ts
vendored
12
src/cache/adapters/types.ts
vendored
@ -7,17 +7,14 @@ export interface Adapter {
|
||||
scan(query: string, keys: true): Awaitable<string[]>;
|
||||
scan(query: string, keys?: boolean): Awaitable<(any | string)[]>;
|
||||
|
||||
get(keys: string[]): Awaitable<any[]>;
|
||||
bulkGet(keys: string[]): Awaitable<any[]>;
|
||||
get(keys: string): Awaitable<any | null>;
|
||||
get(keys: string | string[]): Awaitable<any | null>;
|
||||
|
||||
set(keyValue: [string, any][]): Awaitable<void>;
|
||||
bulkSet(keyValue: [string, any][]): Awaitable<void>;
|
||||
set(id: string, data: any): Awaitable<void>;
|
||||
set(id: string | [string, any][], data?: any): Awaitable<void>;
|
||||
|
||||
patch(updateOnly: boolean, keyValue: [string, any][]): Awaitable<void>;
|
||||
bulkPatch(updateOnly: boolean, keyValue: [string, any][]): Awaitable<void>;
|
||||
patch(updateOnly: boolean, id: string, data: any): Awaitable<void>;
|
||||
patch(updateOnly: boolean, id: string | [string, any][], data?: any): Awaitable<void>;
|
||||
|
||||
values(to: string): Awaitable<any[]>;
|
||||
|
||||
@ -25,7 +22,8 @@ export interface Adapter {
|
||||
|
||||
count(to: string): Awaitable<number>;
|
||||
|
||||
remove(keys: string | string[]): Awaitable<void>;
|
||||
bulkRemove(keys: string[]): Awaitable<void>;
|
||||
remove(keys: string): Awaitable<void>;
|
||||
|
||||
flush(): Awaitable<void>;
|
||||
|
||||
|
16
src/cache/adapters/workeradapter.ts
vendored
16
src/cache/adapters/workeradapter.ts
vendored
@ -45,14 +45,26 @@ export class WorkerAdapter implements Adapter {
|
||||
return this.send('scan', ...rest);
|
||||
}
|
||||
|
||||
bulkGet(...rest: any[]) {
|
||||
return this.send('bulkGet', ...rest);
|
||||
}
|
||||
|
||||
get(...rest: any[]) {
|
||||
return this.send('get', ...rest);
|
||||
}
|
||||
|
||||
bulkSet(...rest: any[]) {
|
||||
return this.send('bulkSet', ...rest);
|
||||
}
|
||||
|
||||
set(...rest: any[]) {
|
||||
return this.send('set', ...rest);
|
||||
}
|
||||
|
||||
bulkPatch(...rest: any[]) {
|
||||
return this.send('bulkPatch', ...rest);
|
||||
}
|
||||
|
||||
patch(...rest: any[]) {
|
||||
return this.send('patch', ...rest);
|
||||
}
|
||||
@ -69,6 +81,10 @@ export class WorkerAdapter implements Adapter {
|
||||
return this.send('count', ...rest);
|
||||
}
|
||||
|
||||
bulkRemove(...rest: any[]) {
|
||||
return this.send('bulkRemove', ...rest);
|
||||
}
|
||||
|
||||
remove(...rest: any[]) {
|
||||
return this.send('remove', ...rest);
|
||||
}
|
||||
|
4
src/cache/index.ts
vendored
4
src/cache/index.ts
vendored
@ -382,7 +382,7 @@ export class Cache {
|
||||
}
|
||||
|
||||
await this.adapter.bulkAddToRelationShip(relationshipsData);
|
||||
await this.adapter.patch(false, allData);
|
||||
await this.adapter.bulkPatch(false, allData);
|
||||
}
|
||||
|
||||
async bulkSet(
|
||||
@ -474,7 +474,7 @@ export class Cache {
|
||||
}
|
||||
|
||||
await this.adapter.bulkAddToRelationShip(relationshipsData);
|
||||
await this.adapter.set(allData);
|
||||
await this.adapter.bulkSet(allData);
|
||||
}
|
||||
|
||||
async onPacket(event: GatewayDispatchPayload) {
|
||||
|
18
src/cache/resources/bans.ts
vendored
18
src/cache/resources/bans.ts
vendored
@ -2,7 +2,7 @@ import type { APIBan } from 'discord-api-types/v10';
|
||||
import type { ReturnCache } from '../..';
|
||||
import { fakePromise } from '../../common';
|
||||
import { GuildBasedResource } from './default/guild-based';
|
||||
import { GuildBan } from '../../structures/GuildBan';
|
||||
import { type GuildBanStructure, Transformers } from '../../client/transformers';
|
||||
export class Bans extends GuildBasedResource {
|
||||
namespace = 'ban';
|
||||
|
||||
@ -16,31 +16,31 @@ export class Bans extends GuildBasedResource {
|
||||
return rest;
|
||||
}
|
||||
|
||||
override get(id: string, guild: string): ReturnCache<GuildBan | undefined> {
|
||||
override get(id: string, guild: string): ReturnCache<GuildBanStructure | undefined> {
|
||||
return fakePromise(super.get(id, guild)).then(rawBan =>
|
||||
rawBan ? new GuildBan(this.client, rawBan, guild) : undefined,
|
||||
rawBan ? Transformers.GuildBan(this.client, rawBan, guild) : undefined,
|
||||
);
|
||||
}
|
||||
|
||||
override bulk(ids: string[], guild: string): ReturnCache<GuildBan[]> {
|
||||
override bulk(ids: string[], guild: string): ReturnCache<GuildBanStructure[]> {
|
||||
return fakePromise(super.bulk(ids, guild)).then(
|
||||
bans =>
|
||||
bans
|
||||
.map(rawBan => {
|
||||
return rawBan ? new GuildBan(this.client, rawBan, guild) : undefined;
|
||||
return rawBan ? Transformers.GuildBan(this.client, rawBan, guild) : undefined;
|
||||
})
|
||||
.filter(Boolean) as GuildBan[],
|
||||
.filter(Boolean) as GuildBanStructure[],
|
||||
);
|
||||
}
|
||||
|
||||
override values(guild: string): ReturnCache<GuildBan[]> {
|
||||
override values(guild: string): ReturnCache<GuildBanStructure[]> {
|
||||
return fakePromise(super.values(guild)).then(
|
||||
bans =>
|
||||
bans
|
||||
.map(rawBan => {
|
||||
return rawBan ? new GuildBan(this.client, rawBan, guild) : undefined;
|
||||
return rawBan ? Transformers.GuildBan(this.client, rawBan, guild) : undefined;
|
||||
})
|
||||
.filter(Boolean) as GuildBan[],
|
||||
.filter(Boolean) as GuildBanStructure[],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
4
src/cache/resources/channels.ts
vendored
4
src/cache/resources/channels.ts
vendored
@ -13,6 +13,10 @@ export class Channels extends GuildRelatedResource {
|
||||
return rest;
|
||||
}
|
||||
|
||||
raw(id: string): ReturnCache<APIChannel | undefined> {
|
||||
return super.get(id);
|
||||
}
|
||||
|
||||
override get(id: string): ReturnCache<AllChannels | undefined> {
|
||||
return fakePromise(super.get(id)).then(rawChannel =>
|
||||
rawChannel ? channelFrom(rawChannel, this.client) : undefined,
|
||||
|
2
src/cache/resources/default/base.ts
vendored
2
src/cache/resources/default/base.ts
vendored
@ -48,7 +48,7 @@ export class BaseResource<T = any> {
|
||||
}
|
||||
|
||||
bulk(ids: string[]): ReturnCache<T[]> {
|
||||
return fakePromise(this.adapter.get(ids.map(id => this.hashId(id)))).then(x => x.filter(y => y));
|
||||
return fakePromise(this.adapter.bulkGet(ids.map(id => this.hashId(id)))).then(x => x.filter(y => y));
|
||||
}
|
||||
|
||||
set(id: string, data: any) {
|
||||
|
10
src/cache/resources/default/guild-based.ts
vendored
10
src/cache/resources/default/guild-based.ts
vendored
@ -54,7 +54,7 @@ export class GuildBasedResource<T = any> {
|
||||
}
|
||||
|
||||
bulk(ids: string[], guild: string): ReturnCache<(T & { guild_id: string })[]> {
|
||||
return fakePromise(this.adapter.get(ids.map(id => this.hashGuildId(guild, id)))).then(x => x.filter(y => y));
|
||||
return fakePromise(this.adapter.bulkGet(ids.map(id => this.hashGuildId(guild, id)))).then(x => x.filter(y => y));
|
||||
}
|
||||
|
||||
set(__keys: string, guild: string, data: any): ReturnCache<void>;
|
||||
@ -71,7 +71,7 @@ export class GuildBasedResource<T = any> {
|
||||
guild,
|
||||
),
|
||||
).then(() =>
|
||||
this.adapter.set(
|
||||
this.adapter.bulkSet(
|
||||
keys.map(([key, value]) => {
|
||||
return [this.hashGuildId(guild, key), this.parse(value, key, guild)] as const;
|
||||
}),
|
||||
@ -87,14 +87,14 @@ export class GuildBasedResource<T = any> {
|
||||
any,
|
||||
][];
|
||||
|
||||
return fakePromise(this.adapter.get(keys.map(([key]) => this.hashGuildId(guild, key)))).then(oldDatas =>
|
||||
return fakePromise(this.adapter.bulkGet(keys.map(([key]) => this.hashGuildId(guild, key)))).then(oldDatas =>
|
||||
fakePromise(
|
||||
this.addToRelationship(
|
||||
keys.map(x => x[0]),
|
||||
guild,
|
||||
),
|
||||
).then(() =>
|
||||
this.adapter.set(
|
||||
this.adapter.bulkSet(
|
||||
keys.map(([key, value]) => {
|
||||
const oldData = oldDatas.find(x => x.id === key) ?? {};
|
||||
return [this.hashGuildId(guild, key), this.parse({ ...oldData, ...value }, key, guild)];
|
||||
@ -107,7 +107,7 @@ export class GuildBasedResource<T = any> {
|
||||
remove(id: string | string[], guild: string) {
|
||||
const ids = Array.isArray(id) ? id : [id];
|
||||
return fakePromise(this.removeToRelationship(ids, guild)).then(() =>
|
||||
this.adapter.remove(ids.map(x => this.hashGuildId(guild, x))),
|
||||
this.adapter.bulkRemove(ids.map(x => this.hashGuildId(guild, x))),
|
||||
);
|
||||
}
|
||||
|
||||
|
12
src/cache/resources/default/guild-related.ts
vendored
12
src/cache/resources/default/guild-related.ts
vendored
@ -54,7 +54,7 @@ export class GuildRelatedResource<T = any> {
|
||||
}
|
||||
|
||||
bulk(ids: string[]): ReturnCache<(T & { guild_id: string })[]> {
|
||||
return fakePromise(this.adapter.get(ids.map(x => this.hashId(x)))).then(x => x.filter(y => y));
|
||||
return fakePromise(this.adapter.bulkGet(ids.map(x => this.hashId(x)))).then(x => x.filter(y => y));
|
||||
}
|
||||
|
||||
set(__keys: string, guild: string, data: any): ReturnCache<void>;
|
||||
@ -72,7 +72,7 @@ export class GuildRelatedResource<T = any> {
|
||||
),
|
||||
).then(
|
||||
() =>
|
||||
this.adapter.set(
|
||||
this.adapter.bulkSet(
|
||||
keys.map(([key, value]) => {
|
||||
return [this.hashId(key), this.parse(value, key, guild)] as const;
|
||||
}),
|
||||
@ -96,7 +96,7 @@ export class GuildRelatedResource<T = any> {
|
||||
),
|
||||
).then(
|
||||
() =>
|
||||
this.adapter.patch(
|
||||
this.adapter.bulkPatch(
|
||||
false,
|
||||
keys.map(([key, value]) => {
|
||||
return [this.hashId(key), this.parse(value, key, guild)] as const;
|
||||
@ -105,7 +105,7 @@ export class GuildRelatedResource<T = any> {
|
||||
);
|
||||
}
|
||||
return fakePromise(
|
||||
this.adapter.patch(
|
||||
this.adapter.bulkPatch(
|
||||
true,
|
||||
keys.map(([key, value]) => {
|
||||
return [this.hashId(key), value];
|
||||
@ -117,7 +117,7 @@ export class GuildRelatedResource<T = any> {
|
||||
remove(id: string | string[], guild: string) {
|
||||
const ids = Array.isArray(id) ? id : [id];
|
||||
return fakePromise(this.removeToRelationship(ids, guild)).then(() =>
|
||||
this.adapter.remove(ids.map(x => this.hashId(x))),
|
||||
this.adapter.bulkRemove(ids.map(x => this.hashId(x))),
|
||||
);
|
||||
}
|
||||
|
||||
@ -133,7 +133,7 @@ export class GuildRelatedResource<T = any> {
|
||||
return guild === '*'
|
||||
? (fakePromise(this.adapter.scan(this.hashId(guild))).then(x => x) as (T & { guild_id: string })[])
|
||||
: (fakePromise(this.adapter.getToRelationship(this.hashId(guild))).then(keys =>
|
||||
this.adapter.get(keys.map(x => `${this.namespace}.${x}`)),
|
||||
this.adapter.bulkGet(keys.map(x => `${this.namespace}.${x}`)),
|
||||
) as (T & { guild_id: string })[]);
|
||||
}
|
||||
|
||||
|
14
src/cache/resources/emojis.ts
vendored
14
src/cache/resources/emojis.ts
vendored
@ -1,8 +1,8 @@
|
||||
import type { APIEmoji } from 'discord-api-types/v10';
|
||||
import type { ReturnCache } from '../..';
|
||||
import { fakePromise } from '../../common';
|
||||
import { GuildEmoji } from '../../structures';
|
||||
import { GuildRelatedResource } from './default/guild-related';
|
||||
import { type GuildEmojiStructure, Transformers } from '../../client/transformers';
|
||||
|
||||
export class Emojis extends GuildRelatedResource {
|
||||
namespace = 'emoji';
|
||||
@ -12,21 +12,21 @@ export class Emojis extends GuildRelatedResource {
|
||||
return true;
|
||||
}
|
||||
|
||||
override get(id: string): ReturnCache<GuildEmoji | undefined> {
|
||||
override get(id: string): ReturnCache<GuildEmojiStructure | undefined> {
|
||||
return fakePromise(super.get(id)).then(rawEmoji =>
|
||||
rawEmoji ? new GuildEmoji(this.client, rawEmoji, rawEmoji.guild_id) : undefined,
|
||||
rawEmoji ? Transformers.GuildEmoji(this.client, rawEmoji, rawEmoji.guild_id) : undefined,
|
||||
);
|
||||
}
|
||||
|
||||
override bulk(ids: string[]): ReturnCache<GuildEmoji[]> {
|
||||
override bulk(ids: string[]): ReturnCache<GuildEmojiStructure[]> {
|
||||
return fakePromise(super.bulk(ids) as (APIEmoji & { id: string; guild_id: string })[]).then(emojis =>
|
||||
emojis.map(rawEmoji => new GuildEmoji(this.client, rawEmoji, rawEmoji.guild_id)),
|
||||
emojis.map(rawEmoji => Transformers.GuildEmoji(this.client, rawEmoji, rawEmoji.guild_id)),
|
||||
);
|
||||
}
|
||||
|
||||
override values(guild: string): ReturnCache<GuildEmoji[]> {
|
||||
override values(guild: string): ReturnCache<GuildEmojiStructure[]> {
|
||||
return fakePromise(super.values(guild) as (APIEmoji & { id: string; guild_id: string })[]).then(emojis =>
|
||||
emojis.map(rawEmoji => new GuildEmoji(this.client, rawEmoji, rawEmoji.guild_id)),
|
||||
emojis.map(rawEmoji => Transformers.GuildEmoji(this.client, rawEmoji, rawEmoji.guild_id)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
24
src/cache/resources/guilds.ts
vendored
24
src/cache/resources/guilds.ts
vendored
@ -1,8 +1,8 @@
|
||||
import type { APIGuild } from 'discord-api-types/v10';
|
||||
import type { Cache, ReturnCache } from '..';
|
||||
import { fakePromise } from '../../common';
|
||||
import { Guild } from '../../structures';
|
||||
import { BaseResource } from './default/base';
|
||||
import { type GuildStructure, Transformers } from '../../client/transformers';
|
||||
|
||||
export class Guilds extends BaseResource {
|
||||
namespace = 'guild';
|
||||
@ -12,24 +12,30 @@ export class Guilds extends BaseResource {
|
||||
return true;
|
||||
}
|
||||
|
||||
override get(id: string): ReturnCache<Guild<'cached'> | undefined> {
|
||||
return fakePromise(super.get(id)).then(guild => (guild ? new Guild<'cached'>(this.client, guild) : undefined));
|
||||
raw(id: string): ReturnCache<APIGuild | undefined> {
|
||||
return super.get(id);
|
||||
}
|
||||
|
||||
override bulk(ids: string[]): ReturnCache<Guild<'cached'>[]> {
|
||||
return fakePromise(super.bulk(ids) as APIGuild[]).then(guilds =>
|
||||
guilds.map(x => new Guild<'cached'>(this.client, x)),
|
||||
override get(id: string): ReturnCache<GuildStructure<'cached'> | undefined> {
|
||||
return fakePromise(super.get(id)).then(guild =>
|
||||
guild ? Transformers.Guild<'cached'>(this.client, guild) : undefined,
|
||||
);
|
||||
}
|
||||
|
||||
override values(): ReturnCache<Guild<'cached'>[]> {
|
||||
override bulk(ids: string[]): ReturnCache<GuildStructure<'cached'>[]> {
|
||||
return fakePromise(super.bulk(ids) as APIGuild[]).then(guilds =>
|
||||
guilds.map(x => Transformers.Guild<'cached'>(this.client, x)),
|
||||
);
|
||||
}
|
||||
|
||||
override values(): ReturnCache<GuildStructure<'cached'>[]> {
|
||||
return fakePromise(super.values() as APIGuild[]).then(guilds =>
|
||||
guilds.map(x => new Guild<'cached'>(this.client, x)),
|
||||
guilds.map(x => Transformers.Guild<'cached'>(this.client, x)),
|
||||
);
|
||||
}
|
||||
|
||||
override async remove(id: string) {
|
||||
await this.cache.adapter.remove(
|
||||
await this.cache.adapter.bulkRemove(
|
||||
(
|
||||
await Promise.all([
|
||||
this.cache.members?.keys(id) ?? [],
|
||||
|
37
src/cache/resources/members.ts
vendored
37
src/cache/resources/members.ts
vendored
@ -1,8 +1,8 @@
|
||||
import type { APIGuildMember } from 'discord-api-types/v10';
|
||||
import type { ReturnCache } from '../..';
|
||||
import { fakePromise } from '../../common';
|
||||
import { GuildMember } from '../../structures';
|
||||
import { GuildBasedResource } from './default/guild-based';
|
||||
import { type GuildMemberStructure, Transformers } from '../../client/transformers';
|
||||
export class Members extends GuildBasedResource {
|
||||
namespace = 'member';
|
||||
|
||||
@ -16,38 +16,51 @@ export class Members extends GuildBasedResource {
|
||||
return rest;
|
||||
}
|
||||
|
||||
override get(id: string, guild: string): ReturnCache<GuildMember | undefined> {
|
||||
raw(id: string, guild: string): ReturnCache<APIGuildMember | undefined> {
|
||||
return fakePromise(super.get(id, guild) as Omit<APIGuildMember, 'user'>).then(rawMember => {
|
||||
return fakePromise(this.client.cache.users?.raw(id)).then(user =>
|
||||
rawMember && user
|
||||
? {
|
||||
...rawMember,
|
||||
user,
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
override get(id: string, guild: string): ReturnCache<GuildMemberStructure | undefined> {
|
||||
return fakePromise(super.get(id, guild)).then(rawMember =>
|
||||
fakePromise(this.client.cache.users?.get(id)).then(user =>
|
||||
rawMember && user ? new GuildMember(this.client, rawMember, user, guild) : undefined,
|
||||
fakePromise(this.client.cache.users?.raw(id)).then(user =>
|
||||
rawMember && user ? Transformers.GuildMember(this.client, rawMember, user, guild) : undefined,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
override bulk(ids: string[], guild: string): ReturnCache<GuildMember[]> {
|
||||
override bulk(ids: string[], guild: string): ReturnCache<GuildMemberStructure[]> {
|
||||
return fakePromise(super.bulk(ids, guild)).then(members =>
|
||||
fakePromise(this.client.cache.users?.bulk(ids)).then(
|
||||
fakePromise(this.client.cache.users?.bulkRaw(ids)).then(
|
||||
users =>
|
||||
members
|
||||
.map(rawMember => {
|
||||
const user = users?.find(x => x.id === rawMember.id);
|
||||
return user ? new GuildMember(this.client, rawMember, user, guild) : undefined;
|
||||
return user ? Transformers.GuildMember(this.client, rawMember, user, guild) : undefined;
|
||||
})
|
||||
.filter(Boolean) as GuildMember[],
|
||||
.filter(Boolean) as GuildMemberStructure[],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
override values(guild: string): ReturnCache<GuildMember[]> {
|
||||
override values(guild: string): ReturnCache<GuildMemberStructure[]> {
|
||||
return fakePromise(super.values(guild)).then(members =>
|
||||
fakePromise(this.client.cache.users?.values()).then(
|
||||
fakePromise(this.client.cache.users?.valuesRaw()).then(
|
||||
users =>
|
||||
members
|
||||
.map(rawMember => {
|
||||
const user = users?.find(x => x.id === rawMember.id);
|
||||
return user ? new GuildMember(this.client, rawMember, user, rawMember.guild_id) : undefined;
|
||||
return user ? Transformers.GuildMember(this.client, rawMember, user, rawMember.guild_id) : undefined;
|
||||
})
|
||||
.filter(Boolean) as GuildMember[],
|
||||
.filter(Boolean) as GuildMemberStructure[],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
20
src/cache/resources/messages.ts
vendored
20
src/cache/resources/messages.ts
vendored
@ -2,7 +2,7 @@ import type { APIMessage, APIUser } from 'discord-api-types/v10';
|
||||
import { GuildRelatedResource } from './default/guild-related';
|
||||
import type { MessageData, ReturnCache } from '../..';
|
||||
import { fakePromise } from '../../common';
|
||||
import { Message } from '../../structures';
|
||||
import { type MessageStructure, Transformers } from '../../client/transformers';
|
||||
|
||||
export class Messages extends GuildRelatedResource {
|
||||
namespace = 'message';
|
||||
@ -19,19 +19,19 @@ export class Messages extends GuildRelatedResource {
|
||||
return rest;
|
||||
}
|
||||
|
||||
override get(id: string): ReturnCache<Message | undefined> {
|
||||
override get(id: string): ReturnCache<MessageStructure | undefined> {
|
||||
return fakePromise(super.get(id) as APIMessageResource | undefined).then(rawMessage => {
|
||||
return this.cache.users && rawMessage?.user_id
|
||||
? fakePromise(this.cache.adapter.get(this.cache.users.hashId(rawMessage.user_id)) as APIUser | undefined).then(
|
||||
user => {
|
||||
return user ? new Message(this.client, { ...rawMessage!, author: user }) : undefined;
|
||||
return user ? Transformers.Message(this.client, { ...rawMessage!, author: user }) : undefined;
|
||||
},
|
||||
)
|
||||
: undefined;
|
||||
});
|
||||
}
|
||||
|
||||
override bulk(ids: string[]): ReturnCache<Message[]> {
|
||||
override bulk(ids: string[]): ReturnCache<MessageStructure[]> {
|
||||
return fakePromise(super.bulk(ids) as APIMessageResource[]).then(
|
||||
messages =>
|
||||
messages
|
||||
@ -40,26 +40,26 @@ export class Messages extends GuildRelatedResource {
|
||||
? fakePromise(
|
||||
this.cache.adapter.get(this.cache.users.hashId(rawMessage.user_id)) as APIUser | undefined,
|
||||
).then(user => {
|
||||
return user ? new Message(this.client, { ...rawMessage!, author: user }) : undefined;
|
||||
return user ? Transformers.Message(this.client, { ...rawMessage!, author: user }) : undefined;
|
||||
})
|
||||
: undefined;
|
||||
})
|
||||
.filter(Boolean) as Message[],
|
||||
.filter(Boolean) as MessageStructure[],
|
||||
);
|
||||
}
|
||||
|
||||
override values(guild: string): ReturnCache<Message[]> {
|
||||
override values(guild: string): ReturnCache<MessageStructure[]> {
|
||||
return fakePromise(super.values(guild) as APIMessageResource[]).then(messages => {
|
||||
const hashes: (string | undefined)[] = this.cache.users
|
||||
? messages.map(x => (x.user_id ? this.cache.users!.hashId(x.user_id) : undefined))
|
||||
: [];
|
||||
return fakePromise(this.cache.adapter.get(hashes.filter(Boolean) as string[]) as APIUser[]).then(users => {
|
||||
return fakePromise(this.cache.adapter.bulkGet(hashes.filter(Boolean) as string[]) as APIUser[]).then(users => {
|
||||
return messages
|
||||
.map(message => {
|
||||
const user = users.find(user => user.id === message.user_id);
|
||||
return user ? new Message(this.client, { ...message, author: user }) : undefined;
|
||||
return user ? Transformers.Message(this.client, { ...message, author: user }) : undefined;
|
||||
})
|
||||
.filter(Boolean) as Message[];
|
||||
.filter(Boolean) as MessageStructure[];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
22
src/cache/resources/roles.ts
vendored
22
src/cache/resources/roles.ts
vendored
@ -1,8 +1,8 @@
|
||||
import type { APIRole } from 'discord-api-types/v10';
|
||||
import type { ReturnCache } from '../..';
|
||||
import { fakePromise } from '../../common';
|
||||
import { GuildRole } from '../../structures';
|
||||
import { GuildRelatedResource } from './default/guild-related';
|
||||
import { type GuildRoleStructure, Transformers } from '../../client/transformers';
|
||||
|
||||
export class Roles extends GuildRelatedResource {
|
||||
namespace = 'role';
|
||||
@ -12,21 +12,29 @@ export class Roles extends GuildRelatedResource {
|
||||
return true;
|
||||
}
|
||||
|
||||
override get(id: string): ReturnCache<GuildRole | undefined> {
|
||||
raw(id: string): ReturnCache<APIRole | undefined> {
|
||||
return super.get(id);
|
||||
}
|
||||
|
||||
override get(id: string): ReturnCache<GuildRoleStructure | undefined> {
|
||||
return fakePromise(super.get(id)).then(rawRole =>
|
||||
rawRole ? new GuildRole(this.client, rawRole, rawRole.guild_id) : undefined,
|
||||
rawRole ? Transformers.GuildRole(this.client, rawRole, rawRole.guild_id) : undefined,
|
||||
);
|
||||
}
|
||||
|
||||
override bulk(ids: string[]): ReturnCache<GuildRole[]> {
|
||||
override bulk(ids: string[]): ReturnCache<GuildRoleStructure[]> {
|
||||
return fakePromise(super.bulk(ids)).then(roles =>
|
||||
roles.map(rawRole => new GuildRole(this.client, rawRole, rawRole.guild_id)),
|
||||
roles.map(rawRole => Transformers.GuildRole(this.client, rawRole, rawRole.guild_id)),
|
||||
);
|
||||
}
|
||||
|
||||
override values(guild: string): ReturnCache<GuildRole[]> {
|
||||
override values(guild: string): ReturnCache<GuildRoleStructure[]> {
|
||||
return fakePromise(super.values(guild)).then(roles =>
|
||||
roles.map(rawRole => new GuildRole(this.client, rawRole, rawRole.guild_id)),
|
||||
roles.map(rawRole => Transformers.GuildRole(this.client, rawRole, rawRole.guild_id)),
|
||||
);
|
||||
}
|
||||
|
||||
valuesRaw(guild: string): ReturnCache<APIRole[]> {
|
||||
return super.values(guild);
|
||||
}
|
||||
}
|
||||
|
14
src/cache/resources/stickers.ts
vendored
14
src/cache/resources/stickers.ts
vendored
@ -1,8 +1,8 @@
|
||||
import type { APISticker } from 'discord-api-types/v10';
|
||||
import type { ReturnCache } from '../..';
|
||||
import { fakePromise } from '../../common';
|
||||
import { Sticker } from '../../structures';
|
||||
import { GuildRelatedResource } from './default/guild-related';
|
||||
import { type StickerStructure, Transformers } from '../../client/transformers';
|
||||
|
||||
export class Stickers extends GuildRelatedResource {
|
||||
namespace = 'sticker';
|
||||
@ -12,21 +12,21 @@ export class Stickers extends GuildRelatedResource {
|
||||
return true;
|
||||
}
|
||||
|
||||
override get(id: string): ReturnCache<Sticker | undefined> {
|
||||
override get(id: string): ReturnCache<StickerStructure | undefined> {
|
||||
return fakePromise(super.get(id)).then(rawSticker =>
|
||||
rawSticker ? new Sticker(this.client, rawSticker) : undefined,
|
||||
rawSticker ? Transformers.Sticker(this.client, rawSticker) : undefined,
|
||||
);
|
||||
}
|
||||
|
||||
override bulk(ids: string[]): ReturnCache<Sticker[]> {
|
||||
override bulk(ids: string[]): ReturnCache<StickerStructure[]> {
|
||||
return fakePromise(super.bulk(ids) as APISticker[]).then(emojis =>
|
||||
emojis.map(rawSticker => new Sticker(this.client, rawSticker)),
|
||||
emojis.map(rawSticker => Transformers.Sticker(this.client, rawSticker)),
|
||||
);
|
||||
}
|
||||
|
||||
override values(guild: string): ReturnCache<Sticker[]> {
|
||||
override values(guild: string): ReturnCache<StickerStructure[]> {
|
||||
return fakePromise(super.values(guild) as APISticker[]).then(emojis =>
|
||||
emojis.map(rawSticker => new Sticker(this.client, rawSticker)),
|
||||
emojis.map(rawSticker => Transformers.Sticker(this.client, rawSticker)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
14
src/cache/resources/threads.ts
vendored
14
src/cache/resources/threads.ts
vendored
@ -1,8 +1,8 @@
|
||||
import type { APIThreadChannel } from 'discord-api-types/v10';
|
||||
import type { ReturnCache } from '../..';
|
||||
import { fakePromise } from '../../common';
|
||||
import { ThreadChannel } from '../../structures';
|
||||
import { GuildRelatedResource } from './default/guild-related';
|
||||
import { type ThreadChannelStructure, Transformers } from '../../client/transformers';
|
||||
|
||||
export class Threads extends GuildRelatedResource {
|
||||
namespace = 'thread';
|
||||
@ -12,21 +12,21 @@ export class Threads extends GuildRelatedResource {
|
||||
return true;
|
||||
}
|
||||
|
||||
override get(id: string): ReturnCache<ThreadChannel | undefined> {
|
||||
override get(id: string): ReturnCache<ThreadChannelStructure | undefined> {
|
||||
return fakePromise(super.get(id)).then(rawThread =>
|
||||
rawThread ? new ThreadChannel(this.client, rawThread) : undefined,
|
||||
rawThread ? Transformers.ThreadChannel(this.client, rawThread) : undefined,
|
||||
);
|
||||
}
|
||||
|
||||
override bulk(ids: string[]): ReturnCache<ThreadChannel[]> {
|
||||
override bulk(ids: string[]): ReturnCache<ThreadChannelStructure[]> {
|
||||
return fakePromise(super.bulk(ids) as APIThreadChannel[]).then(threads =>
|
||||
threads.map(rawThread => new ThreadChannel(this.client, rawThread)),
|
||||
threads.map(rawThread => Transformers.ThreadChannel(this.client, rawThread)),
|
||||
);
|
||||
}
|
||||
|
||||
override values(guild: string): ReturnCache<ThreadChannel[]> {
|
||||
override values(guild: string): ReturnCache<ThreadChannelStructure[]> {
|
||||
return fakePromise(super.values(guild) as APIThreadChannel[]).then(threads =>
|
||||
threads.map(rawThread => new ThreadChannel(this.client, rawThread)),
|
||||
threads.map(rawThread => Transformers.ThreadChannel(this.client, rawThread)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
28
src/cache/resources/users.ts
vendored
28
src/cache/resources/users.ts
vendored
@ -1,8 +1,8 @@
|
||||
import type { APIUser } from 'discord-api-types/v10';
|
||||
import type { ReturnCache } from '../..';
|
||||
import { fakePromise } from '../../common';
|
||||
import { User } from '../../structures';
|
||||
import { BaseResource } from './default/base';
|
||||
import { Transformers, type UserStructure } from '../../client/transformers';
|
||||
|
||||
export class Users extends BaseResource {
|
||||
namespace = 'user';
|
||||
@ -12,17 +12,31 @@ export class Users extends BaseResource {
|
||||
return true;
|
||||
}
|
||||
|
||||
override get(id: string): ReturnCache<User | undefined> {
|
||||
return fakePromise(super.get(id)).then(rawUser => (rawUser ? new User(this.client, rawUser) : undefined));
|
||||
raw(id: string): ReturnCache<APIUser | undefined> {
|
||||
return super.get(id);
|
||||
}
|
||||
|
||||
override bulk(ids: string[]): ReturnCache<User[]> {
|
||||
override get(id: string): ReturnCache<UserStructure | undefined> {
|
||||
return fakePromise(super.get(id)).then(rawUser => (rawUser ? Transformers.User(this.client, rawUser) : undefined));
|
||||
}
|
||||
|
||||
override bulk(ids: string[]): ReturnCache<UserStructure[]> {
|
||||
return fakePromise(super.bulk(ids) as APIUser[]).then(users =>
|
||||
users.map(rawUser => new User(this.client, rawUser)),
|
||||
users.map(rawUser => Transformers.User(this.client, rawUser)),
|
||||
);
|
||||
}
|
||||
|
||||
override values(): ReturnCache<User[]> {
|
||||
return fakePromise(super.values() as APIUser[]).then(users => users.map(rawUser => new User(this.client, rawUser)));
|
||||
bulkRaw(ids: string[]): ReturnCache<APIUser[]> {
|
||||
return super.bulk(ids);
|
||||
}
|
||||
|
||||
override values(): ReturnCache<UserStructure[]> {
|
||||
return fakePromise(super.values() as APIUser[]).then(users =>
|
||||
users.map(rawUser => Transformers.User(this.client, rawUser)),
|
||||
);
|
||||
}
|
||||
|
||||
valuesRaw(): ReturnCache<APIUser[]> {
|
||||
return super.values();
|
||||
}
|
||||
}
|
||||
|
18
src/cache/resources/voice-states.ts
vendored
18
src/cache/resources/voice-states.ts
vendored
@ -1,8 +1,8 @@
|
||||
import type { GatewayVoiceState } from 'discord-api-types/v10';
|
||||
import type { ReturnCache } from '../..';
|
||||
import { fakePromise } from '../../common';
|
||||
import { VoiceState } from '../../structures';
|
||||
import { GuildBasedResource } from './default/guild-based';
|
||||
import { Transformers, type VoiceStateStructure } from '../../client/transformers';
|
||||
|
||||
export class VoiceStates extends GuildBasedResource {
|
||||
namespace = 'voice_state';
|
||||
@ -17,21 +17,25 @@ export class VoiceStates extends GuildBasedResource {
|
||||
return rest;
|
||||
}
|
||||
|
||||
override get(memberId: string, guildId: string): ReturnCache<VoiceState | undefined> {
|
||||
override get(memberId: string, guildId: string): ReturnCache<VoiceStateStructure | undefined> {
|
||||
return fakePromise(super.get(memberId, guildId)).then(state =>
|
||||
state ? new VoiceState(this.client, state) : undefined,
|
||||
state ? Transformers.VoiceState(this.client, state) : undefined,
|
||||
);
|
||||
}
|
||||
|
||||
override bulk(ids: string[], guild: string): ReturnCache<VoiceState[]> {
|
||||
override bulk(ids: string[], guild: string): ReturnCache<VoiceStateStructure[]> {
|
||||
return fakePromise(super.bulk(ids, guild)).then(
|
||||
states =>
|
||||
states.map(state => (state ? new VoiceState(this.client, state) : undefined)).filter(y => !!y) as VoiceState[],
|
||||
states
|
||||
.map(state => (state ? Transformers.VoiceState(this.client, state) : undefined))
|
||||
.filter(y => !!y) as VoiceStateStructure[],
|
||||
);
|
||||
}
|
||||
|
||||
override values(guildId: string): ReturnCache<VoiceState[]> {
|
||||
return fakePromise(super.values(guildId)).then(states => states.map(state => new VoiceState(this.client, state)));
|
||||
override values(guildId: string): ReturnCache<VoiceStateStructure[]> {
|
||||
return fakePromise(super.values(guildId)).then(states =>
|
||||
states.map(state => Transformers.VoiceState(this.client, state)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,9 +5,11 @@ import { Cache, MemoryAdapter } from '../cache';
|
||||
import type {
|
||||
Command,
|
||||
CommandContext,
|
||||
ContextMenuCommand,
|
||||
ExtraProps,
|
||||
OnOptionsReturnObject,
|
||||
MenuCommandContext,
|
||||
RegisteredMiddlewares,
|
||||
SubCommand,
|
||||
UsingClient,
|
||||
} from '../commands';
|
||||
import { IgnoreCommand, type InferWithPrefix, type MiddlewareContext } from '../commands/applications/shared';
|
||||
@ -40,7 +42,6 @@ import { LangsHandler } from '../langs/handler';
|
||||
import type {
|
||||
ChatInputCommandInteraction,
|
||||
ComponentInteraction,
|
||||
Message,
|
||||
MessageCommandInteraction,
|
||||
ModalSubmitInteraction,
|
||||
UserCommandInteraction,
|
||||
@ -48,6 +49,8 @@ import type {
|
||||
import type { ComponentCommand, ComponentContext, ModalCommand, ModalContext } from '../components';
|
||||
import { promises } from 'node:fs';
|
||||
import { BanShorter } from '../common/shorters/bans';
|
||||
import { HandleCommand } from '../commands/handle';
|
||||
import type { MessageStructure } from './transformers';
|
||||
|
||||
export class BaseClient {
|
||||
rest!: ApiHandler;
|
||||
@ -76,6 +79,7 @@ export class BaseClient {
|
||||
langs? = new LangsHandler(this.logger);
|
||||
commands? = new CommandHandler(this.logger, this);
|
||||
components? = new ComponentHandler(this.logger, this);
|
||||
handleCommand!: HandleCommand;
|
||||
|
||||
private _applicationId?: string;
|
||||
private _botId?: string;
|
||||
@ -101,30 +105,30 @@ export class BaseClient {
|
||||
{
|
||||
commands: {
|
||||
defaults: {
|
||||
onRunError(context: CommandContext<any>, error: unknown): any {
|
||||
onRunError(context, error): any {
|
||||
context.client.logger.fatal(`${context.command.name}.<onRunError>`, context.author.id, error);
|
||||
},
|
||||
onOptionsError(context: CommandContext<{}, never>, metadata: OnOptionsReturnObject): any {
|
||||
onOptionsError(context, metadata): any {
|
||||
context.client.logger.fatal(`${context.command.name}.<onOptionsError>`, context.author.id, metadata);
|
||||
},
|
||||
onMiddlewaresError(context: CommandContext<{}, never>, error: string): any {
|
||||
onMiddlewaresError(context, error: string): any {
|
||||
context.client.logger.fatal(`${context.command.name}.<onMiddlewaresError>`, context.author.id, error);
|
||||
},
|
||||
onBotPermissionsFail(context: CommandContext<{}, never>, permissions: PermissionStrings): any {
|
||||
onBotPermissionsFail(context, permissions): any {
|
||||
context.client.logger.fatal(
|
||||
`${context.command.name}.<onBotPermissionsFail>`,
|
||||
context.author.id,
|
||||
permissions,
|
||||
);
|
||||
},
|
||||
onPermissionsFail(context: CommandContext<{}, never>, permissions: PermissionStrings): any {
|
||||
onPermissionsFail(context, permissions): any {
|
||||
context.client.logger.fatal(
|
||||
`${context.command.name}.<onPermissionsFail>`,
|
||||
context.author.id,
|
||||
permissions,
|
||||
);
|
||||
},
|
||||
onInternalError(client: UsingClient, command: Command, error?: unknown): any {
|
||||
onInternalError(client: UsingClient, command, error?: unknown): any {
|
||||
client.logger.fatal(`${command.name}.<onInternalError>`, error);
|
||||
},
|
||||
},
|
||||
@ -180,7 +184,7 @@ export class BaseClient {
|
||||
return new Router(this.rest).createProxy();
|
||||
}
|
||||
|
||||
setServices({ rest, cache, langs, middlewares, handlers }: ServicesOptions) {
|
||||
setServices({ rest, cache, langs, middlewares, handlers, handleCommand }: ServicesOptions) {
|
||||
if (rest) {
|
||||
this.rest = rest;
|
||||
}
|
||||
@ -201,9 +205,6 @@ export class BaseClient {
|
||||
this.components = undefined;
|
||||
} else if (typeof handlers.components === 'function') {
|
||||
this.components ??= new ComponentHandler(this.logger, this);
|
||||
this.components.setHandlers({
|
||||
callback: handlers.components,
|
||||
});
|
||||
} else {
|
||||
this.components = handlers.components;
|
||||
}
|
||||
@ -213,7 +214,6 @@ export class BaseClient {
|
||||
this.commands = undefined;
|
||||
} else if (typeof handlers.commands === 'object') {
|
||||
this.commands ??= new CommandHandler(this.logger, this);
|
||||
this.commands.setHandlers(handlers.commands);
|
||||
} else {
|
||||
this.commands = handlers.commands;
|
||||
}
|
||||
@ -233,6 +233,8 @@ export class BaseClient {
|
||||
if (langs.default) this.langs!.defaultLang = langs.default;
|
||||
if (langs.aliases) this.langs!.aliases = Object.entries(langs.aliases);
|
||||
}
|
||||
|
||||
if (handleCommand) this.handleCommand = new handleCommand(this);
|
||||
}
|
||||
|
||||
protected async execute(..._options: unknown[]) {
|
||||
@ -275,6 +277,8 @@ export class BaseClient {
|
||||
} else {
|
||||
this.cache = new Cache(0, new MemoryAdapter(), [], this);
|
||||
}
|
||||
|
||||
if (!this.handleCommand) this.handleCommand = new HandleCommand(this);
|
||||
}
|
||||
|
||||
protected async onPacket(..._packet: unknown[]) {
|
||||
@ -383,18 +387,25 @@ export interface BaseClientOptions {
|
||||
| MessageCommandInteraction<boolean>
|
||||
| ComponentInteraction
|
||||
| ModalSubmitInteraction
|
||||
| When<InferWithPrefix, Message, never>,
|
||||
| When<InferWithPrefix, MessageStructure, never>,
|
||||
) => {};
|
||||
globalMiddlewares?: readonly (keyof RegisteredMiddlewares)[];
|
||||
commands?: {
|
||||
defaults?: {
|
||||
onRunError?: Command['onRunError'];
|
||||
onRunError?: (context: MenuCommandContext<any, never> | CommandContext, error: unknown) => unknown;
|
||||
onPermissionsFail?: Command['onPermissionsFail'];
|
||||
onBotPermissionsFail?: Command['onBotPermissionsFail'];
|
||||
onInternalError?: Command['onInternalError'];
|
||||
onMiddlewaresError?: Command['onMiddlewaresError'];
|
||||
onBotPermissionsFail?: (
|
||||
context: MenuCommandContext<any, never> | CommandContext,
|
||||
permissions: PermissionStrings,
|
||||
) => unknown;
|
||||
onInternalError?: (
|
||||
client: UsingClient,
|
||||
command: Command | SubCommand | ContextMenuCommand,
|
||||
error?: unknown,
|
||||
) => unknown;
|
||||
onMiddlewaresError?: (context: CommandContext | MenuCommandContext<any, never>, error: string) => unknown;
|
||||
onOptionsError?: Command['onOptionsError'];
|
||||
onAfterRun?: Command['onAfterRun'];
|
||||
onAfterRun?: (context: CommandContext | MenuCommandContext<any, never>, error: unknown) => unknown;
|
||||
props?: ExtraProps;
|
||||
};
|
||||
};
|
||||
@ -480,7 +491,8 @@ export interface ServicesOptions {
|
||||
middlewares?: Record<string, MiddlewareContext>;
|
||||
handlers?: {
|
||||
components?: ComponentHandler | ComponentHandler['callback'];
|
||||
commands?: CommandHandler | Parameters<CommandHandler['setHandlers']>[0];
|
||||
commands?: CommandHandler;
|
||||
langs?: LangsHandler | LangsHandler['callback'];
|
||||
};
|
||||
handleCommand?: typeof HandleCommand;
|
||||
}
|
||||
|
@ -1,23 +1,7 @@
|
||||
import {
|
||||
type APIApplicationCommandInteractionDataOption,
|
||||
GatewayIntentBits,
|
||||
type GatewayMessageCreateDispatchData,
|
||||
type GatewayDispatchPayload,
|
||||
type GatewayPresenceUpdateData,
|
||||
} from 'discord-api-types/v10';
|
||||
import type {
|
||||
Command,
|
||||
CommandContext,
|
||||
ContextOptionsResolved,
|
||||
Message,
|
||||
MessageCommandOptionErrors,
|
||||
SubCommand,
|
||||
UsingClient,
|
||||
} from '..';
|
||||
import { GatewayIntentBits, type GatewayDispatchPayload, type GatewayPresenceUpdateData } from 'discord-api-types/v10';
|
||||
import type { CommandContext, Message } from '..';
|
||||
import {
|
||||
type Awaitable,
|
||||
type MakeRequired,
|
||||
MergeOptions,
|
||||
lazyLoadPackage,
|
||||
type DeepPartial,
|
||||
type If,
|
||||
@ -25,24 +9,22 @@ import {
|
||||
type WatcherSendToShard,
|
||||
} from '../common';
|
||||
import { EventHandler } from '../events';
|
||||
import { ClientUser } from '../structures';
|
||||
import { ShardManager, properties, type ShardManagerOptions } from '../websocket';
|
||||
import { MemberUpdateHandler } from '../websocket/discord/events/memberUpdate';
|
||||
import { PresenceUpdateHandler } from '../websocket/discord/events/presenceUpdate';
|
||||
import type { BaseClientOptions, InternalRuntimeConfig, ServicesOptions, StartOptions } from './base';
|
||||
import { BaseClient } from './base';
|
||||
import { onInteractionCreate } from './oninteractioncreate';
|
||||
import { defaultArgsParser, defaultOptionsParser, onMessageCreate } from './onmessagecreate';
|
||||
import { Collectors } from './collectors';
|
||||
import { type ClientUserStructure, Transformers, type MessageStructure } from './transformers';
|
||||
|
||||
let parentPort: import('node:worker_threads').MessagePort;
|
||||
|
||||
export class Client<Ready extends boolean = boolean> extends BaseClient {
|
||||
private __handleGuilds?: Set<string> = new Set();
|
||||
gateway!: ShardManager;
|
||||
me!: If<Ready, ClientUser>;
|
||||
me!: If<Ready, ClientUserStructure>;
|
||||
declare options: Omit<ClientOptions, 'commands'> & {
|
||||
commands: MakeRequired<NonNullable<ClientOptions['commands']>, 'argsParser' | 'optionsParser'>;
|
||||
commands: NonNullable<ClientOptions['commands']>;
|
||||
};
|
||||
memberUpdateHandler = new MemberUpdateHandler();
|
||||
presenceUpdateHandler = new PresenceUpdateHandler();
|
||||
@ -51,15 +33,6 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
|
||||
|
||||
constructor(options?: ClientOptions) {
|
||||
super(options);
|
||||
this.options = MergeOptions(
|
||||
{
|
||||
commands: {
|
||||
argsParser: options?.commands?.argsParser ?? defaultArgsParser,
|
||||
optionsParser: options?.commands?.optionsParser ?? defaultOptionsParser,
|
||||
},
|
||||
} satisfies ClientOptions,
|
||||
this.options,
|
||||
);
|
||||
}
|
||||
|
||||
setServices({
|
||||
@ -86,9 +59,6 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
|
||||
this.events = undefined;
|
||||
} else if (typeof rest.handlers.events === 'function') {
|
||||
this.events = new EventHandler(this);
|
||||
this.events.setHandlers({
|
||||
callback: rest.handlers.events,
|
||||
});
|
||||
} else {
|
||||
this.events = rest.handlers.events;
|
||||
}
|
||||
@ -169,7 +139,10 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
|
||||
}
|
||||
|
||||
protected async onPacket(shardId: number, packet: GatewayDispatchPayload) {
|
||||
await this.events?.runEvent('RAW', this, packet, shardId);
|
||||
Promise.allSettled([
|
||||
this.events?.runEvent('RAW', this, packet, shardId, false),
|
||||
this.collectors.run('RAW', packet),
|
||||
]); //ignore promise
|
||||
switch (packet.t) {
|
||||
//// Cases where we must obtain the old data before updating
|
||||
case 'GUILD_MEMBER_UPDATE':
|
||||
@ -179,7 +152,7 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
|
||||
await this.events?.execute(packet.t, packet, this as Client<true>, shardId);
|
||||
break;
|
||||
case 'PRESENCE_UPDATE':
|
||||
if (!this.presenceUpdateHandler.check(packet.d as any)) {
|
||||
if (!this.presenceUpdateHandler.check(packet.d)) {
|
||||
return;
|
||||
}
|
||||
await this.events?.execute(packet.t, packet, this as Client<true>, shardId);
|
||||
@ -198,24 +171,25 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
|
||||
}
|
||||
//rest of the events
|
||||
default: {
|
||||
await this.events?.execute(packet.t, packet, this as Client<true>, shardId);
|
||||
await this.events?.execute(packet.t as never, packet, this as Client<true>, shardId);
|
||||
switch (packet.t) {
|
||||
case 'INTERACTION_CREATE':
|
||||
await onInteractionCreate(this, packet.d, shardId);
|
||||
await this.handleCommand.interaction(packet.d, shardId);
|
||||
break;
|
||||
case 'MESSAGE_CREATE':
|
||||
await onMessageCreate(this, packet.d, shardId);
|
||||
await this.handleCommand.message(packet.d, shardId);
|
||||
break;
|
||||
case 'READY':
|
||||
if (!this.__handleGuilds) this.__handleGuilds = new Set();
|
||||
for (const g of packet.d.guilds) {
|
||||
this.__handleGuilds?.add(g.id);
|
||||
this.__handleGuilds.add(g.id);
|
||||
}
|
||||
this.botId = packet.d.user.id;
|
||||
this.applicationId = packet.d.application.id;
|
||||
this.me = new ClientUser(this, packet.d.user, packet.d.application) as never;
|
||||
this.me = Transformers.ClientUser(this, packet.d.user, packet.d.application) as never;
|
||||
if (
|
||||
!(
|
||||
this.__handleGuilds?.size &&
|
||||
this.__handleGuilds.size &&
|
||||
(this.gateway.options.intents & GatewayIntentBits.Guilds) === GatewayIntentBits.Guilds
|
||||
)
|
||||
) {
|
||||
@ -245,24 +219,9 @@ export interface ClientOptions extends BaseClientOptions {
|
||||
compress?: ShardManagerOptions['compress'];
|
||||
};
|
||||
commands?: BaseClientOptions['commands'] & {
|
||||
prefix?: (message: Message) => Promise<string[]> | string[];
|
||||
prefix?: (message: MessageStructure) => Awaitable<string[]>;
|
||||
deferReplyResponse?: (ctx: CommandContext) => Parameters<Message['write']>[0];
|
||||
reply?: (ctx: CommandContext) => boolean;
|
||||
argsParser?: (content: string, command: SubCommand | Command, message: Message) => Record<string, string>;
|
||||
optionsParser?: (
|
||||
self: UsingClient,
|
||||
command: Command | SubCommand,
|
||||
message: GatewayMessageCreateDispatchData,
|
||||
args: Partial<Record<string, string>>,
|
||||
resolved: MakeRequired<ContextOptionsResolved>,
|
||||
) => Awaitable<{
|
||||
errors: {
|
||||
name: string;
|
||||
error: string;
|
||||
fullError: MessageCommandOptionErrors;
|
||||
}[];
|
||||
options: APIApplicationCommandInteractionDataOption[];
|
||||
}>;
|
||||
};
|
||||
handlePayload?: ShardManagerOptions['handlePayload'];
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import { isBufferLike } from '../api/utils/utils';
|
||||
import { MergeOptions, isCloudfareWorker, type DeepPartial } from '../common';
|
||||
import type { BaseClientOptions, InternalRuntimeConfigHTTP, StartOptions } from './base';
|
||||
import { BaseClient } from './base';
|
||||
import { onInteractionCreate } from './oninteractioncreate';
|
||||
|
||||
let UWS: typeof import('uWebSockets.js') | undefined;
|
||||
let nacl: typeof import('tweetnacl') | undefined;
|
||||
@ -164,10 +163,11 @@ export class HttpClient extends BaseClient {
|
||||
default:
|
||||
return new Promise<Response>(r => {
|
||||
if (isCloudfareWorker())
|
||||
return onInteractionCreate(this, rawBody, -1)
|
||||
return this.handleCommand
|
||||
.interaction(rawBody, -1)
|
||||
.then(() => r(new Response()))
|
||||
.catch(() => r(new Response()));
|
||||
return onInteractionCreate(this, rawBody, -1, async ({ body, files }) => {
|
||||
return this.handleCommand.interaction(rawBody, -1, async ({ body, files }) => {
|
||||
let response: FormData | APIInteractionResponse;
|
||||
const headers: { 'Content-Type'?: string } = {};
|
||||
|
||||
@ -224,7 +224,7 @@ export class HttpClient extends BaseClient {
|
||||
.end(JSON.stringify({ type: InteractionResponseType.Pong }));
|
||||
break;
|
||||
default:
|
||||
await onInteractionCreate(this, rawBody, -1, async ({ body, files }) => {
|
||||
await this.handleCommand.interaction(rawBody, -1, async ({ body, files }) => {
|
||||
res.cork(() => {
|
||||
let response: FormData | APIInteractionResponse;
|
||||
const headers: { 'Content-Type'?: string } = {};
|
||||
|
@ -2,3 +2,4 @@ export type { RuntimeConfig, RuntimeConfigHTTP } from './base';
|
||||
export * from './client';
|
||||
export * from './httpclient';
|
||||
export * from './workerclient';
|
||||
export * from './transformers';
|
||||
|
@ -1,244 +0,0 @@
|
||||
import { ApplicationCommandType, InteractionType, type APIInteraction } from 'discord-api-types/v10';
|
||||
import {
|
||||
BaseCommand,
|
||||
CommandContext,
|
||||
MenuCommandContext,
|
||||
OptionResolver,
|
||||
type RegisteredMiddlewares,
|
||||
type Command,
|
||||
type ContextMenuCommand,
|
||||
type ContextOptionsResolved,
|
||||
type UsingClient,
|
||||
} from '../commands';
|
||||
import type {
|
||||
ChatInputCommandInteraction,
|
||||
ComponentInteraction,
|
||||
MessageCommandInteraction,
|
||||
ModalSubmitInteraction,
|
||||
UserCommandInteraction,
|
||||
__InternalReplyFunction,
|
||||
} from '../structures';
|
||||
import { AutocompleteInteraction, BaseInteraction } from '../structures';
|
||||
import { ComponentContext, ModalContext } from '../components';
|
||||
|
||||
export async function onInteractionCreate(
|
||||
self: UsingClient,
|
||||
body: APIInteraction,
|
||||
shardId: number,
|
||||
__reply?: __InternalReplyFunction,
|
||||
) {
|
||||
self.debugger?.debug(`[${InteractionType[body.type] ?? body.type}] Interaction received.`);
|
||||
switch (body.type) {
|
||||
case InteractionType.ApplicationCommandAutocomplete:
|
||||
{
|
||||
const parentCommand = self.commands?.values.find(x => {
|
||||
if (body.data.guild_id) {
|
||||
return x.guildId?.includes(body.data.guild_id) && x.name === body.data.name;
|
||||
}
|
||||
return x.name === body.data.name;
|
||||
});
|
||||
const optionsResolver = new OptionResolver(
|
||||
self,
|
||||
body.data.options ?? [],
|
||||
parentCommand as Command,
|
||||
body.guild_id,
|
||||
body.data.resolved as ContextOptionsResolved,
|
||||
);
|
||||
const interaction = new AutocompleteInteraction(self, body, optionsResolver, __reply);
|
||||
const command = optionsResolver.getAutocomplete();
|
||||
|
||||
// idc, is a YOU problem
|
||||
if (!command?.autocomplete)
|
||||
return self.logger.warn(
|
||||
`${optionsResolver.fullCommandName} ${command?.name} command does not have 'autocomplete' callback`,
|
||||
);
|
||||
try {
|
||||
try {
|
||||
await command.autocomplete(interaction);
|
||||
} catch (error) {
|
||||
if (!command.onAutocompleteError)
|
||||
return self.logger.error(
|
||||
`${optionsResolver.fullCommandName} ${command.name} just threw an error, ${
|
||||
error ? (typeof error === 'object' && 'message' in error ? error.message : error) : 'Unknown'
|
||||
}`,
|
||||
);
|
||||
await command.onAutocompleteError(interaction, error);
|
||||
}
|
||||
} catch (error) {
|
||||
try {
|
||||
await optionsResolver.getCommand()?.onInternalError?.(self, optionsResolver.getCommand()!, error);
|
||||
} catch {
|
||||
// supress error
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case InteractionType.ApplicationCommand:
|
||||
{
|
||||
switch (body.data.type) {
|
||||
case ApplicationCommandType.Message:
|
||||
case ApplicationCommandType.User:
|
||||
{
|
||||
const command = self.commands?.values.find(x => {
|
||||
if (body.data.guild_id) {
|
||||
return x.guildId?.includes(body.data.guild_id) && x.name === body.data.name;
|
||||
}
|
||||
return x.name === body.data.name;
|
||||
}) as ContextMenuCommand;
|
||||
const interaction = BaseInteraction.from(self, body, __reply) as
|
||||
| UserCommandInteraction
|
||||
| MessageCommandInteraction;
|
||||
// idc, is a YOU problem
|
||||
if (!command?.run)
|
||||
return self.logger.warn(`${command.name ?? 'Unknown'} command does not have 'run' callback`);
|
||||
const context = new MenuCommandContext(self, interaction, shardId, command);
|
||||
const extendContext = self.options?.context?.(interaction) ?? {};
|
||||
Object.assign(context, extendContext);
|
||||
try {
|
||||
if (command.botPermissions && interaction.appPermissions) {
|
||||
const permissions = interaction.appPermissions.missings(
|
||||
...interaction.appPermissions.values([command.botPermissions]),
|
||||
);
|
||||
if (!interaction.appPermissions.has('Administrator') && permissions.length) {
|
||||
return command.onBotPermissionsFail(context, interaction.appPermissions.keys(permissions));
|
||||
}
|
||||
}
|
||||
const resultRunGlobalMiddlewares = await BaseCommand.__runMiddlewares(
|
||||
context,
|
||||
(self.options?.globalMiddlewares ?? []) as keyof RegisteredMiddlewares,
|
||||
true,
|
||||
);
|
||||
if (resultRunGlobalMiddlewares.pass) {
|
||||
return;
|
||||
}
|
||||
if ('error' in resultRunGlobalMiddlewares) {
|
||||
return command.onMiddlewaresError(context, resultRunGlobalMiddlewares.error ?? 'Unknown error');
|
||||
}
|
||||
|
||||
const resultRunMiddlewares = await BaseCommand.__runMiddlewares(
|
||||
context,
|
||||
command.middlewares as keyof RegisteredMiddlewares,
|
||||
false,
|
||||
);
|
||||
if (resultRunMiddlewares.pass) {
|
||||
return;
|
||||
}
|
||||
if ('error' in resultRunMiddlewares) {
|
||||
return command.onMiddlewaresError(context, resultRunMiddlewares.error ?? 'Unknown error');
|
||||
}
|
||||
|
||||
try {
|
||||
await command.run(context);
|
||||
await command.onAfterRun?.(context, undefined);
|
||||
} catch (error) {
|
||||
await command.onRunError(context, error);
|
||||
await command.onAfterRun?.(context, error);
|
||||
}
|
||||
} catch (error) {
|
||||
try {
|
||||
await command.onInternalError(self, error);
|
||||
} catch {
|
||||
// supress error
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ApplicationCommandType.ChatInput:
|
||||
{
|
||||
const parentCommand = self.commands?.values.find(x => {
|
||||
if (body.data.guild_id) {
|
||||
return x.guildId?.includes(body.data.guild_id) && x.name === body.data.name;
|
||||
}
|
||||
return x.name === body.data.name;
|
||||
});
|
||||
const optionsResolver = new OptionResolver(
|
||||
self,
|
||||
body.data.options ?? [],
|
||||
parentCommand as Command,
|
||||
body.guild_id,
|
||||
body.data.resolved as ContextOptionsResolved,
|
||||
);
|
||||
const interaction = BaseInteraction.from(self, body, __reply) as ChatInputCommandInteraction;
|
||||
const command = optionsResolver.getCommand();
|
||||
if (!command?.run)
|
||||
return self.logger.warn(`${optionsResolver.fullCommandName} command does not have 'run' callback`);
|
||||
const context = new CommandContext(self, interaction, optionsResolver, shardId, command);
|
||||
const extendContext = self.options?.context?.(interaction) ?? {};
|
||||
Object.assign(context, extendContext);
|
||||
try {
|
||||
if (command.botPermissions && interaction.appPermissions) {
|
||||
const permissions = interaction.appPermissions.missings(
|
||||
...interaction.appPermissions.values([command.botPermissions]),
|
||||
);
|
||||
if (!interaction.appPermissions.has('Administrator') && permissions.length) {
|
||||
return command.onBotPermissionsFail?.(context, interaction.appPermissions.keys(permissions));
|
||||
}
|
||||
}
|
||||
const [erroredOptions, result] = await command.__runOptions(context, optionsResolver);
|
||||
if (erroredOptions) {
|
||||
return command.onOptionsError?.(context, result);
|
||||
}
|
||||
const resultRunGlobalMiddlewares = await command.__runGlobalMiddlewares(context);
|
||||
if (resultRunGlobalMiddlewares.pass) {
|
||||
return;
|
||||
}
|
||||
if ('error' in resultRunGlobalMiddlewares) {
|
||||
return command.onMiddlewaresError?.(context, resultRunGlobalMiddlewares.error ?? 'Unknown error');
|
||||
}
|
||||
const resultRunMiddlewares = await command.__runMiddlewares(context);
|
||||
if (resultRunMiddlewares.pass) {
|
||||
return;
|
||||
}
|
||||
if ('error' in resultRunMiddlewares) {
|
||||
return command.onMiddlewaresError?.(context, resultRunMiddlewares.error ?? 'Unknown error');
|
||||
}
|
||||
|
||||
try {
|
||||
await command.run(context);
|
||||
await command.onAfterRun?.(context, undefined);
|
||||
} catch (error) {
|
||||
await command.onRunError?.(context, error);
|
||||
await command.onAfterRun?.(context, error);
|
||||
}
|
||||
} catch (error) {
|
||||
try {
|
||||
await command.onInternalError?.(self, context.command, error);
|
||||
} catch {
|
||||
// supress error
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case InteractionType.ModalSubmit:
|
||||
{
|
||||
const interaction = BaseInteraction.from(self, body, __reply) as ModalSubmitInteraction;
|
||||
if (self.components?.hasModal(interaction)) {
|
||||
await self.components.onModalSubmit(interaction);
|
||||
} else {
|
||||
const context = new ModalContext(self, interaction);
|
||||
const extended = self.options?.context?.(interaction) ?? {};
|
||||
Object.assign(context, extended);
|
||||
await self.components?.executeModal(context);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case InteractionType.MessageComponent:
|
||||
{
|
||||
const interaction = BaseInteraction.from(self, body, __reply) as ComponentInteraction;
|
||||
if (self.components?.hasComponent(body.message.id, interaction.customId)) {
|
||||
await self.components.onComponent(body.message.id, interaction);
|
||||
} else {
|
||||
//@ts-expect-error
|
||||
const context = new ComponentContext(self, interaction);
|
||||
const extended = self.options?.context?.(interaction) ?? {};
|
||||
Object.assign(context, extended);
|
||||
await self.components?.executeComponent(context);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
@ -1,471 +0,0 @@
|
||||
import {
|
||||
ApplicationCommandOptionType,
|
||||
ChannelType,
|
||||
InteractionContextType,
|
||||
type APIApplicationCommandInteractionDataOption,
|
||||
type GatewayMessageCreateDispatchData,
|
||||
} from 'discord-api-types/v10';
|
||||
import {
|
||||
Command,
|
||||
CommandContext,
|
||||
IgnoreCommand,
|
||||
type MessageCommandOptionErrors,
|
||||
OptionResolver,
|
||||
SubCommand,
|
||||
User,
|
||||
type UsingClient,
|
||||
type Client,
|
||||
type CommandOption,
|
||||
type ContextOptionsResolved,
|
||||
type SeyfertChannelOption,
|
||||
type SeyfertIntegerOption,
|
||||
type SeyfertNumberOption,
|
||||
type SeyfertStringOption,
|
||||
type WorkerClient,
|
||||
} from '..';
|
||||
import type { MakeRequired } from '../common';
|
||||
import { Message } from '../structures';
|
||||
|
||||
function getCommandFromContent(
|
||||
commandRaw: string[],
|
||||
self: Client | WorkerClient,
|
||||
): {
|
||||
command?: Command | SubCommand;
|
||||
parent?: Command;
|
||||
fullCommandName: string;
|
||||
} {
|
||||
const rawParentName = commandRaw[0];
|
||||
const rawGroupName = commandRaw.length === 3 ? commandRaw[1] : undefined;
|
||||
const rawSubcommandName = rawGroupName ? commandRaw[2] : commandRaw[1];
|
||||
const parent = self.commands!.values.find(
|
||||
x =>
|
||||
(!('ignore' in x) || x.ignore !== IgnoreCommand.Message) &&
|
||||
(x.name === rawParentName || ('aliases' in x ? x.aliases?.includes(rawParentName) : false)),
|
||||
);
|
||||
const fullCommandName = `${rawParentName}${
|
||||
rawGroupName ? ` ${rawGroupName} ${rawSubcommandName}` : `${rawSubcommandName ? ` ${rawSubcommandName}` : ''}`
|
||||
}`;
|
||||
|
||||
if (!(parent instanceof Command)) return { fullCommandName };
|
||||
|
||||
if (rawGroupName && !parent.groups?.[rawGroupName] && !parent.groupsAliases?.[rawGroupName])
|
||||
return getCommandFromContent([rawParentName, rawGroupName], self);
|
||||
if (
|
||||
rawSubcommandName &&
|
||||
!parent.options?.some(
|
||||
x => x instanceof SubCommand && (x.name === rawSubcommandName || x.aliases?.includes(rawSubcommandName)),
|
||||
)
|
||||
)
|
||||
return getCommandFromContent([rawParentName], self);
|
||||
|
||||
const groupName = rawGroupName ? parent.groupsAliases?.[rawGroupName] || rawGroupName : undefined;
|
||||
|
||||
const command =
|
||||
groupName || rawSubcommandName
|
||||
? (parent.options?.find(opt => {
|
||||
if (opt instanceof SubCommand) {
|
||||
if (groupName) {
|
||||
if (opt.group !== groupName) return false;
|
||||
}
|
||||
if (opt.group && !groupName) return false;
|
||||
return rawSubcommandName === opt.name || opt.aliases?.includes(rawSubcommandName);
|
||||
}
|
||||
return false;
|
||||
}) as SubCommand)
|
||||
: parent;
|
||||
|
||||
return {
|
||||
command,
|
||||
fullCommandName,
|
||||
parent,
|
||||
};
|
||||
}
|
||||
|
||||
export async function onMessageCreate(
|
||||
self: Client | WorkerClient,
|
||||
rawMessage: GatewayMessageCreateDispatchData,
|
||||
shardId: number,
|
||||
) {
|
||||
if (!self.options?.commands?.prefix) return;
|
||||
const message = new Message(self, rawMessage);
|
||||
const prefixes = (await self.options.commands.prefix(message)).sort((a, b) => b.length - a.length);
|
||||
const prefix = prefixes.find(x => message.content.startsWith(x));
|
||||
|
||||
if (!(prefix !== undefined && message.content.startsWith(prefix))) return;
|
||||
|
||||
const content = message.content.slice(prefix.length).trimStart();
|
||||
const { fullCommandName, command, parent } = getCommandFromContent(
|
||||
content
|
||||
.split(' ')
|
||||
.filter(x => x)
|
||||
.slice(0, 3),
|
||||
self,
|
||||
);
|
||||
|
||||
if (!command) return;
|
||||
if (!command.run) return self.logger.warn(`${fullCommandName} command does not have 'run' callback`);
|
||||
|
||||
if (!(command.contexts.includes(InteractionContextType.BotDM) || message.guildId)) return;
|
||||
if (!command.contexts.includes(InteractionContextType.Guild) && message.guildId) return;
|
||||
if (command.guildId && !command.guildId?.includes(message.guildId!)) return;
|
||||
|
||||
const resolved: MakeRequired<ContextOptionsResolved> = {
|
||||
channels: {},
|
||||
roles: {},
|
||||
users: {},
|
||||
members: {},
|
||||
attachments: {},
|
||||
};
|
||||
|
||||
let newContent = content;
|
||||
for (const i of fullCommandName.split(' ')) {
|
||||
newContent = newContent.slice(newContent.indexOf(i) + i.length);
|
||||
}
|
||||
|
||||
const args = self.options.commands.argsParser(newContent.slice(1), command, message);
|
||||
const { options, errors } = await self.options.commands.optionsParser(self, command, rawMessage, args, resolved);
|
||||
const optionsResolver = new OptionResolver(self, options, parent as Command, message.guildId, resolved);
|
||||
const context = new CommandContext(self, message, optionsResolver, shardId, command);
|
||||
//@ts-expect-error
|
||||
const extendContext = self.options?.context?.(message) ?? {};
|
||||
Object.assign(context, extendContext);
|
||||
try {
|
||||
if (errors.length) {
|
||||
return command.onOptionsError?.(
|
||||
context,
|
||||
Object.fromEntries(
|
||||
errors.map(x => {
|
||||
return [
|
||||
x.name,
|
||||
{
|
||||
failed: true,
|
||||
value: x.error,
|
||||
parseError: x.fullError,
|
||||
},
|
||||
];
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (command.defaultMemberPermissions && message.guildId) {
|
||||
const memberPermissions = await self.members.permissions(message.guildId, message.author.id);
|
||||
const permissions = memberPermissions.missings(...memberPermissions.values([command.defaultMemberPermissions]));
|
||||
if (
|
||||
!memberPermissions.has('Administrator') &&
|
||||
permissions.length &&
|
||||
(await message.guild())!.ownerId !== message.author.id
|
||||
) {
|
||||
return command.onPermissionsFail?.(context, memberPermissions.keys(permissions));
|
||||
}
|
||||
}
|
||||
if (command.botPermissions && message.guildId) {
|
||||
const meMember = await self.cache.members?.get(self.botId, message.guildId);
|
||||
if (!meMember) return; //enable member cache and "Guilds" intent, lol
|
||||
const appPermissions = await meMember.fetchPermissions();
|
||||
const permissions = appPermissions.missings(...appPermissions.values([command.botPermissions]));
|
||||
if (!appPermissions.has('Administrator') && permissions.length) {
|
||||
return command.onBotPermissionsFail?.(context, appPermissions.keys(permissions));
|
||||
}
|
||||
}
|
||||
const [erroredOptions, result] = await command.__runOptions(context, optionsResolver);
|
||||
if (erroredOptions) {
|
||||
return command.onOptionsError?.(context, result);
|
||||
}
|
||||
const resultRunGlobalMiddlewares = await command.__runGlobalMiddlewares(context);
|
||||
if (resultRunGlobalMiddlewares.pass) {
|
||||
return;
|
||||
}
|
||||
if ('error' in resultRunGlobalMiddlewares) {
|
||||
return command.onMiddlewaresError?.(context, resultRunGlobalMiddlewares.error ?? 'Unknown error');
|
||||
}
|
||||
const resultRunMiddlewares = await command.__runMiddlewares(context);
|
||||
if (resultRunMiddlewares.pass) {
|
||||
return;
|
||||
}
|
||||
if ('error' in resultRunMiddlewares) {
|
||||
return command.onMiddlewaresError?.(context, resultRunMiddlewares.error ?? 'Unknown error');
|
||||
}
|
||||
|
||||
try {
|
||||
await command.run?.(context);
|
||||
await command.onAfterRun?.(context, undefined);
|
||||
} catch (error) {
|
||||
await command.onRunError?.(context, error);
|
||||
await command.onAfterRun?.(context, error);
|
||||
}
|
||||
} catch (error) {
|
||||
try {
|
||||
await command.onInternalError?.(self, context.command, error);
|
||||
} catch {
|
||||
// supress error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function defaultOptionsParser(
|
||||
self: UsingClient,
|
||||
command: Command | SubCommand,
|
||||
message: GatewayMessageCreateDispatchData,
|
||||
args: Partial<Record<string, string>>,
|
||||
resolved: MakeRequired<ContextOptionsResolved>,
|
||||
) {
|
||||
const options: APIApplicationCommandInteractionDataOption[] = [];
|
||||
const errors: { name: string; error: string; fullError: MessageCommandOptionErrors }[] = [];
|
||||
for (const i of (command.options ?? []) as (CommandOption & { type: ApplicationCommandOptionType })[]) {
|
||||
try {
|
||||
let value: string | boolean | number | undefined;
|
||||
let indexAttachment = -1;
|
||||
switch (i.type) {
|
||||
case ApplicationCommandOptionType.Attachment:
|
||||
if (message.attachments[++indexAttachment]) {
|
||||
value = message.attachments[indexAttachment].id;
|
||||
resolved.attachments[value] = message.attachments[indexAttachment];
|
||||
}
|
||||
break;
|
||||
case ApplicationCommandOptionType.Boolean:
|
||||
if (args[i.name]) {
|
||||
value = ['yes', 'y', 'true', 'treu'].includes(args[i.name]!.toLowerCase());
|
||||
}
|
||||
break;
|
||||
case ApplicationCommandOptionType.Channel:
|
||||
{
|
||||
const rawId =
|
||||
message.content.match(/(?<=<#)[0-9]{17,19}(?=>)/g)?.find(x => args[i.name]?.includes(x)) ||
|
||||
args[i.name]?.match(/[0-9]{17,19}/g)?.[0];
|
||||
if (rawId) {
|
||||
const channel =
|
||||
(await self.cache.channels?.get(rawId)) ?? (i.required ? await self.channels.fetch(rawId) : undefined);
|
||||
if (channel) {
|
||||
if ('channel_types' in i) {
|
||||
if (!(i as SeyfertChannelOption).channel_types!.includes(channel.type)) {
|
||||
errors.push({
|
||||
name: i.name,
|
||||
error: `The entered channel type is not one of ${(i as SeyfertChannelOption)
|
||||
.channel_types!.map(t => ChannelType[t])
|
||||
.join(', ')}`,
|
||||
fullError: ['CHANNEL_TYPES', (i as SeyfertChannelOption).channel_types!],
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
value = rawId;
|
||||
resolved.channels[rawId] = channel;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ApplicationCommandOptionType.Mentionable:
|
||||
{
|
||||
const matches = message.content.match(/<@[0-9]{17,19}(?=>)|<@&[0-9]{17,19}(?=>)/g) ?? [];
|
||||
for (const match of matches) {
|
||||
if (match.includes('&')) {
|
||||
const rawId = match.slice(3);
|
||||
if (rawId) {
|
||||
const role =
|
||||
(await self.cache.roles?.get(rawId)) ??
|
||||
(i.required ? (await self.roles.list(message.guild_id!)).find(x => x.id === rawId) : undefined);
|
||||
if (role) {
|
||||
value = rawId;
|
||||
resolved.roles[rawId] = role;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const rawId = match.slice(2);
|
||||
const raw = message.mentions.find(x => rawId === x.id);
|
||||
if (raw) {
|
||||
const { member, ...user } = raw;
|
||||
value = raw.id;
|
||||
resolved.users[raw.id] = user;
|
||||
if (member) resolved.members[raw.id] = member;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ApplicationCommandOptionType.Role:
|
||||
{
|
||||
const rawId =
|
||||
message.mention_roles.find(x => args[i.name]?.includes(x)) || args[i.name]?.match(/[0-9]{17,19}/g)?.[0];
|
||||
if (rawId) {
|
||||
const role =
|
||||
(await self.cache.roles?.get(rawId)) ??
|
||||
(i.required ? (await self.roles.list(message.guild_id!)).find(x => x.id === rawId) : undefined);
|
||||
|
||||
if (role) {
|
||||
value = rawId;
|
||||
resolved.roles[rawId] = role;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ApplicationCommandOptionType.User:
|
||||
{
|
||||
const rawId =
|
||||
message.mentions.find(x => args[i.name]?.includes(x.id))?.id || args[i.name]?.match(/[0-9]{17,19}/g)?.[0];
|
||||
if (rawId) {
|
||||
const raw =
|
||||
message.mentions.find(x => args[i.name]?.includes(x.id)) ??
|
||||
(await self.cache.users?.get(rawId)) ??
|
||||
(i.required ? await self.users.fetch(rawId) : undefined);
|
||||
if (raw) {
|
||||
value = raw.id;
|
||||
if (raw instanceof User) {
|
||||
resolved.users[raw.id] = raw;
|
||||
if (message.guild_id) {
|
||||
const member =
|
||||
message.mentions.find(x => args[i.name]?.includes(x.id))?.member ??
|
||||
(await self.cache.members?.get(rawId, message.guild_id)) ??
|
||||
(i.required ? await self.members.fetch(rawId, message.guild_id) : undefined);
|
||||
if (member) resolved.members[raw.id] = member;
|
||||
}
|
||||
} else {
|
||||
const { member, ...user } = raw;
|
||||
resolved.users[user.id] = user;
|
||||
if (member) resolved.members[user.id] = member;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ApplicationCommandOptionType.String:
|
||||
{
|
||||
value = args[i.name];
|
||||
const option = i as SeyfertStringOption;
|
||||
if (!value) break;
|
||||
if (option.min_length) {
|
||||
if (value.length < option.min_length) {
|
||||
value = undefined;
|
||||
errors.push({
|
||||
name: i.name,
|
||||
error: `The entered string has less than ${option.min_length} characters. The minimum required is ${option.min_length} characters.`,
|
||||
fullError: ['STRING_MIN_LENGTH', option.min_length],
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (option.max_length) {
|
||||
if (value.length > option.max_length) {
|
||||
value = undefined;
|
||||
errors.push({
|
||||
name: i.name,
|
||||
error: `The entered string has more than ${option.max_length} characters. The maximum required is ${option.max_length} characters.`,
|
||||
fullError: ['STRING_MAX_LENGTH', option.max_length],
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (option.choices?.length) {
|
||||
const choice = option.choices.find(x => x.name === value);
|
||||
if (!choice) {
|
||||
value = undefined;
|
||||
errors.push({
|
||||
name: i.name,
|
||||
error: `The entered choice is invalid. Please choose one of the following options: ${option.choices
|
||||
.map(x => x.name)
|
||||
.join(', ')}.`,
|
||||
fullError: ['STRING_INVALID_CHOICE', option.choices],
|
||||
});
|
||||
break;
|
||||
}
|
||||
value = choice.value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ApplicationCommandOptionType.Number:
|
||||
case ApplicationCommandOptionType.Integer:
|
||||
{
|
||||
const option = i as SeyfertNumberOption | SeyfertIntegerOption;
|
||||
if (!option.choices?.length) {
|
||||
value = Number(args[i.name]);
|
||||
if (args[i.name] === undefined) {
|
||||
value = undefined;
|
||||
break;
|
||||
}
|
||||
if (Number.isNaN(value)) {
|
||||
value = undefined;
|
||||
errors.push({
|
||||
name: i.name,
|
||||
error: 'The entered choice is an invalid number.',
|
||||
fullError: ['NUMBER_NAN', args[i.name]],
|
||||
});
|
||||
break;
|
||||
}
|
||||
if (option.min_value) {
|
||||
if (value < option.min_value) {
|
||||
value = undefined;
|
||||
errors.push({
|
||||
name: i.name,
|
||||
error: `The entered number is less than ${option.min_value}. The minimum allowed is ${option.min_value}`,
|
||||
fullError: ['NUMBER_MIN_VALUE', option.min_value],
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (option.max_value) {
|
||||
if (value > option.max_value) {
|
||||
value = undefined;
|
||||
errors.push({
|
||||
name: i.name,
|
||||
error: `The entered number is greater than ${option.max_value}. The maximum allowed is ${option.max_value}`,
|
||||
fullError: ['NUMBER_MAX_VALUE', option.max_value],
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
const choice = option.choices.find(x => x.name === args[i.name]);
|
||||
if (!choice) {
|
||||
value = undefined;
|
||||
errors.push({
|
||||
name: i.name,
|
||||
error: `The entered choice is invalid. Please choose one of the following options: ${option.choices
|
||||
.map(x => x.name)
|
||||
.join(', ')}.`,
|
||||
fullError: ['NUMBER_INVALID_CHOICE', option.choices],
|
||||
});
|
||||
break;
|
||||
}
|
||||
value = choice.value;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (value !== undefined) {
|
||||
options.push({
|
||||
name: i.name,
|
||||
type: i.type,
|
||||
value,
|
||||
} as APIApplicationCommandInteractionDataOption);
|
||||
} else if (i.required)
|
||||
if (!errors.some(x => x.name === i.name))
|
||||
errors.push({
|
||||
error: 'Option is required but returned undefined',
|
||||
name: i.name,
|
||||
fullError: ['OPTION_REQUIRED'],
|
||||
});
|
||||
} catch (e) {
|
||||
errors.push({
|
||||
error: e && typeof e === 'object' && 'message' in e ? (e.message as string) : `${e}`,
|
||||
name: i.name,
|
||||
fullError: ['UNKNOWN', e],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { errors, options };
|
||||
}
|
||||
|
||||
export function defaultArgsParser(content: string) {
|
||||
const args: Record<string, string> = {};
|
||||
for (const i of content.match(/-(.*?)(?=\s-|$)/gs) ?? []) {
|
||||
args[i.slice(1).split(' ')[0]] = i.split(' ').slice(1).join(' ');
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
//-(.*?)(?=\s-|$)/gs
|
||||
//-(?<text>[^-]*)/gm
|
193
src/client/transformers.ts
Normal file
193
src/client/transformers.ts
Normal file
@ -0,0 +1,193 @@
|
||||
import type { ChannelType } from 'discord-api-types/v10';
|
||||
import { type CustomStructures, OptionResolver } from '../commands';
|
||||
import {
|
||||
AnonymousGuild,
|
||||
AutoModerationRule,
|
||||
BaseChannel,
|
||||
BaseGuildChannel,
|
||||
CategoryChannel,
|
||||
ClientUser,
|
||||
DMChannel,
|
||||
DirectoryChannel,
|
||||
ForumChannel,
|
||||
Guild,
|
||||
GuildEmoji,
|
||||
GuildMember,
|
||||
GuildRole,
|
||||
GuildTemplate,
|
||||
InteractionGuildMember,
|
||||
MediaChannel,
|
||||
Message,
|
||||
NewsChannel,
|
||||
Poll,
|
||||
StageChannel,
|
||||
Sticker,
|
||||
TextGuildChannel,
|
||||
ThreadChannel,
|
||||
User,
|
||||
VoiceChannel,
|
||||
VoiceState,
|
||||
Webhook,
|
||||
WebhookMessage,
|
||||
} from '../structures';
|
||||
import type { StructStates } from '../common/types/util';
|
||||
import { GuildBan } from '../structures/GuildBan';
|
||||
|
||||
export type PollStructure = InferCustomStructure<Poll, 'Poll'>;
|
||||
export type ClientUserStructure = InferCustomStructure<ClientUser, 'ClientUser'>;
|
||||
export type AnonymousGuildStructure = InferCustomStructure<AnonymousGuild, 'AnonymousGuild'>;
|
||||
export type AutoModerationRuleStructure = InferCustomStructure<AutoModerationRule, 'AutoModerationRule'>;
|
||||
export type BaseChannelStructure = InferCustomStructure<BaseChannel<ChannelType>, 'BaseChannel'>;
|
||||
export type BaseGuildChannelStructure = InferCustomStructure<BaseGuildChannel, 'BaseGuildChannel'>;
|
||||
export type TextGuildChannelStructure = InferCustomStructure<TextGuildChannel, 'TextGuildChannel'>;
|
||||
export type DMChannelStructure = InferCustomStructure<DMChannel, 'DMChannel'>;
|
||||
export type VoiceChannelStructure = InferCustomStructure<VoiceChannel, 'VoiceChannel'>;
|
||||
export type StageChannelStructure = InferCustomStructure<StageChannel, 'StageChannel'>;
|
||||
export type MediaChannelStructure = InferCustomStructure<MediaChannel, 'MediaChannel'>;
|
||||
export type ForumChannelStructure = InferCustomStructure<ForumChannel, 'ForumChannel'>;
|
||||
export type ThreadChannelStructure = InferCustomStructure<ThreadChannel, 'ThreadChannel'>;
|
||||
export type CategoryChannelStructure = InferCustomStructure<CategoryChannel, 'CategoryChannel'>;
|
||||
export type NewsChannelStructure = InferCustomStructure<NewsChannel, 'NewsChannel'>;
|
||||
export type DirectoryChannelStructure = InferCustomStructure<DirectoryChannel, 'DirectoryChannel'>;
|
||||
export type GuildStructure<State extends StructStates = 'api'> = InferCustomStructure<Guild<State>, 'Guild'>;
|
||||
export type GuildBanStructure = InferCustomStructure<GuildBan, 'GuildBan'>;
|
||||
export type GuildEmojiStructure = InferCustomStructure<GuildEmoji, 'GuildEmoji'>;
|
||||
export type GuildMemberStructure = InferCustomStructure<GuildMember, 'GuildMember'>;
|
||||
export type InteractionGuildMemberStructure = InferCustomStructure<InteractionGuildMember, 'InteractionGuildMember'>;
|
||||
export type GuildRoleStructure = InferCustomStructure<GuildRole, 'GuildRole'>;
|
||||
export type GuildTemplateStructure = InferCustomStructure<GuildTemplate, 'GuildTemplate'>;
|
||||
export type MessageStructure = InferCustomStructure<Message, 'Message'>;
|
||||
export type WebhookMessageStructure = InferCustomStructure<WebhookMessage, 'WebhookMessage'>;
|
||||
export type StickerStructure = InferCustomStructure<Sticker, 'Sticker'>;
|
||||
export type UserStructure = InferCustomStructure<User, 'User'>;
|
||||
export type VoiceStateStructure = InferCustomStructure<VoiceState, 'VoiceState'>;
|
||||
export type WebhookStructure = InferCustomStructure<Webhook, 'Webhook'>;
|
||||
export type OptionResolverStructure = InferCustomStructure<OptionResolver, 'OptionResolver'>;
|
||||
|
||||
export class Transformers {
|
||||
static AnonymousGuild(...args: ConstructorParameters<typeof AnonymousGuild>): AnonymousGuildStructure {
|
||||
return new AnonymousGuild(...args);
|
||||
}
|
||||
|
||||
static AutoModerationRule(...args: ConstructorParameters<typeof AutoModerationRule>): AutoModerationRuleStructure {
|
||||
return new AutoModerationRule(...args);
|
||||
}
|
||||
|
||||
static BaseChannel(...args: ConstructorParameters<typeof BaseChannel>): BaseChannelStructure {
|
||||
return new BaseChannel(...args);
|
||||
}
|
||||
|
||||
static BaseGuildChannel(...args: ConstructorParameters<typeof BaseGuildChannel>): BaseGuildChannelStructure {
|
||||
return new BaseGuildChannel(...args);
|
||||
}
|
||||
|
||||
static TextGuildChannel(...args: ConstructorParameters<typeof TextGuildChannel>): TextGuildChannelStructure {
|
||||
return new TextGuildChannel(...args);
|
||||
}
|
||||
|
||||
static DMChannel(...args: ConstructorParameters<typeof DMChannel>): DMChannelStructure {
|
||||
return new DMChannel(...args);
|
||||
}
|
||||
|
||||
static VoiceChannel(...args: ConstructorParameters<typeof VoiceChannel>): VoiceChannelStructure {
|
||||
return new VoiceChannel(...args);
|
||||
}
|
||||
|
||||
static StageChannel(...args: ConstructorParameters<typeof StageChannel>): StageChannelStructure {
|
||||
return new StageChannel(...args);
|
||||
}
|
||||
|
||||
static MediaChannel(...args: ConstructorParameters<typeof MediaChannel>): MediaChannelStructure {
|
||||
return new MediaChannel(...args);
|
||||
}
|
||||
|
||||
static ForumChannel(...args: ConstructorParameters<typeof ForumChannel>): ForumChannelStructure {
|
||||
return new ForumChannel(...args);
|
||||
}
|
||||
|
||||
static ThreadChannel(...args: ConstructorParameters<typeof ThreadChannel>): ThreadChannelStructure {
|
||||
return new ThreadChannel(...args);
|
||||
}
|
||||
|
||||
static CategoryChannel(...args: ConstructorParameters<typeof CategoryChannel>): CategoryChannelStructure {
|
||||
return new CategoryChannel(...args);
|
||||
}
|
||||
|
||||
static NewsChannel(...args: ConstructorParameters<typeof NewsChannel>): NewsChannelStructure {
|
||||
return new NewsChannel(...args);
|
||||
}
|
||||
|
||||
static DirectoryChannel(...args: ConstructorParameters<typeof DirectoryChannel>): DirectoryChannelStructure {
|
||||
return new DirectoryChannel(...args);
|
||||
}
|
||||
|
||||
static ClientUser(...args: ConstructorParameters<typeof ClientUser>): ClientUserStructure {
|
||||
return new ClientUser(...args);
|
||||
}
|
||||
|
||||
static Guild<State extends StructStates = 'api'>(
|
||||
...args: ConstructorParameters<typeof Guild>
|
||||
): GuildStructure<State> {
|
||||
return new Guild<State>(...args);
|
||||
}
|
||||
|
||||
static GuildBan(...args: ConstructorParameters<typeof GuildBan>): GuildBanStructure {
|
||||
return new GuildBan(...args);
|
||||
}
|
||||
|
||||
static GuildEmoji(...args: ConstructorParameters<typeof GuildEmoji>): GuildEmojiStructure {
|
||||
return new GuildEmoji(...args);
|
||||
}
|
||||
|
||||
static GuildMember(...args: ConstructorParameters<typeof GuildMember>): GuildMemberStructure {
|
||||
return new GuildMember(...args);
|
||||
}
|
||||
|
||||
static InteractionGuildMember(
|
||||
...args: ConstructorParameters<typeof InteractionGuildMember>
|
||||
): InteractionGuildMemberStructure {
|
||||
return new InteractionGuildMember(...args);
|
||||
}
|
||||
|
||||
static GuildRole(...args: ConstructorParameters<typeof GuildRole>): GuildRoleStructure {
|
||||
return new GuildRole(...args);
|
||||
}
|
||||
|
||||
static GuildTemplate(...args: ConstructorParameters<typeof GuildTemplate>): GuildTemplateStructure {
|
||||
return new GuildTemplate(...args);
|
||||
}
|
||||
|
||||
static Message(...args: ConstructorParameters<typeof Message>): MessageStructure {
|
||||
return new Message(...args);
|
||||
}
|
||||
|
||||
static WebhookMessage(...args: ConstructorParameters<typeof WebhookMessage>): WebhookMessageStructure {
|
||||
return new WebhookMessage(...args);
|
||||
}
|
||||
|
||||
static Poll(...args: ConstructorParameters<typeof Poll>): PollStructure {
|
||||
return new Poll(...args);
|
||||
}
|
||||
|
||||
static Sticker(...args: ConstructorParameters<typeof Sticker>): StickerStructure {
|
||||
return new Sticker(...args);
|
||||
}
|
||||
|
||||
static User(...args: ConstructorParameters<typeof User>): UserStructure {
|
||||
return new User(...args);
|
||||
}
|
||||
|
||||
static VoiceState(...args: ConstructorParameters<typeof VoiceState>): VoiceStateStructure {
|
||||
return new VoiceState(...args);
|
||||
}
|
||||
|
||||
static Webhook(...args: ConstructorParameters<typeof Webhook>): WebhookStructure {
|
||||
return new Webhook(...args);
|
||||
}
|
||||
|
||||
static OptionResolver(...args: ConstructorParameters<typeof OptionResolver>): OptionResolverStructure {
|
||||
return new OptionResolver(...args);
|
||||
}
|
||||
}
|
||||
|
||||
export type InferCustomStructure<T, N extends string> = CustomStructures extends Record<N, infer P> ? P : T;
|
@ -3,9 +3,8 @@ import { randomUUID } from 'node:crypto';
|
||||
import { ApiHandler, Logger } from '..';
|
||||
import type { Cache } from '../cache';
|
||||
import { WorkerAdapter } from '../cache';
|
||||
import { LogLevels, MergeOptions, lazyLoadPackage, type DeepPartial, type When } from '../common';
|
||||
import { LogLevels, lazyLoadPackage, type DeepPartial, type When } from '../common';
|
||||
import { EventHandler } from '../events';
|
||||
import { ClientUser } from '../structures';
|
||||
import { Shard, properties, type ShardManagerOptions, type WorkerData } from '../websocket';
|
||||
import type {
|
||||
WorkerReady,
|
||||
@ -23,9 +22,9 @@ import type { ManagerMessages } from '../websocket/discord/workermanager';
|
||||
import type { BaseClientOptions, ServicesOptions, StartOptions } from './base';
|
||||
import { BaseClient } from './base';
|
||||
import type { Client, ClientOptions } from './client';
|
||||
import { onInteractionCreate } from './oninteractioncreate';
|
||||
import { defaultArgsParser, defaultOptionsParser, onMessageCreate } from './onmessagecreate';
|
||||
|
||||
import { Collectors } from './collectors';
|
||||
import { type ClientUserStructure, Transformers } from './transformers';
|
||||
|
||||
let workerData: WorkerData;
|
||||
let manager: import('node:worker_threads').MessagePort;
|
||||
@ -49,7 +48,7 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
|
||||
|
||||
collectors = new Collectors();
|
||||
events? = new EventHandler(this);
|
||||
me!: When<Ready, ClientUser>;
|
||||
me!: When<Ready, ClientUserStructure>;
|
||||
promises = new Map<string, { resolve: (value: any) => void; timeout: NodeJS.Timeout }>();
|
||||
|
||||
shards = new Map<number, Shard>();
|
||||
@ -58,15 +57,7 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
|
||||
|
||||
constructor(options?: WorkerClientOptions) {
|
||||
super(options);
|
||||
this.options = MergeOptions(
|
||||
{
|
||||
commands: {
|
||||
argsParser: defaultArgsParser,
|
||||
optionsParser: defaultOptionsParser,
|
||||
},
|
||||
} satisfies Partial<WorkerClientOptions>,
|
||||
this.options,
|
||||
);
|
||||
|
||||
if (!process.env.SEYFERT_SPAWNING) {
|
||||
throw new Error('WorkerClient cannot spawn without manager');
|
||||
}
|
||||
@ -129,9 +120,6 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
|
||||
this.events = undefined;
|
||||
} else if (typeof rest.handlers.events === 'function') {
|
||||
this.events = new EventHandler(this);
|
||||
this.events.setHandlers({
|
||||
callback: rest.handlers.events,
|
||||
});
|
||||
} else {
|
||||
this.events = rest.handlers.events;
|
||||
}
|
||||
@ -336,7 +324,10 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
|
||||
}
|
||||
|
||||
protected async onPacket(packet: GatewayDispatchPayload, shardId: number) {
|
||||
await this.events?.execute('RAW', packet, this as WorkerClient<true>, shardId);
|
||||
Promise.allSettled([
|
||||
this.events?.runEvent('RAW', this, packet, shardId, false),
|
||||
this.collectors.run('RAW', packet),
|
||||
]); //ignore promise
|
||||
switch (packet.t) {
|
||||
case 'GUILD_CREATE': {
|
||||
if (this.__handleGuilds?.has(packet.d.id)) {
|
||||
@ -355,19 +346,19 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
await this.events?.execute(packet.t, packet, this, shardId);
|
||||
await this.events?.execute(packet.t as never, packet, this, shardId);
|
||||
switch (packet.t) {
|
||||
case 'READY':
|
||||
if (!this.__handleGuilds) this.__handleGuilds = new Set();
|
||||
for (const g of packet.d.guilds) {
|
||||
this.__handleGuilds?.add(g.id);
|
||||
this.__handleGuilds.add(g.id);
|
||||
}
|
||||
this.botId = packet.d.user.id;
|
||||
this.applicationId = packet.d.application.id;
|
||||
this.me = new ClientUser(this, packet.d.user, packet.d.application) as never;
|
||||
this.me = Transformers.ClientUser(this, packet.d.user, packet.d.application) as never;
|
||||
if (
|
||||
!(
|
||||
this.__handleGuilds?.size &&
|
||||
(workerData.intents & GatewayIntentBits.Guilds) === GatewayIntentBits.Guilds
|
||||
this.__handleGuilds.size && (workerData.intents & GatewayIntentBits.Guilds) === GatewayIntentBits.Guilds
|
||||
)
|
||||
) {
|
||||
if ([...this.shards.values()].every(shard => shard.data.session_id)) {
|
||||
@ -382,10 +373,10 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
|
||||
this.debugger?.debug(`#${shardId} [${packet.d.user.username}](${this.botId}) is online...`);
|
||||
break;
|
||||
case 'INTERACTION_CREATE':
|
||||
await onInteractionCreate(this, packet.d, shardId);
|
||||
await this.handleCommand.interaction(packet.d, shardId);
|
||||
break;
|
||||
case 'MESSAGE_CREATE':
|
||||
await onMessageCreate(this, packet.d, shardId);
|
||||
await this.handleCommand.message(packet.d, shardId);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -17,10 +17,9 @@ import type {
|
||||
SeyfertStringOption,
|
||||
} from '../..';
|
||||
import type { Attachment } from '../../builders';
|
||||
import { magicImport, type FlatObjectKeys } from '../../common';
|
||||
import type { AllChannels, AutocompleteInteraction, GuildRole, InteractionGuildMember, User } from '../../structures';
|
||||
import { type Awaitable, magicImport, type FlatObjectKeys } from '../../common';
|
||||
import type { AllChannels, AutocompleteInteraction } from '../../structures';
|
||||
import type { Groups, RegisteredMiddlewares } from '../decorators';
|
||||
import type { OptionResolver } from '../optionresolver';
|
||||
import type { CommandContext } from './chatcontext';
|
||||
import type {
|
||||
DefaultLocale,
|
||||
@ -33,6 +32,12 @@ import type {
|
||||
UsingClient,
|
||||
} from './shared';
|
||||
import { inspect } from 'node:util';
|
||||
import type {
|
||||
GuildRoleStructure,
|
||||
InteractionGuildMemberStructure,
|
||||
OptionResolverStructure,
|
||||
UserStructure,
|
||||
} from '../../client/transformers';
|
||||
|
||||
export interface ReturnOptionsTypes {
|
||||
1: never; // subcommand
|
||||
@ -40,10 +45,10 @@ export interface ReturnOptionsTypes {
|
||||
3: string;
|
||||
4: number; // integer
|
||||
5: boolean;
|
||||
6: InteractionGuildMember | User;
|
||||
6: InteractionGuildMemberStructure | UserStructure;
|
||||
7: AllChannels;
|
||||
8: GuildRole;
|
||||
9: GuildRole | AllChannels | User;
|
||||
8: GuildRoleStructure;
|
||||
9: GuildRoleStructure | AllChannels | UserStructure;
|
||||
10: number; // number
|
||||
11: Attachment;
|
||||
}
|
||||
@ -58,7 +63,7 @@ type Wrap<N extends ApplicationCommandOptionType> = N extends
|
||||
data: { context: CommandContext; value: ReturnOptionsTypes[N] },
|
||||
ok: OKFunction<any>,
|
||||
fail: StopFunction,
|
||||
): void;
|
||||
): Awaitable<void>;
|
||||
} & {
|
||||
description: string;
|
||||
description_localizations?: APIApplicationCommandBasicOption['description_localizations'];
|
||||
@ -144,7 +149,7 @@ export class BaseCommand {
|
||||
/** @internal */
|
||||
async __runOptions(
|
||||
ctx: CommandContext<{}, never>,
|
||||
resolver: OptionResolver,
|
||||
resolver: OptionResolverStructure,
|
||||
): Promise<[boolean, OnOptionsReturnObject]> {
|
||||
if (!this?.options?.length) {
|
||||
return [false, {}];
|
||||
|
@ -1,19 +1,21 @@
|
||||
import { MessageFlags } from 'discord-api-types/v10';
|
||||
import type { AllChannels, Guild, InferWithPrefix, ReturnCache, WebhookMessage } from '../..';
|
||||
import type { AllChannels, InferWithPrefix, Message, ReturnCache } from '../..';
|
||||
import type { Client, WorkerClient } from '../../client';
|
||||
import type { If, UnionToTuple, When } from '../../common';
|
||||
import type { InteractionCreateBodyRequest, InteractionMessageUpdateBodyRequest } from '../../common/types/write';
|
||||
import {
|
||||
Message,
|
||||
type ChatInputCommandInteraction,
|
||||
type GuildMember,
|
||||
type InteractionGuildMember,
|
||||
} from '../../structures';
|
||||
import { ChatInputCommandInteraction } from '../../structures';
|
||||
import { BaseContext } from '../basecontext';
|
||||
import type { RegisteredMiddlewares } from '../decorators';
|
||||
import type { OptionResolver } from '../optionresolver';
|
||||
import type { Command, ContextOptions, OptionsRecord, SubCommand } from './chat';
|
||||
import type { CommandMetadata, ExtendContext, GlobalMetadata, UsingClient } from './shared';
|
||||
import type {
|
||||
GuildMemberStructure,
|
||||
GuildStructure,
|
||||
InteractionGuildMemberStructure,
|
||||
MessageStructure,
|
||||
OptionResolverStructure,
|
||||
WebhookMessageStructure,
|
||||
} from '../../client/transformers';
|
||||
|
||||
export interface CommandContext<T extends OptionsRecord = {}, M extends keyof RegisteredMiddlewares = never>
|
||||
extends BaseContext,
|
||||
@ -23,22 +25,22 @@ export class CommandContext<
|
||||
T extends OptionsRecord = {},
|
||||
M extends keyof RegisteredMiddlewares = never,
|
||||
> extends BaseContext {
|
||||
message!: If<InferWithPrefix, Message | undefined, undefined>;
|
||||
message!: If<InferWithPrefix, MessageStructure | undefined, undefined>;
|
||||
interaction!: If<InferWithPrefix, ChatInputCommandInteraction | undefined, ChatInputCommandInteraction>;
|
||||
|
||||
messageResponse?: If<InferWithPrefix, Message | undefined>;
|
||||
messageResponse?: If<InferWithPrefix, MessageStructure | undefined>;
|
||||
constructor(
|
||||
readonly client: UsingClient,
|
||||
data: ChatInputCommandInteraction | Message,
|
||||
readonly resolver: OptionResolver,
|
||||
data: ChatInputCommandInteraction | MessageStructure,
|
||||
readonly resolver: OptionResolverStructure,
|
||||
readonly shardId: number,
|
||||
readonly command: Command | SubCommand,
|
||||
) {
|
||||
super(client);
|
||||
if (data instanceof Message) {
|
||||
this.message = data as never;
|
||||
} else {
|
||||
if (data instanceof ChatInputCommandInteraction) {
|
||||
this.interaction = data;
|
||||
} else {
|
||||
this.message = data as never;
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,7 +63,7 @@ export class CommandContext<
|
||||
async write<FR extends boolean = false>(
|
||||
body: InteractionCreateBodyRequest,
|
||||
fetchReply?: FR,
|
||||
): Promise<When<FR, WebhookMessage | Message, void | WebhookMessage | Message>> {
|
||||
): Promise<When<FR, WebhookMessageStructure | MessageStructure, void | WebhookMessageStructure | MessageStructure>> {
|
||||
if (this.interaction) return this.interaction.write(body, fetchReply);
|
||||
const options = (this.client as Client | WorkerClient).options?.commands;
|
||||
return (this.messageResponse = await (this.message! as Message)[
|
||||
@ -90,7 +92,7 @@ export class CommandContext<
|
||||
editOrReply<FR extends boolean = false>(
|
||||
body: InteractionCreateBodyRequest | InteractionMessageUpdateBodyRequest,
|
||||
fetchReply?: FR,
|
||||
): Promise<When<FR, WebhookMessage | Message, void | WebhookMessage | Message>> {
|
||||
): Promise<When<FR, WebhookMessageStructure | MessageStructure, void | WebhookMessageStructure | MessageStructure>> {
|
||||
if (this.interaction) return this.interaction.editOrReply(body as InteractionCreateBodyRequest, fetchReply);
|
||||
if (this.messageResponse) {
|
||||
return this.editResponse(body);
|
||||
@ -99,7 +101,7 @@ export class CommandContext<
|
||||
}
|
||||
|
||||
async fetchResponse(): Promise<
|
||||
If<InferWithPrefix, WebhookMessage | Message | undefined, WebhookMessage | undefined>
|
||||
If<InferWithPrefix, WebhookMessageStructure | MessageStructure | undefined, WebhookMessageStructure | undefined>
|
||||
> {
|
||||
if (this.interaction) return this.interaction.fetchResponse();
|
||||
this.messageResponse = await this.messageResponse?.fetch();
|
||||
@ -122,8 +124,8 @@ export class CommandContext<
|
||||
}
|
||||
}
|
||||
|
||||
me(mode?: 'rest' | 'flow'): Promise<GuildMember>;
|
||||
me(mode?: 'cache'): ReturnCache<GuildMember | undefined>;
|
||||
me(mode?: 'rest' | 'flow'): Promise<GuildMemberStructure>;
|
||||
me(mode?: 'cache'): ReturnCache<GuildMemberStructure | undefined>;
|
||||
me(mode: 'cache' | 'rest' | 'flow' = 'cache') {
|
||||
if (!this.guildId)
|
||||
return mode === 'cache' ? (this.client.cache.adapter.isAsync ? Promise.resolve() : undefined) : Promise.resolve();
|
||||
@ -138,8 +140,8 @@ export class CommandContext<
|
||||
}
|
||||
}
|
||||
|
||||
guild(mode?: 'rest' | 'flow'): Promise<Guild<'cached' | 'api'> | undefined>;
|
||||
guild(mode?: 'cache'): ReturnCache<Guild<'cached'> | undefined>;
|
||||
guild(mode?: 'rest' | 'flow'): Promise<GuildStructure<'cached' | 'api'> | undefined>;
|
||||
guild(mode?: 'cache'): ReturnCache<GuildStructure<'cached'> | undefined>;
|
||||
guild(mode: 'cache' | 'rest' | 'flow' = 'cache') {
|
||||
if (!this.guildId)
|
||||
return (mode === 'cache'
|
||||
@ -159,23 +161,23 @@ export class CommandContext<
|
||||
}
|
||||
|
||||
get guildId() {
|
||||
return this.interaction?.guildId || (this.message! as Message | undefined)?.guildId;
|
||||
return this.interaction?.guildId || (this.message! as MessageStructure | undefined)?.guildId;
|
||||
}
|
||||
|
||||
get channelId() {
|
||||
return this.interaction?.channelId || (this.message! as Message).channelId;
|
||||
return this.interaction?.channelId || (this.message! as MessageStructure).channelId;
|
||||
}
|
||||
|
||||
get author() {
|
||||
return this.interaction?.user || (this.message! as Message).author;
|
||||
return this.interaction?.user || (this.message! as MessageStructure).author;
|
||||
}
|
||||
|
||||
get member(): If<
|
||||
InferWithPrefix,
|
||||
GuildMember | InteractionGuildMember | undefined,
|
||||
InteractionGuildMember | undefined
|
||||
GuildMemberStructure | InteractionGuildMemberStructure | undefined,
|
||||
InteractionGuildMemberStructure | undefined
|
||||
> {
|
||||
return this.interaction?.member || ((this.message! as Message)?.member as any);
|
||||
return this.interaction?.member || ((this.message! as MessageStructure)?.member as any);
|
||||
}
|
||||
|
||||
isChat(): this is CommandContext {
|
||||
|
@ -64,10 +64,10 @@ export abstract class ContextMenuCommand {
|
||||
onBotPermissionsFail(context: MenuCommandContext<any, never>, permissions: PermissionStrings): any {
|
||||
context.client.logger.fatal(`${this.name}.<onBotPermissionsFail>`, context.author.id, permissions);
|
||||
}
|
||||
onPermissionsFail(context: MenuCommandContext<any, never>, permissions: PermissionStrings): any {
|
||||
context.client.logger.fatal(`${this.name}.<onPermissionsFail>`, context.author.id, permissions);
|
||||
}
|
||||
onInternalError(client: UsingClient, error?: unknown): any {
|
||||
client.logger.fatal(error);
|
||||
// onPermissionsFail(context: MenuCommandContext<any, never>, permissions: PermissionStrings): any {
|
||||
// context.client.logger.fatal(`${this.name}.<onPermissionsFail>`, context.author.id, permissions);
|
||||
// }
|
||||
onInternalError(client: UsingClient, command: ContextMenuCommand, error?: unknown): any {
|
||||
client.logger.fatal(command.name, error);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { type APIMessage, ApplicationCommandType, MessageFlags } from 'discord-api-types/v10';
|
||||
import type { ContextMenuCommand, ReturnCache, WebhookMessage } from '../..';
|
||||
import type { ContextMenuCommand, ReturnCache } from '../..';
|
||||
import {
|
||||
toSnakeCase,
|
||||
type InteractionCreateBodyRequest,
|
||||
@ -8,20 +8,20 @@ import {
|
||||
type UnionToTuple,
|
||||
type When,
|
||||
} from '../../common';
|
||||
import {
|
||||
Message,
|
||||
User,
|
||||
type AllChannels,
|
||||
type Guild,
|
||||
type GuildMember,
|
||||
type MessageCommandInteraction,
|
||||
type UserCommandInteraction,
|
||||
} from '../../structures';
|
||||
import type { AllChannels, MessageCommandInteraction, UserCommandInteraction } from '../../structures';
|
||||
import { BaseContext } from '../basecontext';
|
||||
import type { RegisteredMiddlewares } from '../decorators';
|
||||
import type { CommandMetadata, ExtendContext, GlobalMetadata, UsingClient } from './shared';
|
||||
import {
|
||||
type GuildMemberStructure,
|
||||
type GuildStructure,
|
||||
type MessageStructure,
|
||||
Transformers,
|
||||
type UserStructure,
|
||||
type WebhookMessageStructure,
|
||||
} from '../../client/transformers';
|
||||
|
||||
export type InteractionTarget<T> = T extends MessageCommandInteraction ? Message : User;
|
||||
export type InteractionTarget<T> = T extends MessageCommandInteraction ? MessageStructure : UserStructure;
|
||||
|
||||
export interface MenuCommandContext<
|
||||
T extends MessageCommandInteraction | UserCommandInteraction,
|
||||
@ -50,11 +50,11 @@ export class MenuCommandContext<
|
||||
switch (this.interaction.data.type) {
|
||||
case ApplicationCommandType.Message: {
|
||||
const data = this.interaction.data.resolved.messages[this.interaction.data.targetId as Lowercase<string>];
|
||||
return new Message(this.client, toSnakeCase(data) as APIMessage) as never;
|
||||
return Transformers.Message(this.client, toSnakeCase(data) as APIMessage) as never;
|
||||
}
|
||||
case ApplicationCommandType.User: {
|
||||
const data = this.interaction.data.resolved.users[this.interaction.data.targetId as Lowercase<string>];
|
||||
return new User(this.client, toSnakeCase(data)) as never;
|
||||
return Transformers.User(this.client, toSnakeCase(data)) as never;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -70,7 +70,7 @@ export class MenuCommandContext<
|
||||
write<FR extends boolean = false>(
|
||||
body: InteractionCreateBodyRequest,
|
||||
fetchReply?: FR,
|
||||
): Promise<When<FR, WebhookMessage, void | WebhookMessage>> {
|
||||
): Promise<When<FR, WebhookMessageStructure, void | WebhookMessageStructure>> {
|
||||
return this.interaction.write(body, fetchReply);
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ export class MenuCommandContext<
|
||||
editOrReply<FR extends boolean = false>(
|
||||
body: InteractionCreateBodyRequest | InteractionMessageUpdateBodyRequest,
|
||||
fetchReply?: FR,
|
||||
): Promise<When<FR, WebhookMessage | Message, void | WebhookMessage | Message>> {
|
||||
): Promise<When<FR, WebhookMessageStructure | MessageStructure, void | WebhookMessageStructure | MessageStructure>> {
|
||||
return this.interaction.editOrReply(body as InteractionCreateBodyRequest, fetchReply);
|
||||
}
|
||||
|
||||
@ -109,8 +109,8 @@ export class MenuCommandContext<
|
||||
return this.client.channels.fetch(this.channelId, mode === 'rest');
|
||||
}
|
||||
|
||||
me(mode?: 'rest' | 'flow'): Promise<GuildMember>;
|
||||
me(mode?: 'cache'): ReturnCache<GuildMember | undefined>;
|
||||
me(mode?: 'rest' | 'flow'): Promise<GuildMemberStructure>;
|
||||
me(mode?: 'cache'): ReturnCache<GuildMemberStructure | undefined>;
|
||||
me(mode: 'cache' | 'rest' | 'flow' = 'cache') {
|
||||
if (!this.guildId)
|
||||
return mode === 'cache' ? (this.client.cache.adapter.isAsync ? Promise.resolve() : undefined) : Promise.resolve();
|
||||
@ -122,8 +122,8 @@ export class MenuCommandContext<
|
||||
}
|
||||
}
|
||||
|
||||
guild(mode?: 'rest' | 'flow'): Promise<Guild<'cached' | 'api'> | undefined>;
|
||||
guild(mode?: 'cache'): ReturnCache<Guild<'cached'> | undefined>;
|
||||
guild(mode?: 'rest' | 'flow'): Promise<GuildStructure<'cached' | 'api'> | undefined>;
|
||||
guild(mode?: 'cache'): ReturnCache<GuildStructure<'cached'> | undefined>;
|
||||
guild(mode: 'cache' | 'rest' | 'flow' = 'cache') {
|
||||
if (!this.guildId)
|
||||
return (
|
||||
@ -158,10 +158,10 @@ export class MenuCommandContext<
|
||||
}
|
||||
|
||||
isMenuUser(): this is MenuCommandContext<UserCommandInteraction> {
|
||||
return this.target instanceof User;
|
||||
return this.interaction.data.type === ApplicationCommandType.User;
|
||||
}
|
||||
|
||||
isMenuMessage(): this is MenuCommandContext<MessageCommandInteraction> {
|
||||
return this.target instanceof Message;
|
||||
return this.interaction.data.type === ApplicationCommandType.Message;
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ export interface ExtraProps {}
|
||||
export interface UsingClient extends BaseClient {}
|
||||
export type ParseClient<T extends BaseClient> = T;
|
||||
export interface InternalOptions {}
|
||||
export interface CustomStructures {}
|
||||
|
||||
export type MiddlewareContext<T = any, C = any> = (context: {
|
||||
context: C;
|
||||
|
806
src/commands/handle.ts
Normal file
806
src/commands/handle.ts
Normal file
@ -0,0 +1,806 @@
|
||||
import {
|
||||
type APIApplicationCommandInteraction,
|
||||
ApplicationCommandType,
|
||||
InteractionType,
|
||||
type APIInteraction,
|
||||
type GatewayMessageCreateDispatchData,
|
||||
InteractionContextType,
|
||||
type APIApplicationCommandInteractionDataOption,
|
||||
ApplicationCommandOptionType,
|
||||
ChannelType,
|
||||
type APIInteractionDataResolvedChannel,
|
||||
} from 'discord-api-types/v10';
|
||||
import {
|
||||
Command,
|
||||
type ContextOptionsResolved,
|
||||
type UsingClient,
|
||||
type CommandAutocompleteOption,
|
||||
type ContextMenuCommand,
|
||||
MenuCommandContext,
|
||||
BaseCommand,
|
||||
CommandContext,
|
||||
type RegisteredMiddlewares,
|
||||
SubCommand,
|
||||
IgnoreCommand,
|
||||
type CommandOption,
|
||||
type MessageCommandOptionErrors,
|
||||
type SeyfertChannelOption,
|
||||
type SeyfertIntegerOption,
|
||||
type SeyfertNumberOption,
|
||||
type SeyfertStringOption,
|
||||
} from '.';
|
||||
import {
|
||||
AutocompleteInteraction,
|
||||
BaseInteraction,
|
||||
type ComponentInteraction,
|
||||
type ModalSubmitInteraction,
|
||||
type ChatInputCommandInteraction,
|
||||
type MessageCommandInteraction,
|
||||
type UserCommandInteraction,
|
||||
type __InternalReplyFunction,
|
||||
} from '../structures';
|
||||
import type { PermissionsBitField } from '../structures/extra/Permissions';
|
||||
import { ComponentContext, ModalContext } from '../components';
|
||||
import type { Client, WorkerClient } from '../client';
|
||||
import type { MakeRequired } from '../common';
|
||||
import { type MessageStructure, Transformers, type OptionResolverStructure } from '../client/transformers';
|
||||
|
||||
export type CommandOptionWithType = CommandOption & {
|
||||
type: ApplicationCommandOptionType;
|
||||
};
|
||||
|
||||
export interface CommandFromContent {
|
||||
command?: Command | SubCommand;
|
||||
parent?: Command;
|
||||
fullCommandName: string;
|
||||
}
|
||||
|
||||
export class HandleCommand {
|
||||
constructor(public client: UsingClient) {}
|
||||
|
||||
async autocomplete(
|
||||
interaction: AutocompleteInteraction,
|
||||
optionsResolver: OptionResolverStructure,
|
||||
command?: CommandAutocompleteOption,
|
||||
) {
|
||||
// idc, is a YOU problem
|
||||
if (!command?.autocomplete) {
|
||||
return this.client.logger.warn(
|
||||
`${optionsResolver.fullCommandName} ${command?.name} command does not have 'autocomplete' callback`,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
try {
|
||||
await command.autocomplete(interaction);
|
||||
} catch (error) {
|
||||
if (!command.onAutocompleteError)
|
||||
return this.client.logger.error(
|
||||
`${optionsResolver.fullCommandName} ${command.name} just threw an error, ${
|
||||
error ? (typeof error === 'object' && 'message' in error ? error.message : error) : 'Unknown'
|
||||
}`,
|
||||
);
|
||||
await command.onAutocompleteError(interaction, error);
|
||||
}
|
||||
} catch (error) {
|
||||
await optionsResolver.getCommand()?.onInternalError?.(this.client, optionsResolver.getCommand()!, error);
|
||||
}
|
||||
} catch (error) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
async contextMenuMessage(
|
||||
command: ContextMenuCommand,
|
||||
interaction: MessageCommandInteraction,
|
||||
context: MenuCommandContext<MessageCommandInteraction>,
|
||||
) {
|
||||
// @ts-expect-error
|
||||
return this.contextMenuUser(command, interaction, context);
|
||||
}
|
||||
|
||||
async contextMenuUser(
|
||||
command: ContextMenuCommand,
|
||||
interaction: UserCommandInteraction,
|
||||
context: MenuCommandContext<UserCommandInteraction>,
|
||||
) {
|
||||
if (command.botPermissions && interaction.appPermissions) {
|
||||
const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions);
|
||||
if (permissions) return command.onBotPermissionsFail(context, permissions);
|
||||
}
|
||||
|
||||
const resultGlobal = await this.runGlobalMiddlewares(command, context);
|
||||
if (typeof resultGlobal === 'boolean') return;
|
||||
const resultMiddle = await this.runMiddlewares(command, context);
|
||||
if (typeof resultMiddle === 'boolean') return;
|
||||
|
||||
try {
|
||||
try {
|
||||
await command.run!(context);
|
||||
await command.onAfterRun?.(context, undefined);
|
||||
} catch (error) {
|
||||
await command.onRunError(context, error);
|
||||
await command.onAfterRun?.(context, error);
|
||||
}
|
||||
} catch (error) {
|
||||
try {
|
||||
await command.onInternalError(this.client, command, error);
|
||||
} catch {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async chatInput(
|
||||
command: Command | SubCommand,
|
||||
interaction: ChatInputCommandInteraction,
|
||||
resolver: OptionResolverStructure,
|
||||
context: CommandContext,
|
||||
) {
|
||||
if (command.botPermissions && interaction.appPermissions) {
|
||||
const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions);
|
||||
if (permissions) return command.onBotPermissionsFail?.(context, permissions);
|
||||
}
|
||||
if (!(await this.runOptions(command, context, resolver))) return;
|
||||
|
||||
const resultGlobal = await this.runGlobalMiddlewares(command, context);
|
||||
if (typeof resultGlobal === 'boolean') return;
|
||||
const resultMiddle = await this.runMiddlewares(command, context);
|
||||
if (typeof resultMiddle === 'boolean') return;
|
||||
|
||||
try {
|
||||
try {
|
||||
await command.run!(context);
|
||||
await command.onAfterRun?.(context, undefined);
|
||||
} catch (error) {
|
||||
await command.onRunError?.(context, error);
|
||||
await command.onAfterRun?.(context, error);
|
||||
}
|
||||
} catch (error) {
|
||||
try {
|
||||
await command.onInternalError?.(this.client, command, error);
|
||||
} catch {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async modal(interaction: ModalSubmitInteraction) {
|
||||
const context = new ModalContext(this.client, interaction);
|
||||
const extended = this.client.options?.context?.(interaction) ?? {};
|
||||
Object.assign(context, extended);
|
||||
await this.client.components?.executeModal(context);
|
||||
}
|
||||
|
||||
async messageComponent(interaction: ComponentInteraction) {
|
||||
//@ts-expect-error
|
||||
const context = new ComponentContext(this.client, interaction);
|
||||
const extended = this.client.options?.context?.(interaction) ?? {};
|
||||
Object.assign(context, extended);
|
||||
await this.client.components?.executeComponent(context);
|
||||
}
|
||||
|
||||
async interaction(body: APIInteraction, shardId: number, __reply?: __InternalReplyFunction) {
|
||||
this.client.debugger?.debug(`[${InteractionType[body.type] ?? body.type}] Interaction received.`);
|
||||
switch (body.type) {
|
||||
case InteractionType.ApplicationCommandAutocomplete:
|
||||
{
|
||||
const optionsResolver = this.makeResolver(
|
||||
this.client,
|
||||
body.data.options ?? [],
|
||||
this.getCommand<Command>(body.data),
|
||||
body.guild_id,
|
||||
body.data.resolved as ContextOptionsResolved,
|
||||
);
|
||||
const interaction = new AutocompleteInteraction(this.client, body, optionsResolver, __reply);
|
||||
const command = optionsResolver.getAutocomplete();
|
||||
await this.autocomplete(interaction, optionsResolver, command);
|
||||
}
|
||||
break;
|
||||
case InteractionType.ApplicationCommand: {
|
||||
switch (body.data.type) {
|
||||
case ApplicationCommandType.Message: {
|
||||
const data = this.makeMenuCommand(body, shardId, __reply);
|
||||
if (!data) return;
|
||||
// @ts-expect-error
|
||||
this.contextMenuMessage(data.command, data.interaction, data.context);
|
||||
break;
|
||||
}
|
||||
case ApplicationCommandType.User: {
|
||||
const data = this.makeMenuCommand(body, shardId, __reply);
|
||||
if (!data) return;
|
||||
// @ts-expect-error
|
||||
this.contextMenuUser(data.command, data.interaction, data.context);
|
||||
break;
|
||||
}
|
||||
case ApplicationCommandType.ChatInput: {
|
||||
const parentCommand = this.getCommand<Command>(body.data);
|
||||
const optionsResolver = this.makeResolver(
|
||||
this.client,
|
||||
body.data.options ?? [],
|
||||
parentCommand,
|
||||
body.guild_id,
|
||||
body.data.resolved as ContextOptionsResolved,
|
||||
);
|
||||
const interaction = BaseInteraction.from(this.client, body, __reply) as ChatInputCommandInteraction;
|
||||
const command = optionsResolver.getCommand();
|
||||
if (!command?.run)
|
||||
return this.client.logger.warn(`${optionsResolver.fullCommandName} command does not have 'run' callback`);
|
||||
const context = new CommandContext(this.client, interaction, optionsResolver, shardId, command);
|
||||
const extendContext = this.client.options?.context?.(interaction) ?? {};
|
||||
Object.assign(context, extendContext);
|
||||
await this.chatInput(command, interaction, optionsResolver, context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case InteractionType.ModalSubmit:
|
||||
{
|
||||
const interaction = BaseInteraction.from(this.client, body, __reply) as ModalSubmitInteraction;
|
||||
if (this.client.components?.hasModal(interaction)) {
|
||||
await this.client.components.onModalSubmit(interaction);
|
||||
} else await this.modal(interaction);
|
||||
}
|
||||
break;
|
||||
case InteractionType.MessageComponent:
|
||||
{
|
||||
const interaction = BaseInteraction.from(this.client, body, __reply) as ComponentInteraction;
|
||||
if (this.client.components?.hasComponent(body.message.id, interaction.customId)) {
|
||||
await this.client.components.onComponent(body.message.id, interaction);
|
||||
} else await this.messageComponent(interaction);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async message(rawMessage: GatewayMessageCreateDispatchData, shardId: number) {
|
||||
const self = this.client as Client | WorkerClient;
|
||||
if (!self.options.commands?.prefix) return;
|
||||
const message = Transformers.Message(this.client, rawMessage);
|
||||
const prefixes = (await self.options.commands.prefix(message)).sort((a, b) => b.length - a.length);
|
||||
const prefix = prefixes.find(x => rawMessage.content.startsWith(x));
|
||||
|
||||
if (!(prefix !== undefined && rawMessage.content.startsWith(prefix))) return;
|
||||
|
||||
const content = rawMessage.content.slice(prefix.length).trimStart();
|
||||
|
||||
const { fullCommandName, command, parent, argsContent } = this.resolveCommandFromContent(
|
||||
content,
|
||||
prefix,
|
||||
rawMessage,
|
||||
);
|
||||
|
||||
if (!command || argsContent === undefined) return;
|
||||
if (!command.run) return self.logger.warn(`${fullCommandName} command does not have 'run' callback`);
|
||||
|
||||
if (!(command.contexts.includes(InteractionContextType.BotDM) || rawMessage.guild_id)) return;
|
||||
if (!command.contexts.includes(InteractionContextType.Guild) && rawMessage.guild_id) return;
|
||||
if (command.guildId && !command.guildId?.includes(rawMessage.guild_id!)) return;
|
||||
|
||||
const resolved: MakeRequired<ContextOptionsResolved> = {
|
||||
channels: {},
|
||||
roles: {},
|
||||
users: {},
|
||||
members: {},
|
||||
attachments: {},
|
||||
};
|
||||
|
||||
const args = this.argsParser(argsContent, command, message);
|
||||
const { options, errors } = await this.argsOptionsParser(command, rawMessage, args, resolved);
|
||||
const optionsResolver = this.makeResolver(self, options, parent as Command, rawMessage.guild_id, resolved);
|
||||
const context = new CommandContext(self, message, optionsResolver, shardId, command);
|
||||
//@ts-expect-error
|
||||
const extendContext = self.options?.context?.(message) ?? {};
|
||||
Object.assign(context, extendContext);
|
||||
|
||||
try {
|
||||
if (errors.length) {
|
||||
return command.onOptionsError?.(
|
||||
context,
|
||||
Object.fromEntries(
|
||||
errors.map(x => {
|
||||
return [
|
||||
x.name,
|
||||
{
|
||||
failed: true,
|
||||
value: x.error,
|
||||
parseError: x.fullError,
|
||||
},
|
||||
];
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (command.defaultMemberPermissions && rawMessage.guild_id) {
|
||||
const memberPermissions = await self.members.permissions(rawMessage.guild_id, rawMessage.author.id);
|
||||
const permissions = this.checkPermissions(memberPermissions, command.defaultMemberPermissions);
|
||||
const guild = await this.client.guilds.raw(rawMessage.guild_id);
|
||||
if (permissions && guild.owner_id !== rawMessage.author.id) {
|
||||
return command.onPermissionsFail?.(context, memberPermissions.keys(permissions));
|
||||
}
|
||||
}
|
||||
|
||||
if (command.botPermissions && rawMessage.guild_id) {
|
||||
const meMember = await self.cache.members?.get(self.botId, rawMessage.guild_id);
|
||||
if (!meMember) return; //enable member cache and "Guilds" intent, lol
|
||||
const appPermissions = await meMember.fetchPermissions();
|
||||
const permissions = this.checkPermissions(appPermissions, command.botPermissions);
|
||||
if (!appPermissions.has('Administrator') && permissions) {
|
||||
return command.onBotPermissionsFail?.(context, permissions);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(await this.runOptions(command, context, optionsResolver))) return;
|
||||
|
||||
const resultGlobal = await this.runGlobalMiddlewares(command, context);
|
||||
if (typeof resultGlobal === 'boolean') return;
|
||||
const resultMiddle = await this.runMiddlewares(command, context);
|
||||
if (typeof resultMiddle === 'boolean') return;
|
||||
try {
|
||||
await command.run!(context);
|
||||
await command.onAfterRun?.(context, undefined);
|
||||
} catch (error) {
|
||||
await command.onRunError?.(context, error);
|
||||
await command.onAfterRun?.(context, error);
|
||||
}
|
||||
} catch (error) {
|
||||
try {
|
||||
await command.onInternalError?.(this.client, command, error);
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
argsParser(content: string, _command: SubCommand | Command, _message: MessageStructure): Record<string, string> {
|
||||
const args: Record<string, string> = {};
|
||||
for (const i of content.match(/-(.*?)(?=\s-|$)/gs) ?? []) {
|
||||
args[i.slice(1).split(' ')[0]] = i.split(' ').slice(1).join(' ');
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
resolveCommandFromContent(
|
||||
content: string,
|
||||
_prefix: string,
|
||||
_message: GatewayMessageCreateDispatchData,
|
||||
): CommandFromContent & { argsContent?: string } {
|
||||
const result = this.getCommandFromContent(
|
||||
content
|
||||
.split(' ')
|
||||
.filter(x => x)
|
||||
.slice(0, 3),
|
||||
);
|
||||
|
||||
if (!result.command) return result;
|
||||
|
||||
let newContent = content;
|
||||
for (const i of result.fullCommandName.split(' ')) {
|
||||
newContent = newContent.slice(newContent.indexOf(i) + i.length);
|
||||
}
|
||||
|
||||
return {
|
||||
...result,
|
||||
argsContent: newContent.slice(1),
|
||||
};
|
||||
}
|
||||
|
||||
getCommandFromContent(commandRaw: string[]): CommandFromContent {
|
||||
const rawParentName = commandRaw[0];
|
||||
const rawGroupName = commandRaw.length === 3 ? commandRaw[1] : undefined;
|
||||
const rawSubcommandName = rawGroupName ? commandRaw[2] : commandRaw[1];
|
||||
const parent = this.getParentMessageCommand(rawParentName);
|
||||
const fullCommandName = `${rawParentName}${
|
||||
rawGroupName ? ` ${rawGroupName} ${rawSubcommandName}` : `${rawSubcommandName ? ` ${rawSubcommandName}` : ''}`
|
||||
}`;
|
||||
|
||||
if (!(parent instanceof Command)) return { fullCommandName };
|
||||
|
||||
if (rawGroupName && !parent.groups?.[rawGroupName] && !parent.groupsAliases?.[rawGroupName])
|
||||
return this.getCommandFromContent([rawParentName, rawGroupName]);
|
||||
if (
|
||||
rawSubcommandName &&
|
||||
!parent.options?.some(
|
||||
x => x instanceof SubCommand && (x.name === rawSubcommandName || x.aliases?.includes(rawSubcommandName)),
|
||||
)
|
||||
)
|
||||
return this.getCommandFromContent([rawParentName]);
|
||||
|
||||
const groupName = rawGroupName ? parent.groupsAliases?.[rawGroupName] || rawGroupName : undefined;
|
||||
|
||||
const command =
|
||||
groupName || rawSubcommandName
|
||||
? (parent.options?.find(opt => {
|
||||
if (opt instanceof SubCommand) {
|
||||
if (groupName) {
|
||||
if (opt.group !== groupName) return false;
|
||||
}
|
||||
if (opt.group && !groupName) return false;
|
||||
return rawSubcommandName === opt.name || opt.aliases?.includes(rawSubcommandName);
|
||||
}
|
||||
return false;
|
||||
}) as SubCommand)
|
||||
: parent;
|
||||
|
||||
return {
|
||||
command,
|
||||
fullCommandName,
|
||||
parent,
|
||||
};
|
||||
}
|
||||
|
||||
makeResolver(...args: Parameters<(typeof Transformers)['OptionResolver']>) {
|
||||
return Transformers.OptionResolver(...args);
|
||||
}
|
||||
|
||||
getParentMessageCommand(rawParentName: string) {
|
||||
return this.client.commands!.values.find(
|
||||
x =>
|
||||
(!('ignore' in x) || x.ignore !== IgnoreCommand.Message) &&
|
||||
(x.name === rawParentName || ('aliases' in x ? x.aliases?.includes(rawParentName) : false)),
|
||||
);
|
||||
}
|
||||
|
||||
getCommand<T extends Command | ContextMenuCommand>(data: {
|
||||
guild_id?: string;
|
||||
name: string;
|
||||
}): T | undefined {
|
||||
return this.client.commands!.values.find(command => {
|
||||
if (data.guild_id) {
|
||||
return command.guildId?.includes(data.guild_id) && command.name === data.name;
|
||||
}
|
||||
return command.name === data.name;
|
||||
}) as T;
|
||||
}
|
||||
|
||||
checkPermissions(app: PermissionsBitField, bot: bigint) {
|
||||
const permissions = app.missings(...app.values([bot]));
|
||||
if (!app.has('Administrator') && permissions.length) {
|
||||
return app.keys(permissions);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async fetchChannel(_option: CommandOptionWithType, id: string) {
|
||||
return this.client.channels.raw(id);
|
||||
}
|
||||
|
||||
async fetchUser(_option: CommandOptionWithType, id: string) {
|
||||
return this.client.users.raw(id);
|
||||
}
|
||||
|
||||
async fetchMember(_option: CommandOptionWithType, id: string, guildId: string) {
|
||||
return this.client.members.raw(guildId, id);
|
||||
}
|
||||
|
||||
async fetchRole(_option: CommandOptionWithType, id: string, guildId?: string) {
|
||||
return guildId ? (await this.client.roles.listRaw(guildId)).find(x => x.id === id) : undefined;
|
||||
}
|
||||
|
||||
async runGlobalMiddlewares(
|
||||
command: Command | ContextMenuCommand | SubCommand,
|
||||
context: CommandContext<{}, never> | MenuCommandContext<any>,
|
||||
) {
|
||||
try {
|
||||
const resultRunGlobalMiddlewares = await BaseCommand.__runMiddlewares(
|
||||
context,
|
||||
(this.client.options?.globalMiddlewares ?? []) as keyof RegisteredMiddlewares,
|
||||
true,
|
||||
);
|
||||
if (resultRunGlobalMiddlewares.pass) {
|
||||
return false;
|
||||
}
|
||||
if ('error' in resultRunGlobalMiddlewares) {
|
||||
await command.onMiddlewaresError?.(context as never, resultRunGlobalMiddlewares.error ?? 'Unknown error');
|
||||
return;
|
||||
}
|
||||
return resultRunGlobalMiddlewares;
|
||||
} catch (e) {
|
||||
try {
|
||||
await command.onInternalError?.(this.client, command as never, e);
|
||||
} catch {}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async runMiddlewares(
|
||||
command: Command | ContextMenuCommand | SubCommand,
|
||||
context: CommandContext<{}, never> | MenuCommandContext<any>,
|
||||
) {
|
||||
try {
|
||||
const resultRunMiddlewares = await BaseCommand.__runMiddlewares(
|
||||
context,
|
||||
command.middlewares as keyof RegisteredMiddlewares,
|
||||
false,
|
||||
);
|
||||
if (resultRunMiddlewares.pass) {
|
||||
return false;
|
||||
}
|
||||
if ('error' in resultRunMiddlewares) {
|
||||
await command.onMiddlewaresError?.(context as never, resultRunMiddlewares.error ?? 'Unknown error');
|
||||
return;
|
||||
}
|
||||
return resultRunMiddlewares;
|
||||
} catch (e) {
|
||||
try {
|
||||
await command.onInternalError?.(this.client, command as never, e);
|
||||
} catch {}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
makeMenuCommand(body: APIApplicationCommandInteraction, shardId: number, __reply?: __InternalReplyFunction) {
|
||||
const command = this.getCommand<ContextMenuCommand>(body.data);
|
||||
const interaction = BaseInteraction.from(this.client, body, __reply) as
|
||||
| UserCommandInteraction
|
||||
| MessageCommandInteraction;
|
||||
// idc, is a YOU problem
|
||||
if (!command?.run)
|
||||
return this.client.logger.warn(`${command?.name ?? 'Unknown'} command does not have 'run' callback`);
|
||||
const context = new MenuCommandContext(this.client, interaction, shardId, command);
|
||||
const extendContext = this.client.options?.context?.(interaction) ?? {};
|
||||
Object.assign(context, extendContext);
|
||||
|
||||
return { command, interaction, context };
|
||||
}
|
||||
|
||||
async runOptions(command: Command | SubCommand, context: CommandContext, resolver: OptionResolverStructure) {
|
||||
const [erroredOptions, result] = await command.__runOptions(context, resolver);
|
||||
if (erroredOptions) {
|
||||
try {
|
||||
await command.onOptionsError?.(context, result);
|
||||
} catch (e) {
|
||||
try {
|
||||
await command.onInternalError?.(this.client, command, e);
|
||||
} catch {}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async argsOptionsParser(
|
||||
command: Command | SubCommand,
|
||||
message: GatewayMessageCreateDispatchData,
|
||||
args: Partial<Record<string, string>>,
|
||||
resolved: MakeRequired<ContextOptionsResolved>,
|
||||
) {
|
||||
const options: APIApplicationCommandInteractionDataOption[] = [];
|
||||
const errors: { name: string; error: string; fullError: MessageCommandOptionErrors }[] = [];
|
||||
for (const i of (command.options ?? []) as (CommandOption & { type: ApplicationCommandOptionType })[]) {
|
||||
try {
|
||||
let value: string | boolean | number | undefined;
|
||||
let indexAttachment = -1;
|
||||
switch (i.type) {
|
||||
case ApplicationCommandOptionType.Attachment:
|
||||
if (message.attachments[++indexAttachment]) {
|
||||
value = message.attachments[indexAttachment].id;
|
||||
resolved.attachments[value] = message.attachments[indexAttachment];
|
||||
}
|
||||
break;
|
||||
case ApplicationCommandOptionType.Boolean:
|
||||
if (args[i.name]) {
|
||||
value = ['yes', 'y', 'true', 'treu'].includes(args[i.name]!.toLowerCase());
|
||||
}
|
||||
break;
|
||||
case ApplicationCommandOptionType.Channel:
|
||||
{
|
||||
const rawId =
|
||||
message.content.match(/(?<=<#)[0-9]{17,19}(?=>)/g)?.find(x => args[i.name]?.includes(x)) ||
|
||||
args[i.name]?.match(/[0-9]{17,19}/g)?.[0];
|
||||
if (!rawId) continue;
|
||||
const channel = (await this.client.cache.channels?.raw(rawId)) ?? (await this.fetchChannel(i, rawId));
|
||||
if (channel) {
|
||||
if ('channel_types' in i) {
|
||||
if (!(i as SeyfertChannelOption).channel_types!.includes(channel.type)) {
|
||||
errors.push({
|
||||
name: i.name,
|
||||
error: `The entered channel type is not one of ${(i as SeyfertChannelOption)
|
||||
.channel_types!.map(t => ChannelType[t])
|
||||
.join(', ')}`,
|
||||
fullError: ['CHANNEL_TYPES', (i as SeyfertChannelOption).channel_types!],
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
value = rawId;
|
||||
//discord funny memoentnt!!!!!!!!
|
||||
resolved.channels[rawId] = channel as APIInteractionDataResolvedChannel;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ApplicationCommandOptionType.Mentionable:
|
||||
{
|
||||
const matches = message.content.match(/<@[0-9]{17,19}(?=>)|<@&[0-9]{17,19}(?=>)/g) ?? [];
|
||||
for (const match of matches) {
|
||||
if (match.includes('&')) {
|
||||
const rawId = match.slice(3);
|
||||
if (rawId) {
|
||||
const role =
|
||||
(await this.client.cache.roles?.raw(rawId)) ?? (await this.fetchRole(i, rawId, message.guild_id));
|
||||
if (role) {
|
||||
value = rawId;
|
||||
resolved.roles[rawId] = role;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const rawId = match.slice(2);
|
||||
const raw = message.mentions.find(x => rawId === x.id);
|
||||
if (raw) {
|
||||
const { member, ...user } = raw;
|
||||
value = raw.id;
|
||||
resolved.users[raw.id] = user;
|
||||
if (member) resolved.members[raw.id] = member;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ApplicationCommandOptionType.Role:
|
||||
{
|
||||
const rawId =
|
||||
message.mention_roles.find(x => args[i.name]?.includes(x)) || args[i.name]?.match(/[0-9]{17,19}/g)?.[0];
|
||||
if (!rawId) continue;
|
||||
const role =
|
||||
(await this.client.cache.roles?.raw(rawId)) ?? (await this.fetchRole(i, rawId, message.guild_id));
|
||||
if (role) {
|
||||
value = rawId;
|
||||
resolved.roles[rawId] = role;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ApplicationCommandOptionType.User:
|
||||
{
|
||||
const rawId =
|
||||
message.mentions.find(x => args[i.name]?.includes(x.id))?.id ||
|
||||
args[i.name]?.match(/[0-9]{17,19}/g)?.[0];
|
||||
if (!rawId) continue;
|
||||
const raw =
|
||||
message.mentions.find(x => args[i.name]?.includes(x.id)) ??
|
||||
(await this.client.cache.users?.raw(rawId)) ??
|
||||
(await this.fetchUser(i, rawId));
|
||||
if (raw) {
|
||||
value = raw.id;
|
||||
resolved.users[raw.id] = raw;
|
||||
if (message.guild_id) {
|
||||
const member =
|
||||
message.mentions.find(x => args[i.name]?.includes(x.id))?.member ??
|
||||
(await this.client.cache.members?.raw(rawId, message.guild_id)) ??
|
||||
(await this.fetchMember(i, rawId, message.guild_id));
|
||||
if (member) resolved.members[raw.id] = member;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ApplicationCommandOptionType.String:
|
||||
{
|
||||
value = args[i.name];
|
||||
const option = i as SeyfertStringOption;
|
||||
if (!value) break;
|
||||
if (option.min_length) {
|
||||
if (value.length < option.min_length) {
|
||||
value = undefined;
|
||||
errors.push({
|
||||
name: i.name,
|
||||
error: `The entered string has less than ${option.min_length} characters. The minimum required is ${option.min_length} characters.`,
|
||||
fullError: ['STRING_MIN_LENGTH', option.min_length],
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (option.max_length) {
|
||||
if (value.length > option.max_length) {
|
||||
value = undefined;
|
||||
errors.push({
|
||||
name: i.name,
|
||||
error: `The entered string has more than ${option.max_length} characters. The maximum required is ${option.max_length} characters.`,
|
||||
fullError: ['STRING_MAX_LENGTH', option.max_length],
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (option.choices?.length) {
|
||||
const choice = option.choices.find(x => x.name === value);
|
||||
if (!choice) {
|
||||
value = undefined;
|
||||
errors.push({
|
||||
name: i.name,
|
||||
error: `The entered choice is invalid. Please choose one of the following options: ${option.choices
|
||||
.map(x => x.name)
|
||||
.join(', ')}.`,
|
||||
fullError: ['STRING_INVALID_CHOICE', option.choices],
|
||||
});
|
||||
break;
|
||||
}
|
||||
value = choice.value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ApplicationCommandOptionType.Number:
|
||||
case ApplicationCommandOptionType.Integer:
|
||||
{
|
||||
const option = i as SeyfertNumberOption | SeyfertIntegerOption;
|
||||
if (!option.choices?.length) {
|
||||
value = Number(args[i.name]);
|
||||
if (args[i.name] === undefined) {
|
||||
value = undefined;
|
||||
break;
|
||||
}
|
||||
if (Number.isNaN(value)) {
|
||||
value = undefined;
|
||||
errors.push({
|
||||
name: i.name,
|
||||
error: 'The entered choice is an invalid number.',
|
||||
fullError: ['NUMBER_NAN', args[i.name]],
|
||||
});
|
||||
break;
|
||||
}
|
||||
if (option.min_value) {
|
||||
if (value < option.min_value) {
|
||||
value = undefined;
|
||||
errors.push({
|
||||
name: i.name,
|
||||
error: `The entered number is less than ${option.min_value}. The minimum allowed is ${option.min_value}`,
|
||||
fullError: ['NUMBER_MIN_VALUE', option.min_value],
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (option.max_value) {
|
||||
if (value > option.max_value) {
|
||||
value = undefined;
|
||||
errors.push({
|
||||
name: i.name,
|
||||
error: `The entered number is greater than ${option.max_value}. The maximum allowed is ${option.max_value}`,
|
||||
fullError: ['NUMBER_MAX_VALUE', option.max_value],
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
const choice = option.choices.find(x => x.name === args[i.name]);
|
||||
if (!choice) {
|
||||
value = undefined;
|
||||
errors.push({
|
||||
name: i.name,
|
||||
error: `The entered choice is invalid. Please choose one of the following options: ${option.choices
|
||||
.map(x => x.name)
|
||||
.join(', ')}.`,
|
||||
fullError: ['NUMBER_INVALID_CHOICE', option.choices],
|
||||
});
|
||||
break;
|
||||
}
|
||||
value = choice.value;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (value !== undefined) {
|
||||
options.push({
|
||||
name: i.name,
|
||||
type: i.type,
|
||||
value,
|
||||
} as APIApplicationCommandInteractionDataOption);
|
||||
} else if (i.required)
|
||||
if (!errors.some(x => x.name === i.name))
|
||||
errors.push({
|
||||
error: 'Option is required but returned undefined',
|
||||
name: i.name,
|
||||
fullError: ['OPTION_REQUIRED'],
|
||||
});
|
||||
} catch (e) {
|
||||
errors.push({
|
||||
error: e && typeof e === 'object' && 'message' in e ? (e.message as string) : `${e}`,
|
||||
name: i.name,
|
||||
fullError: ['UNKNOWN', e],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { errors, options };
|
||||
}
|
||||
}
|
@ -10,9 +10,9 @@ import {
|
||||
type APIApplicationCommandChannelOption,
|
||||
} from 'discord-api-types/v10';
|
||||
import { basename, dirname } from 'node:path';
|
||||
import type { Logger } from '../common';
|
||||
import type { Logger, MakeRequired, NulleableCoalising, OmitInsert } from '../common';
|
||||
import { BaseHandler } from '../common';
|
||||
import { Command, SubCommand } from './applications/chat';
|
||||
import { Command, type CommandOption, SubCommand } from './applications/chat';
|
||||
import { ContextMenuCommand } from './applications/menu';
|
||||
import type { UsingClient } from './applications/shared';
|
||||
import { promises } from 'node:fs';
|
||||
@ -169,26 +169,21 @@ export class CommandHandler extends BaseHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
async load(commandsDir: string, client: UsingClient, instances?: { new (): Command | ContextMenuCommand }[]) {
|
||||
const result =
|
||||
instances?.map(x => {
|
||||
const i = new x();
|
||||
return { name: i.name, file: x, path: i.__filePath ?? '*' };
|
||||
}) ??
|
||||
(
|
||||
await this.loadFilesK<{ new (): Command | SubCommand | ContextMenuCommand }>(await this.getFiles(commandsDir))
|
||||
).filter(x => x.file);
|
||||
async load(commandsDir: string, client: UsingClient) {
|
||||
const result = await this.loadFilesK<FileLoaded<null>>(await this.getFiles(commandsDir));
|
||||
this.values = [];
|
||||
|
||||
for (const command of result) {
|
||||
for (const { commands, file } of result.map(x => ({ commands: this.onFile(x.file), file: x }))) {
|
||||
if (!commands) continue;
|
||||
for (const command of commands) {
|
||||
let commandInstance;
|
||||
try {
|
||||
commandInstance = this.onCommand(command.file);
|
||||
commandInstance = this.onCommand(command);
|
||||
if (!commandInstance) continue;
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message.includes('is not a constructor')) {
|
||||
this.logger.warn(
|
||||
`${command.path
|
||||
`${file.path
|
||||
.split(process.cwd())
|
||||
.slice(1)
|
||||
.join(process.cwd())} doesn't export the class by \`export default <Command>\``,
|
||||
@ -196,47 +191,202 @@ export class CommandHandler extends BaseHandler {
|
||||
} else this.logger.warn(e, command);
|
||||
continue;
|
||||
}
|
||||
if (commandInstance instanceof ContextMenuCommand) {
|
||||
this.values.push(commandInstance);
|
||||
commandInstance.__filePath = command.path;
|
||||
this.__parseCommandLocales(commandInstance);
|
||||
commandInstance.props ??= client.options.commands?.defaults?.props ?? {};
|
||||
continue;
|
||||
}
|
||||
if (!(commandInstance instanceof Command)) {
|
||||
continue;
|
||||
}
|
||||
commandInstance.onAfterRun ??= client.options.commands?.defaults?.onAfterRun;
|
||||
commandInstance.onBotPermissionsFail ??= client.options.commands?.defaults?.onBotPermissionsFail;
|
||||
commandInstance.onInternalError ??= client.options.commands?.defaults?.onInternalError;
|
||||
commandInstance.onMiddlewaresError ??= client.options.commands?.defaults?.onMiddlewaresError;
|
||||
commandInstance.onOptionsError ??= client.options.commands?.defaults?.onOptionsError;
|
||||
commandInstance.onPermissionsFail ??= client.options.commands?.defaults?.onPermissionsFail;
|
||||
commandInstance.onRunError ??= client.options.commands?.defaults?.onRunError;
|
||||
commandInstance.__filePath = command.path;
|
||||
commandInstance.options ??= [] as NonNullable<Command['options']>;
|
||||
if (commandInstance instanceof SubCommand) continue;
|
||||
|
||||
commandInstance.__filePath = file.path;
|
||||
commandInstance.props ??= client.options.commands?.defaults?.props ?? {};
|
||||
const isAvailableCommand = this.stablishCommandDefaults(commandInstance);
|
||||
if (isAvailableCommand) {
|
||||
commandInstance = isAvailableCommand;
|
||||
if (commandInstance.__autoload) {
|
||||
//@AutoLoad
|
||||
const options = await this.getFiles(dirname(command.path));
|
||||
const options = await this.getFiles(dirname(file.path));
|
||||
for (const option of options) {
|
||||
if (command.name === basename(option)) {
|
||||
if (file.name === basename(option)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
const subCommand = this.onSubCommand(result.find(x => x.path === option)!.file as { new (): SubCommand });
|
||||
const fileSubCommands = this.onFile(result.find(x => x.path === option)!.file);
|
||||
if (!fileSubCommands) {
|
||||
this.logger.warn(`SubCommand returned (${fileSubCommands}) ignoring.`);
|
||||
continue;
|
||||
}
|
||||
for (const fileSubCommand of fileSubCommands) {
|
||||
const subCommand = this.onSubCommand(fileSubCommand as HandleableSubCommand);
|
||||
if (subCommand && subCommand instanceof SubCommand) {
|
||||
subCommand.__filePath = option;
|
||||
commandInstance.options.push(subCommand);
|
||||
} else {
|
||||
this.logger.warn(subCommand ? 'SubCommand expected' : 'Invalid SubCommand', subCommand);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
//pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const option of commandInstance.options ?? []) {
|
||||
if (option instanceof SubCommand) this.stablishSubCommandDefaults(commandInstance, option);
|
||||
}
|
||||
}
|
||||
this.stablishContextCommandDefaults(commandInstance);
|
||||
this.values.push(commandInstance);
|
||||
this.parseLocales(commandInstance);
|
||||
}
|
||||
}
|
||||
|
||||
return this.values;
|
||||
}
|
||||
|
||||
parseLocales(command: Command | SubCommand | ContextMenuCommand) {
|
||||
this.parseGlobalLocales(command);
|
||||
if (command instanceof ContextMenuCommand) {
|
||||
this.parseContextMenuLocales(command);
|
||||
return command;
|
||||
}
|
||||
|
||||
if (command instanceof Command && command.__tGroups) {
|
||||
this.parseCommandLocales(command);
|
||||
for (const option of command.options ?? []) {
|
||||
if (option instanceof SubCommand) {
|
||||
this.parseSubCommandLocales(option);
|
||||
continue;
|
||||
}
|
||||
// @ts-expect-error
|
||||
if (option.locales) this.parseCommandOptionLocales(option);
|
||||
}
|
||||
}
|
||||
if (command instanceof SubCommand) {
|
||||
this.parseSubCommandLocales(command);
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
parseGlobalLocales(command: Command | SubCommand | ContextMenuCommand) {
|
||||
if (command.__t) {
|
||||
command.name_localizations = {};
|
||||
command.description_localizations = {};
|
||||
for (const locale of Object.keys(this.client.langs!.values)) {
|
||||
const locales = this.client.langs!.aliases.find(x => x[0] === locale)?.[1] ?? [];
|
||||
if (Object.values<string>(Locale).includes(locale)) locales.push(locale as LocaleString);
|
||||
|
||||
if (command.__t.name) {
|
||||
for (const i of locales) {
|
||||
const valueName = this.client.langs!.getKey(locale, command.__t.name!);
|
||||
if (valueName) command.name_localizations[i] = valueName;
|
||||
}
|
||||
}
|
||||
|
||||
if (command.__t.description) {
|
||||
for (const i of locales) {
|
||||
const valueKey = this.client.langs!.getKey(locale, command.__t.description!);
|
||||
if (valueKey) command.description_localizations[i] = valueKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseCommandOptionLocales(option: MakeRequired<CommandOption, 'locales'>) {
|
||||
option.name_localizations = {};
|
||||
option.description_localizations = {};
|
||||
for (const locale of Object.keys(this.client.langs!.values)) {
|
||||
const locales = this.client.langs!.aliases.find(x => x[0] === locale)?.[1] ?? [];
|
||||
if (Object.values<string>(Locale).includes(locale)) locales.push(locale as LocaleString);
|
||||
|
||||
if (option.locales.name) {
|
||||
for (const i of locales) {
|
||||
const valueName = this.client.langs!.getKey(locale, option.locales.name!);
|
||||
if (valueName) option.name_localizations[i] = valueName;
|
||||
}
|
||||
}
|
||||
|
||||
if (option.locales.description) {
|
||||
for (const i of locales) {
|
||||
const valueKey = this.client.langs!.getKey(locale, option.locales.description!);
|
||||
if (valueKey) option.description_localizations[i] = valueKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseCommandLocales(command: Command) {
|
||||
command.groups = {};
|
||||
for (const locale of Object.keys(this.client.langs!.values)) {
|
||||
const locales = this.client.langs!.aliases.find(x => x[0] === locale)?.[1] ?? [];
|
||||
if (Object.values<string>(Locale).includes(locale)) locales.push(locale as LocaleString);
|
||||
for (const group in command.__tGroups) {
|
||||
command.groups[group] ??= {
|
||||
defaultDescription: command.__tGroups[group].defaultDescription,
|
||||
description: [],
|
||||
name: [],
|
||||
};
|
||||
|
||||
if (command.__tGroups[group].name) {
|
||||
for (const i of locales) {
|
||||
const valueName = this.client.langs!.getKey(locale, command.__tGroups[group].name!);
|
||||
if (valueName) {
|
||||
command.groups[group].name!.push([i, valueName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (command.__tGroups[group].description) {
|
||||
for (const i of locales) {
|
||||
const valueKey = this.client.langs!.getKey(locale, command.__tGroups[group].description!);
|
||||
if (valueKey) {
|
||||
command.groups[group].description!.push([i, valueKey]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseContextMenuLocales(command: ContextMenuCommand) {
|
||||
return command;
|
||||
}
|
||||
|
||||
parseSubCommandLocales(command: SubCommand) {
|
||||
for (const i of command.options ?? []) {
|
||||
// @ts-expect-error
|
||||
if (i.locales) this.parseCommandOptionLocales(i);
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
stablishContextCommandDefaults(commandInstance: InstanceType<HandleableCommand>): ContextMenuCommand | false {
|
||||
if (!(commandInstance instanceof ContextMenuCommand)) return false;
|
||||
commandInstance.onAfterRun ??= this.client.options.commands?.defaults?.onAfterRun;
|
||||
//@ts-expect-error magic.
|
||||
commandInstance.onBotPermissionsFail ??= this.client.options.commands?.defaults?.onBotPermissionsFail;
|
||||
//@ts-expect-error magic.
|
||||
commandInstance.onInternalError ??= this.client.options.commands?.defaults?.onInternalError;
|
||||
//@ts-expect-error magic.
|
||||
commandInstance.onMiddlewaresError ??= this.client.options.commands?.defaults?.onMiddlewaresError;
|
||||
//@ts-expect-error magic.
|
||||
commandInstance.onPermissionsFail ??= this.client.options.commands?.defaults?.onPermissionsFail;
|
||||
//@ts-expect-error magic.
|
||||
commandInstance.onRunError ??= this.client.options.commands?.defaults?.onRunError;
|
||||
return commandInstance;
|
||||
}
|
||||
|
||||
stablishCommandDefaults(
|
||||
commandInstance: InstanceType<HandleableCommand>,
|
||||
): OmitInsert<Command, 'options', { options: NonNullable<Command['options']> }> | false {
|
||||
if (!(commandInstance instanceof Command)) return false;
|
||||
commandInstance.onAfterRun ??= this.client.options.commands?.defaults?.onAfterRun;
|
||||
commandInstance.onBotPermissionsFail ??= this.client.options.commands?.defaults?.onBotPermissionsFail;
|
||||
commandInstance.onInternalError ??= this.client.options.commands?.defaults?.onInternalError;
|
||||
commandInstance.onMiddlewaresError ??= this.client.options.commands?.defaults?.onMiddlewaresError;
|
||||
commandInstance.onOptionsError ??= this.client.options.commands?.defaults?.onOptionsError;
|
||||
commandInstance.onPermissionsFail ??= this.client.options.commands?.defaults?.onPermissionsFail;
|
||||
commandInstance.onRunError ??= this.client.options.commands?.defaults?.onRunError;
|
||||
commandInstance.options ??= [];
|
||||
return commandInstance as any;
|
||||
}
|
||||
|
||||
stablishSubCommandDefaults(commandInstance: Command, option: SubCommand): SubCommand {
|
||||
option.middlewares = (commandInstance.middlewares ?? []).concat(option.middlewares ?? []);
|
||||
option.onMiddlewaresError =
|
||||
option.onMiddlewaresError?.bind(option) ??
|
||||
@ -271,121 +421,25 @@ export class CommandHandler extends BaseHandler {
|
||||
option.contexts ??= commandInstance.contexts;
|
||||
option.integrationTypes ??= commandInstance.integrationTypes;
|
||||
option.props ??= commandInstance.props;
|
||||
return option;
|
||||
}
|
||||
|
||||
onFile(file: FileLoaded): HandleableCommand[] | undefined {
|
||||
return file.default ? [file.default] : undefined;
|
||||
}
|
||||
|
||||
onCommand(file: HandleableCommand): Command | SubCommand | ContextMenuCommand | false {
|
||||
return new file();
|
||||
}
|
||||
|
||||
onSubCommand(file: HandleableSubCommand): SubCommand | false {
|
||||
return new file();
|
||||
}
|
||||
}
|
||||
|
||||
this.values.push(commandInstance);
|
||||
this.__parseCommandLocales(commandInstance);
|
||||
export type FileLoaded<T = null> = {
|
||||
default?: NulleableCoalising<T, HandleableCommand>;
|
||||
} & Record<string, NulleableCoalising<T, HandleableCommand>>;
|
||||
|
||||
for (const i of commandInstance.options ?? []) {
|
||||
if (i instanceof SubCommand) {
|
||||
this.__parseCommandLocales(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.values;
|
||||
}
|
||||
|
||||
private __parseCommandLocales(command: Command | SubCommand | ContextMenuCommand) {
|
||||
if (command.__t) {
|
||||
command.name_localizations = {};
|
||||
command.description_localizations = {};
|
||||
for (const locale of Object.keys(this.client.langs!.values)) {
|
||||
const locales = this.client.langs!.aliases.find(x => x[0] === locale)?.[1] ?? [];
|
||||
if (Object.values<string>(Locale).includes(locale)) locales.push(locale as LocaleString);
|
||||
|
||||
if (command.__t.name) {
|
||||
for (const i of locales) {
|
||||
const valueName = this.client.langs!.getKey(locale, command.__t.name!);
|
||||
if (valueName) command.name_localizations[i] = valueName;
|
||||
}
|
||||
}
|
||||
|
||||
if (command.__t.description) {
|
||||
for (const i of locales) {
|
||||
const valueKey = this.client.langs!.getKey(locale, command.__t.description!);
|
||||
if (valueKey) command.description_localizations[i] = valueKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (command instanceof ContextMenuCommand) return;
|
||||
|
||||
for (const options of command.options ?? []) {
|
||||
if (options instanceof SubCommand || !options.locales) continue;
|
||||
options.name_localizations = {};
|
||||
options.description_localizations = {};
|
||||
for (const locale of Object.keys(this.client.langs!.values)) {
|
||||
const locales = this.client.langs!.aliases.find(x => x[0] === locale)?.[1] ?? [];
|
||||
if (Object.values<string>(Locale).includes(locale)) locales.push(locale as LocaleString);
|
||||
|
||||
if (options.locales.name) {
|
||||
for (const i of locales) {
|
||||
const valueName = this.client.langs!.getKey(locale, options.locales.name!);
|
||||
if (valueName) options.name_localizations[i] = valueName;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.locales.description) {
|
||||
for (const i of locales) {
|
||||
const valueKey = this.client.langs!.getKey(locale, options.locales.description!);
|
||||
if (valueKey) options.description_localizations[i] = valueKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (command instanceof Command && command.__tGroups) {
|
||||
command.groups = {};
|
||||
for (const locale of Object.keys(this.client.langs!.values)) {
|
||||
const locales = this.client.langs!.aliases.find(x => x[0] === locale)?.[1] ?? [];
|
||||
if (Object.values<string>(Locale).includes(locale)) locales.push(locale as LocaleString);
|
||||
for (const group in command.__tGroups) {
|
||||
command.groups[group] ??= {
|
||||
defaultDescription: command.__tGroups[group].defaultDescription,
|
||||
description: [],
|
||||
name: [],
|
||||
};
|
||||
|
||||
if (command.__tGroups[group].name) {
|
||||
for (const i of locales) {
|
||||
const valueName = this.client.langs!.getKey(locale, command.__tGroups[group].name!);
|
||||
if (valueName) {
|
||||
command.groups[group].name!.push([i, valueName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (command.__tGroups[group].description) {
|
||||
for (const i of locales) {
|
||||
const valueKey = this.client.langs!.getKey(locale, command.__tGroups[group].description!);
|
||||
if (valueKey) {
|
||||
command.groups[group].description!.push([i, valueKey]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setHandlers({
|
||||
onCommand,
|
||||
onSubCommand,
|
||||
}: {
|
||||
onCommand?: CommandHandler['onCommand'];
|
||||
onSubCommand?: CommandHandler['onSubCommand'];
|
||||
}) {
|
||||
if (onCommand) this.onCommand = onCommand;
|
||||
if (onSubCommand) this.onSubCommand = onSubCommand;
|
||||
}
|
||||
|
||||
onCommand = (file: { new (): Command | SubCommand | ContextMenuCommand }):
|
||||
| Command
|
||||
| SubCommand
|
||||
| ContextMenuCommand
|
||||
| false => new file();
|
||||
onSubCommand = (file: { new (): SubCommand }): SubCommand | false => new file();
|
||||
}
|
||||
export type HandleableCommand = new () => Command | SubCommand | ContextMenuCommand;
|
||||
export type HandleableSubCommand = new () => SubCommand;
|
||||
|
@ -8,23 +8,26 @@ import {
|
||||
type APIUser,
|
||||
ApplicationCommandOptionType,
|
||||
} from 'discord-api-types/v10';
|
||||
import { Attachment, GuildMember } from '..';
|
||||
import { Attachment } from '..';
|
||||
import type { MakeRequired } from '../common';
|
||||
import type { AllChannels } from '../structures';
|
||||
import { GuildRole, InteractionGuildMember, User } from '../structures';
|
||||
import channelFrom from '../structures/channels';
|
||||
import type { Command, CommandAutocompleteOption, CommandOption, SubCommand } from './applications/chat';
|
||||
import type { UsingClient } from './applications/shared';
|
||||
import {
|
||||
type GuildMemberStructure,
|
||||
type GuildRoleStructure,
|
||||
type InteractionGuildMemberStructure,
|
||||
Transformers,
|
||||
type UserStructure,
|
||||
} from '../client/transformers';
|
||||
|
||||
export type ContextOptionsResolved = {
|
||||
members?: Record<
|
||||
string,
|
||||
APIGuildMember | Omit<APIGuildMember, 'user'> | APIInteractionGuildMember | GuildMember | InteractionGuildMember
|
||||
>;
|
||||
users?: Record<string, APIUser | User>;
|
||||
roles?: Record<string, APIRole | GuildRole>;
|
||||
channels?: Record<string, APIInteractionDataResolvedChannel | AllChannels>;
|
||||
attachments?: Record<string, APIAttachment | Attachment>;
|
||||
members?: Record<string, APIGuildMember | Omit<APIGuildMember, 'user'> | APIInteractionGuildMember>;
|
||||
users?: Record<string, APIUser>;
|
||||
roles?: Record<string, APIRole>;
|
||||
channels?: Record<string, APIInteractionDataResolvedChannel>;
|
||||
attachments?: Record<string, APIAttachment>;
|
||||
};
|
||||
|
||||
export class OptionResolver {
|
||||
@ -164,18 +167,16 @@ export class OptionResolver {
|
||||
const value = resolve.value as string;
|
||||
const user = resolved.users?.[value];
|
||||
if (user) {
|
||||
resolve.user = user instanceof User ? user : new User(this.client, user);
|
||||
resolve.user = Transformers.User(this.client, user);
|
||||
}
|
||||
|
||||
const member = resolved.members?.[value];
|
||||
|
||||
if (member) {
|
||||
resolve.member =
|
||||
member instanceof GuildMember || member instanceof InteractionGuildMember
|
||||
? member
|
||||
: 'permissions' in member
|
||||
? new InteractionGuildMember(this.client, member, user!, this.guildId!)
|
||||
: new GuildMember(this.client, member, user!, this.guildId!);
|
||||
'permissions' in member
|
||||
? Transformers.InteractionGuildMember(this.client, member, user!, this.guildId!)
|
||||
: Transformers.GuildMember(this.client, member, user!, this.guildId!);
|
||||
}
|
||||
|
||||
const channel = resolved.channels?.[value];
|
||||
@ -185,7 +186,7 @@ export class OptionResolver {
|
||||
|
||||
const role = resolved.roles?.[value];
|
||||
if (role) {
|
||||
resolve.role = role instanceof GuildRole ? role : new GuildRole(this.client, role, this.guildId!);
|
||||
resolve.role = Transformers.GuildRole(this.client, role, this.guildId!);
|
||||
}
|
||||
|
||||
const attachment = resolved.attachments?.[value];
|
||||
@ -203,11 +204,11 @@ export interface OptionResolved {
|
||||
type: ApplicationCommandOptionType;
|
||||
value?: string | number | boolean;
|
||||
options?: OptionResolved[];
|
||||
user?: User;
|
||||
member?: GuildMember | InteractionGuildMember;
|
||||
user?: UserStructure;
|
||||
member?: GuildMemberStructure | InteractionGuildMemberStructure;
|
||||
attachment?: Attachment;
|
||||
channel?: AllChannels;
|
||||
role?: GuildRole;
|
||||
role?: GuildRoleStructure;
|
||||
focused?: boolean;
|
||||
}
|
||||
|
||||
|
@ -130,7 +130,7 @@ export class BaseHandler {
|
||||
* @param paths The paths of the files to load.
|
||||
* @returns A Promise that resolves to an array of loaded files.
|
||||
*/
|
||||
protected async loadFiles<T extends NonNullable<unknown>>(paths: string[]): Promise<T[]> {
|
||||
protected loadFiles<T extends NonNullable<unknown>>(paths: string[]): Promise<T[]> {
|
||||
return Promise.all(paths.map(path => magicImport(path).then(file => file.default ?? file)));
|
||||
}
|
||||
|
||||
@ -139,13 +139,13 @@ export class BaseHandler {
|
||||
* @param paths The paths of the files to load.
|
||||
* @returns A Promise that resolves to an array of objects containing name, file, and path.
|
||||
*/
|
||||
protected async loadFilesK<T>(paths: string[]): Promise<{ name: string; file: T; path: string }[]> {
|
||||
protected loadFilesK<T>(paths: string[]): Promise<{ name: string; file: T; path: string }[]> {
|
||||
return Promise.all(
|
||||
paths.map(path =>
|
||||
magicImport(path).then(file => {
|
||||
return {
|
||||
name: basename(path),
|
||||
file: file.default ?? file,
|
||||
file,
|
||||
path,
|
||||
};
|
||||
}),
|
||||
|
@ -5,7 +5,7 @@ import type {
|
||||
RESTPutAPIGuildBanJSONBody,
|
||||
} from 'discord-api-types/v10';
|
||||
import { BaseShorter } from './base';
|
||||
import { GuildBan } from '../../structures/GuildBan';
|
||||
import { Transformers } from '../../client/transformers';
|
||||
|
||||
export class BanShorter extends BaseShorter {
|
||||
/**
|
||||
@ -58,7 +58,7 @@ export class BanShorter extends BaseShorter {
|
||||
|
||||
ban = await this.client.proxy.guilds(guildId).bans(userId).get();
|
||||
await this.client.cache.members?.set(ban.user!.id, guildId, ban);
|
||||
return new GuildBan(this.client, ban, guildId);
|
||||
return Transformers.GuildBan(this.client, ban, guildId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,6 +81,6 @@ export class BanShorter extends BaseShorter {
|
||||
bans.map<[string, APIBan]>(x => [x.user!.id, x]),
|
||||
guildId,
|
||||
);
|
||||
return bans.map(m => new GuildBan(this.client, m, guildId));
|
||||
return bans.map(m => Transformers.GuildBan(this.client, m, guildId));
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,17 @@
|
||||
import {
|
||||
type APIChannel,
|
||||
PermissionFlagsBits,
|
||||
type RESTGetAPIChannelMessagesQuery,
|
||||
type RESTPatchAPIChannelJSONBody,
|
||||
type RESTPostAPIChannelThreadsJSONBody,
|
||||
type RESTPostAPIGuildForumThreadsJSONBody,
|
||||
} from 'discord-api-types/v10';
|
||||
import { BaseChannel, Message, type GuildMember, type GuildRole } from '../../structures';
|
||||
import { BaseChannel, type GuildRole, type GuildMember } from '../../structures';
|
||||
import channelFrom, { type AllChannels } from '../../structures/channels';
|
||||
import { PermissionsBitField } from '../../structures/extra/Permissions';
|
||||
import { BaseShorter } from './base';
|
||||
import { MergeOptions } from '../it/utils';
|
||||
import { type MessageStructure, Transformers } from '../../client/transformers';
|
||||
|
||||
export class ChannelShorter extends BaseShorter {
|
||||
/**
|
||||
@ -19,15 +21,19 @@ export class ChannelShorter extends BaseShorter {
|
||||
* @returns A Promise that resolves to the fetched channel.
|
||||
*/
|
||||
async fetch(id: string, force?: boolean): Promise<AllChannels> {
|
||||
return channelFrom(await this.raw(id, force), this.client);
|
||||
}
|
||||
|
||||
async raw(id: string, force?: boolean): Promise<APIChannel> {
|
||||
let channel;
|
||||
if (!force) {
|
||||
channel = await this.client.cache.channels?.get(id);
|
||||
channel = await this.client.cache.channels?.raw(id);
|
||||
if (channel) return channel;
|
||||
}
|
||||
|
||||
channel = await this.client.proxy.channels(id).get();
|
||||
await this.client.cache.channels?.patch(id, undefined, channel);
|
||||
return channelFrom(channel, this.client);
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,7 +83,7 @@ export class ChannelShorter extends BaseShorter {
|
||||
await this.client.proxy.channels(id).typing.post();
|
||||
}
|
||||
|
||||
async pins(channelId: string): Promise<Message[]> {
|
||||
async pins(channelId: string): Promise<MessageStructure[]> {
|
||||
const messages = await this.client.proxy.channels(channelId).pins.get();
|
||||
await this.client.cache.messages?.patch(
|
||||
messages.map(x => {
|
||||
@ -85,7 +91,7 @@ export class ChannelShorter extends BaseShorter {
|
||||
}) satisfies [string, any][],
|
||||
channelId,
|
||||
);
|
||||
return messages.map(message => new Message(this.client, message));
|
||||
return messages.map(message => Transformers.Message(this.client, message));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -194,7 +200,7 @@ export class ChannelShorter extends BaseShorter {
|
||||
}) satisfies [string, any][],
|
||||
channelId,
|
||||
);
|
||||
return result.map(message => new Message(this.client, message));
|
||||
return result.map(message => Transformers.Message(this.client, message));
|
||||
}
|
||||
|
||||
setVoiceStatus(channelId: string, status: string | null = null) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type { APIEmoji, RESTPatchAPIGuildEmojiJSONBody, RESTPostAPIGuildEmojiJSONBody } from 'discord-api-types/v10';
|
||||
import { GuildEmoji } from '../..';
|
||||
import { resolveImage } from '../../builders';
|
||||
import type { ImageResolvable } from '../types/resolvables';
|
||||
import type { OmitInsert } from '../types/util';
|
||||
import { BaseShorter } from './base';
|
||||
import { Transformers } from '../../client/transformers';
|
||||
|
||||
export class EmojiShorter extends BaseShorter {
|
||||
/**
|
||||
@ -25,7 +25,7 @@ export class EmojiShorter extends BaseShorter {
|
||||
emojis.map<[string, APIEmoji]>(x => [x.id!, x]),
|
||||
guildId,
|
||||
);
|
||||
return emojis.map(m => new GuildEmoji(this.client, m, guildId));
|
||||
return emojis.map(m => Transformers.GuildEmoji(this.client, m, guildId));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,7 +42,7 @@ export class EmojiShorter extends BaseShorter {
|
||||
|
||||
await this.client.cache.emojis?.setIfNI('GuildEmojisAndStickers', emoji.id!, guildId, emoji);
|
||||
|
||||
return new GuildEmoji(this.client, emoji, guildId);
|
||||
return Transformers.GuildEmoji(this.client, emoji, guildId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,7 +59,7 @@ export class EmojiShorter extends BaseShorter {
|
||||
if (emoji) return emoji;
|
||||
}
|
||||
emoji = await this.client.proxy.guilds(guildId).emojis(emojiId).get();
|
||||
return new GuildEmoji(this.client, emoji, guildId);
|
||||
return Transformers.GuildEmoji(this.client, emoji, guildId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,6 +84,6 @@ export class EmojiShorter extends BaseShorter {
|
||||
async edit(guildId: string, emojiId: string, body: RESTPatchAPIGuildEmojiJSONBody, reason?: string) {
|
||||
const emoji = await this.client.proxy.guilds(guildId).emojis(emojiId).patch({ body, reason });
|
||||
await this.client.cache.emojis?.setIfNI('GuildEmojisAndStickers', emoji.id!, guildId, emoji);
|
||||
return new GuildEmoji(this.client, emoji, guildId);
|
||||
return Transformers.GuildEmoji(this.client, emoji, guildId);
|
||||
}
|
||||
}
|
||||
|
@ -12,17 +12,10 @@ import type {
|
||||
} from 'discord-api-types/v10';
|
||||
import { toSnakeCase, type ObjectToLower } from '..';
|
||||
import { resolveFiles } from '../../builders';
|
||||
import {
|
||||
AnonymousGuild,
|
||||
AutoModerationRule,
|
||||
BaseChannel,
|
||||
Guild,
|
||||
GuildMember,
|
||||
Sticker,
|
||||
type CreateStickerBodyRequest,
|
||||
} from '../../structures';
|
||||
import { BaseChannel, GuildMember, type CreateStickerBodyRequest } from '../../structures';
|
||||
import channelFrom from '../../structures/channels';
|
||||
import { BaseShorter } from './base';
|
||||
import { type GuildStructure, Transformers } from '../../client/transformers';
|
||||
|
||||
export class GuildShorter extends BaseShorter {
|
||||
/**
|
||||
@ -30,10 +23,10 @@ export class GuildShorter extends BaseShorter {
|
||||
* @param body The data for creating the guild.
|
||||
* @returns A Promise that resolves to the created guild.
|
||||
*/
|
||||
async create(body: RESTPostAPIGuildsJSONBody): Promise<Guild<'api'>> {
|
||||
async create(body: RESTPostAPIGuildsJSONBody): Promise<GuildStructure<'api'>> {
|
||||
const guild = await this.client.proxy.guilds.post({ body });
|
||||
await this.client.cache.guilds?.setIfNI('Guilds', guild.id, guild);
|
||||
return new Guild<'api'>(this.client, guild);
|
||||
return Transformers.Guild<'api'>(this.client, guild);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -43,14 +36,18 @@ export class GuildShorter extends BaseShorter {
|
||||
* @returns A Promise that resolves to the fetched guild.
|
||||
*/
|
||||
async fetch(id: string, force = false) {
|
||||
return Transformers.Guild<'api'>(this.client, await this.raw(id, force));
|
||||
}
|
||||
|
||||
async raw(id: string, force = false) {
|
||||
if (!force) {
|
||||
const guild = await this.client.cache.guilds?.get(id);
|
||||
const guild = await this.client.cache.guilds?.raw(id);
|
||||
if (guild) return guild;
|
||||
}
|
||||
|
||||
const data = await this.client.proxy.guilds(id).get();
|
||||
await this.client.cache.guilds?.patch(id, data);
|
||||
return (await this.client.cache.guilds?.get(id)) ?? new Guild<'api'>(this.client, data);
|
||||
return (await this.client.cache.guilds?.raw(id)) ?? data;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,17 +69,17 @@ export class GuildShorter extends BaseShorter {
|
||||
return this.client.proxy
|
||||
.users('@me')
|
||||
.guilds.get({ query })
|
||||
.then(guilds => guilds.map(guild => new AnonymousGuild(this.client, { ...guild, splash: null })));
|
||||
.then(guilds => guilds.map(guild => Transformers.AnonymousGuild(this.client, { ...guild, splash: null })));
|
||||
}
|
||||
|
||||
async fetchSelf(id: string, force = false) {
|
||||
if (!force) {
|
||||
const self = await this.client.cache.members?.get(this.client.botId, id);
|
||||
if (self) return self;
|
||||
const self = await this.client.cache.members?.raw(this.client.botId, id);
|
||||
if (self?.user) return new GuildMember(this.client, self, self.user, id);
|
||||
}
|
||||
const self = await this.client.proxy.guilds(id).members(this.client.botId).get();
|
||||
await this.client.cache.members?.patch(self.user!.id, id, self);
|
||||
return new GuildMember(this.client, self, self.user!, id);
|
||||
await this.client.cache.members?.patch(self.user.id, id, self);
|
||||
return new GuildMember(this.client, self, self.user, id);
|
||||
}
|
||||
|
||||
leave(id: string) {
|
||||
@ -211,7 +208,7 @@ export class GuildShorter extends BaseShorter {
|
||||
this.client.proxy
|
||||
.guilds(guildId)
|
||||
['auto-moderation'].rules.get()
|
||||
.then(rules => rules.map(rule => new AutoModerationRule(this.client, rule))),
|
||||
.then(rules => rules.map(rule => Transformers.AutoModerationRule(this.client, rule))),
|
||||
|
||||
/**
|
||||
* Creates a new auto-moderation rule in the guild.
|
||||
@ -223,7 +220,7 @@ export class GuildShorter extends BaseShorter {
|
||||
this.client.proxy
|
||||
.guilds(guildId)
|
||||
['auto-moderation'].rules.post({ body })
|
||||
.then(rule => new AutoModerationRule(this.client, rule)),
|
||||
.then(rule => Transformers.AutoModerationRule(this.client, rule)),
|
||||
|
||||
/**
|
||||
* Deletes an auto-moderation rule from the guild.
|
||||
@ -247,7 +244,7 @@ export class GuildShorter extends BaseShorter {
|
||||
.guilds(guildId)
|
||||
['auto-moderation'].rules(ruleId)
|
||||
.get()
|
||||
.then(rule => new AutoModerationRule(this.client, rule));
|
||||
.then(rule => Transformers.AutoModerationRule(this.client, rule));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -268,7 +265,7 @@ export class GuildShorter extends BaseShorter {
|
||||
.guilds(guildId)
|
||||
['auto-moderation'].rules(ruleId)
|
||||
.patch({ body: toSnakeCase(body), reason })
|
||||
.then(rule => new AutoModerationRule(this.client, rule));
|
||||
.then(rule => Transformers.AutoModerationRule(this.client, rule));
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -289,7 +286,7 @@ export class GuildShorter extends BaseShorter {
|
||||
stickers.map(st => [st.id, st] as any),
|
||||
guildId,
|
||||
);
|
||||
return stickers.map(st => new Sticker(this.client, st));
|
||||
return stickers.map(st => Transformers.Sticker(this.client, st));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -305,7 +302,7 @@ export class GuildShorter extends BaseShorter {
|
||||
.guilds(guildId)
|
||||
.stickers.post({ reason, body: json, files: [{ ...fileResolve[0], key: 'file' }], appendToFormData: true });
|
||||
await this.client.cache.stickers?.setIfNI('GuildEmojisAndStickers', sticker.id, guildId, sticker);
|
||||
return new Sticker(this.client, sticker);
|
||||
return Transformers.Sticker(this.client, sticker);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -319,7 +316,7 @@ export class GuildShorter extends BaseShorter {
|
||||
edit: async (guildId: string, stickerId: string, body: RESTPatchAPIGuildStickerJSONBody, reason?: string) => {
|
||||
const sticker = await this.client.proxy.guilds(guildId).stickers(stickerId).patch({ body, reason });
|
||||
await this.client.cache.stickers?.setIfNI('GuildEmojisAndStickers', stickerId, guildId, sticker);
|
||||
return new Sticker(this.client, sticker);
|
||||
return Transformers.Sticker(this.client, sticker);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -337,7 +334,7 @@ export class GuildShorter extends BaseShorter {
|
||||
}
|
||||
sticker = await this.client.proxy.guilds(guildId).stickers(stickerId).get();
|
||||
await this.client.cache.stickers?.patch(stickerId, guildId, sticker);
|
||||
return new Sticker(this.client, sticker);
|
||||
return Transformers.Sticker(this.client, sticker);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { BaseInteraction, WebhookMessage, resolveFiles, type ReplyInteractionBody, Modal } from '../..';
|
||||
import { BaseInteraction, resolveFiles, type ReplyInteractionBody, Modal } from '../..';
|
||||
import { Transformers } from '../../client/transformers';
|
||||
import type { InteractionMessageUpdateBodyRequest, MessageWebhookCreateBodyRequest } from '../types/write';
|
||||
import { BaseShorter } from './base';
|
||||
|
||||
@ -42,7 +43,7 @@ export class InteractionShorter extends BaseShorter {
|
||||
body: BaseInteraction.transformBody(data, parsedFiles, this.client),
|
||||
files: parsedFiles,
|
||||
});
|
||||
return new WebhookMessage(this.client, apiMessage, this.client.applicationId, token);
|
||||
return Transformers.WebhookMessage(this.client, apiMessage, this.client.applicationId, token);
|
||||
}
|
||||
|
||||
editOriginal(token: string, body: InteractionMessageUpdateBodyRequest) {
|
||||
@ -69,6 +70,6 @@ export class InteractionShorter extends BaseShorter {
|
||||
body: BaseInteraction.transformBody(body, parsedFiles, this.client),
|
||||
files: parsedFiles,
|
||||
});
|
||||
return new WebhookMessage(this.client, apiMessage, this.client.applicationId, token);
|
||||
return Transformers.WebhookMessage(this.client, apiMessage, this.client.applicationId, token);
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,10 @@ import type {
|
||||
RESTPutAPIGuildBanJSONBody,
|
||||
RESTPutAPIGuildMemberJSONBody,
|
||||
} from 'discord-api-types/v10';
|
||||
import { GuildMember } from '../../structures';
|
||||
import { PermissionsBitField } from '../../structures/extra/Permissions';
|
||||
import type { GuildMemberResolvable } from '../types/resolvables';
|
||||
import { BaseShorter } from './base';
|
||||
import { Transformers } from '../../client/transformers';
|
||||
|
||||
export class MemberShorter extends BaseShorter {
|
||||
/**
|
||||
@ -52,7 +52,7 @@ export class MemberShorter extends BaseShorter {
|
||||
members.map(x => [x.user!.id, x]),
|
||||
guildId,
|
||||
);
|
||||
return members.map(m => new GuildMember(this.client, m, m.user!, guildId));
|
||||
return members.map(m => Transformers.GuildMember(this.client, m, m.user!, guildId));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,7 +100,7 @@ export class MemberShorter extends BaseShorter {
|
||||
async edit(guildId: string, memberId: string, body: RESTPatchAPIGuildMemberJSONBody, reason?: string) {
|
||||
const member = await this.client.proxy.guilds(guildId).members(memberId).patch({ body, reason });
|
||||
await this.client.cache.members?.setIfNI('GuildMembers', memberId, guildId, member);
|
||||
return new GuildMember(this.client, member, member.user!, guildId);
|
||||
return Transformers.GuildMember(this.client, member, member.user!, guildId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -122,7 +122,7 @@ export class MemberShorter extends BaseShorter {
|
||||
|
||||
await this.client.cache.members?.setIfNI('GuildMembers', member.user!.id, guildId, member);
|
||||
|
||||
return new GuildMember(this.client, member, member.user!, guildId);
|
||||
return Transformers.GuildMember(this.client, member, member.user!, guildId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,15 +133,20 @@ export class MemberShorter extends BaseShorter {
|
||||
* @returns A Promise that resolves to the fetched member.
|
||||
*/
|
||||
async fetch(guildId: string, memberId: string, force = false) {
|
||||
const member = await this.raw(guildId, memberId, force);
|
||||
return Transformers.GuildMember(this.client, member, member.user, guildId);
|
||||
}
|
||||
|
||||
async raw(guildId: string, memberId: string, force = false) {
|
||||
let member;
|
||||
if (!force) {
|
||||
member = await this.client.cache.members?.get(memberId, guildId);
|
||||
member = await this.client.cache.members?.raw(memberId, guildId);
|
||||
if (member) return member;
|
||||
}
|
||||
|
||||
member = await this.client.proxy.guilds(guildId).members(memberId).get();
|
||||
await this.client.cache.members?.set(member.user!.id, guildId, member);
|
||||
return new GuildMember(this.client, member, member.user!, guildId);
|
||||
await this.client.cache.members?.set(member.user.id, guildId, member);
|
||||
return member;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -161,7 +166,7 @@ export class MemberShorter extends BaseShorter {
|
||||
query,
|
||||
});
|
||||
await this.client.cache.members?.set(members.map(x => [x.user!.id, x]) as [string, APIGuildMember][], guildId);
|
||||
return members.map(m => new GuildMember(this.client, m, m.user!, guildId));
|
||||
return members.map(m => Transformers.GuildMember(this.client, m, m.user!, guildId));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,11 +4,12 @@ import type {
|
||||
RESTPostAPIChannelMessagesThreadsJSONBody,
|
||||
} from 'discord-api-types/v10';
|
||||
import { resolveFiles } from '../../builders';
|
||||
import { Message, MessagesMethods, User } from '../../structures';
|
||||
import { MessagesMethods } from '../../structures';
|
||||
|
||||
import type { MessageCreateBodyRequest, MessageUpdateBodyRequest } from '../types/write';
|
||||
import { BaseShorter } from './base';
|
||||
import type { ValidAnswerId } from '../../api/Routes/channels';
|
||||
import { Transformers } from '../../client/transformers';
|
||||
|
||||
export class MessageShorter extends BaseShorter {
|
||||
async write(channelId: string, { files, ...body }: MessageCreateBodyRequest) {
|
||||
@ -27,7 +28,7 @@ export class MessageShorter extends BaseShorter {
|
||||
})
|
||||
.then(async message => {
|
||||
await this.client.cache.messages?.setIfNI('GuildMessages', message.id, message.channel_id, message);
|
||||
return new Message(this.client, message);
|
||||
return Transformers.Message(this.client, message);
|
||||
});
|
||||
}
|
||||
|
||||
@ -42,7 +43,7 @@ export class MessageShorter extends BaseShorter {
|
||||
})
|
||||
.then(async message => {
|
||||
await this.client.cache.messages?.setIfNI('GuildMessages', message.id, message.channel_id, message);
|
||||
return new Message(this.client, message);
|
||||
return Transformers.Message(this.client, message);
|
||||
});
|
||||
}
|
||||
|
||||
@ -53,7 +54,7 @@ export class MessageShorter extends BaseShorter {
|
||||
.crosspost.post({ reason })
|
||||
.then(async m => {
|
||||
await this.client.cache.messages?.setIfNI('GuildMessages', m.id, m.channel_id, m);
|
||||
return new Message(this.client, m);
|
||||
return Transformers.Message(this.client, m);
|
||||
});
|
||||
}
|
||||
|
||||
@ -75,7 +76,7 @@ export class MessageShorter extends BaseShorter {
|
||||
.get()
|
||||
.then(async x => {
|
||||
await this.client.cache.messages?.set(x.id, x.channel_id, x);
|
||||
return new Message(this.client, x);
|
||||
return Transformers.Message(this.client, x);
|
||||
});
|
||||
}
|
||||
|
||||
@ -99,7 +100,7 @@ export class MessageShorter extends BaseShorter {
|
||||
.channels(channelId)
|
||||
.polls(messageId)
|
||||
.expire.post()
|
||||
.then(message => new Message(this.client, message));
|
||||
.then(message => Transformers.Message(this.client, message));
|
||||
}
|
||||
|
||||
getAnswerVoters(channelId: string, messageId: string, answerId: ValidAnswerId) {
|
||||
@ -108,6 +109,6 @@ export class MessageShorter extends BaseShorter {
|
||||
.polls(messageId)
|
||||
.answers(answerId)
|
||||
.get()
|
||||
.then(data => data.users.map(user => new User(this.client, user)));
|
||||
.then(data => data.users.map(user => Transformers.User(this.client, user)));
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import type { RESTGetAPIChannelMessageReactionUsersQuery } from 'discord-api-types/v10';
|
||||
import { User } from '../../structures';
|
||||
import { encodeEmoji, resolveEmoji } from '../../structures/extra/functions';
|
||||
import type { EmojiResolvable } from '../types/resolvables';
|
||||
import { BaseShorter } from './base';
|
||||
import { Transformers, type UserStructure } from '../../client/transformers';
|
||||
|
||||
export class ReactionShorter extends BaseShorter {
|
||||
async add(messageId: string, channelId: string, emoji: EmojiResolvable): Promise<void> {
|
||||
@ -30,7 +30,7 @@ export class ReactionShorter extends BaseShorter {
|
||||
channelId: string,
|
||||
emoji: EmojiResolvable,
|
||||
query?: RESTGetAPIChannelMessageReactionUsersQuery,
|
||||
): Promise<User[]> {
|
||||
): Promise<UserStructure[]> {
|
||||
const rawEmoji = await resolveEmoji(emoji, this.client.cache);
|
||||
|
||||
if (!rawEmoji) {
|
||||
@ -42,7 +42,7 @@ export class ReactionShorter extends BaseShorter {
|
||||
.messages(messageId)
|
||||
.reactions(encodeEmoji(rawEmoji))
|
||||
.get({ query })
|
||||
.then(u => u.map(user => new User(this.client, user)));
|
||||
.then(u => u.map(user => Transformers.User(this.client, user)));
|
||||
}
|
||||
|
||||
async purge(messageId: string, channelId: string, emoji?: EmojiResolvable): Promise<void> {
|
||||
|
@ -4,8 +4,8 @@ import type {
|
||||
RESTPatchAPIGuildRolePositionsJSONBody,
|
||||
RESTPostAPIGuildRoleJSONBody,
|
||||
} from 'discord-api-types/v10';
|
||||
import { GuildRole } from '../../structures';
|
||||
import { BaseShorter } from './base';
|
||||
import { Transformers } from '../../client/transformers';
|
||||
|
||||
export class RoleShorter extends BaseShorter {
|
||||
/**
|
||||
@ -18,7 +18,7 @@ export class RoleShorter extends BaseShorter {
|
||||
async create(guildId: string, body: RESTPostAPIGuildRoleJSONBody, reason?: string) {
|
||||
const res = await this.client.proxy.guilds(guildId).roles.post({ body, reason });
|
||||
await this.client.cache.roles?.setIfNI('Guilds', res.id, guildId, res);
|
||||
return new GuildRole(this.client, res, guildId);
|
||||
return Transformers.GuildRole(this.client, res, guildId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -28,9 +28,14 @@ export class RoleShorter extends BaseShorter {
|
||||
* @returns A Promise that resolves to an array of roles.
|
||||
*/
|
||||
async list(guildId: string, force = false) {
|
||||
const roles = await this.listRaw(guildId, force);
|
||||
return roles.map(r => Transformers.GuildRole(this.client, r, guildId));
|
||||
}
|
||||
|
||||
async listRaw(guildId: string, force = false) {
|
||||
let roles: APIRole[] = [];
|
||||
if (!force) {
|
||||
const cachedRoles = (await this.client.cache.roles?.values(guildId)) ?? [];
|
||||
const cachedRoles = (await this.client.cache.roles?.valuesRaw(guildId)) ?? [];
|
||||
if (cachedRoles.length) {
|
||||
return cachedRoles;
|
||||
}
|
||||
@ -40,7 +45,7 @@ export class RoleShorter extends BaseShorter {
|
||||
roles.map<[string, APIRole]>(r => [r.id, r]),
|
||||
guildId,
|
||||
);
|
||||
return roles.map(r => new GuildRole(this.client, r, guildId));
|
||||
return roles;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,7 +59,7 @@ export class RoleShorter extends BaseShorter {
|
||||
async edit(guildId: string, roleId: string, body: RESTPatchAPIGuildRoleJSONBody, reason?: string) {
|
||||
const res = await this.client.proxy.guilds(guildId).roles(roleId).patch({ body, reason });
|
||||
await this.client.cache.roles?.setIfNI('Guilds', roleId, guildId, res);
|
||||
return new GuildRole(this.client, res, guildId);
|
||||
return Transformers.GuildRole(this.client, res, guildId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,7 +72,7 @@ export class RoleShorter extends BaseShorter {
|
||||
async delete(guildId: string, roleId: string, reason?: string) {
|
||||
const res = await this.client.proxy.guilds(guildId).roles(roleId).delete({ reason });
|
||||
this.client.cache.roles?.removeIfNI('Guilds', roleId, guildId);
|
||||
return new GuildRole(this.client, res, guildId);
|
||||
return Transformers.GuildRole(this.client, res, guildId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,6 +91,6 @@ export class RoleShorter extends BaseShorter {
|
||||
guildId,
|
||||
);
|
||||
}
|
||||
return roles.map(x => new GuildRole(this.client, x, guildId));
|
||||
return roles.map(x => Transformers.GuildRole(this.client, x, guildId));
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,27 @@
|
||||
import type { RESTPatchAPIGuildTemplateJSONBody, RESTPostAPIGuildTemplatesJSONBody } from 'discord-api-types/v10';
|
||||
import { BaseShorter } from './base';
|
||||
import { GuildTemplate } from '../..';
|
||||
import { Transformers } from '../../client/transformers';
|
||||
|
||||
export class TemplateShorter extends BaseShorter {
|
||||
fetch(code: string) {
|
||||
return this.client.proxy.guilds
|
||||
.templates(code)
|
||||
.get()
|
||||
.then(template => new GuildTemplate(this.client, template));
|
||||
.then(template => Transformers.GuildTemplate(this.client, template));
|
||||
}
|
||||
|
||||
list(guildId: string) {
|
||||
return this.client.proxy
|
||||
.guilds(guildId)
|
||||
.templates.get()
|
||||
.then(list => list.map(template => new GuildTemplate(this.client, template)));
|
||||
.then(list => list.map(template => Transformers.GuildTemplate(this.client, template)));
|
||||
}
|
||||
|
||||
create(guildId: string, body: RESTPostAPIGuildTemplatesJSONBody) {
|
||||
return this.client.proxy
|
||||
.guilds(guildId)
|
||||
.templates.post({ body })
|
||||
.then(template => new GuildTemplate(this.client, template));
|
||||
.then(template => Transformers.GuildTemplate(this.client, template));
|
||||
}
|
||||
|
||||
sync(guildId: string, code: string) {
|
||||
@ -29,7 +29,7 @@ export class TemplateShorter extends BaseShorter {
|
||||
.guilds(guildId)
|
||||
.templates(code)
|
||||
.put({})
|
||||
.then(template => new GuildTemplate(this.client, template));
|
||||
.then(template => Transformers.GuildTemplate(this.client, template));
|
||||
}
|
||||
|
||||
edit(guildId: string, code: string, body: RESTPatchAPIGuildTemplateJSONBody) {
|
||||
@ -37,7 +37,7 @@ export class TemplateShorter extends BaseShorter {
|
||||
.guilds(guildId)
|
||||
.templates(code)
|
||||
.patch({ body })
|
||||
.then(template => new GuildTemplate(this.client, template));
|
||||
.then(template => Transformers.GuildTemplate(this.client, template));
|
||||
}
|
||||
|
||||
delete(guildId: string, code: string) {
|
||||
@ -45,6 +45,6 @@ export class TemplateShorter extends BaseShorter {
|
||||
.guilds(guildId)
|
||||
.templates(code)
|
||||
.delete()
|
||||
.then(template => new GuildTemplate(this.client, template));
|
||||
.then(template => Transformers.GuildTemplate(this.client, template));
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,10 @@ import type {
|
||||
RESTPostAPIChannelThreadsJSONBody,
|
||||
RESTPostAPIGuildForumThreadsJSONBody,
|
||||
} from 'discord-api-types/v10';
|
||||
import type { ThreadChannel } from '../../structures';
|
||||
import channelFrom from '../../structures/channels';
|
||||
import { BaseShorter } from './base';
|
||||
import type { MakeRequired, When } from '../types/util';
|
||||
import type { ThreadChannelStructure } from '../../client/transformers';
|
||||
|
||||
export class ThreadShorter extends BaseShorter {
|
||||
/**
|
||||
@ -29,7 +29,7 @@ export class ThreadShorter extends BaseShorter {
|
||||
.channels(channelId)
|
||||
.threads.post({ body, reason })
|
||||
// When testing this, discord returns the thread object, but in discord api types it does not.
|
||||
.then(thread => channelFrom(thread, this.client) as ThreadChannel)
|
||||
.then(thread => channelFrom(thread, this.client) as ThreadChannelStructure)
|
||||
);
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ export class ThreadShorter extends BaseShorter {
|
||||
.channels(channelId)
|
||||
.messages(messageId)
|
||||
.threads.post({ body, reason })
|
||||
.then(thread => channelFrom(thread, this.client) as ThreadChannel);
|
||||
.then(thread => channelFrom(thread, this.client) as ThreadChannelStructure);
|
||||
}
|
||||
|
||||
async join(threadId: string) {
|
||||
@ -56,7 +56,7 @@ export class ThreadShorter extends BaseShorter {
|
||||
}
|
||||
|
||||
async lock(threadId: string, locked = true, reason?: string) {
|
||||
return this.edit(threadId, { locked }, reason).then(x => channelFrom(x, this.client) as ThreadChannel);
|
||||
return this.edit(threadId, { locked }, reason).then(x => channelFrom(x, this.client) as ThreadChannelStructure);
|
||||
}
|
||||
|
||||
async edit(threadId: string, body: RESTPatchAPIChannelJSONBody, reason?: string) {
|
||||
@ -98,7 +98,7 @@ export class ThreadShorter extends BaseShorter {
|
||||
const data = await this.client.proxy.channels(channelId).threads.archived[type].get({ query });
|
||||
|
||||
return {
|
||||
threads: data.threads.map(thread => channelFrom(thread, this.client) as ThreadChannel),
|
||||
threads: data.threads.map(thread => channelFrom(thread, this.client) as ThreadChannelStructure),
|
||||
members: data.members as GetAPIChannelThreadMemberResult[],
|
||||
hasMore: data.has_more,
|
||||
};
|
||||
@ -107,7 +107,7 @@ export class ThreadShorter extends BaseShorter {
|
||||
async listJoinedArchivedPrivate(channelId: string, query?: RESTGetAPIChannelThreadsArchivedQuery) {
|
||||
const data = await this.client.proxy.channels(channelId).users('@me').threads.archived.private.get({ query });
|
||||
return {
|
||||
threads: data.threads.map(thread => channelFrom(thread, this.client) as ThreadChannel),
|
||||
threads: data.threads.map(thread => channelFrom(thread, this.client) as ThreadChannelStructure),
|
||||
members: data.members as GetAPIChannelThreadMemberResult[],
|
||||
hasMore: data.has_more,
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { BaseChannel, DMChannel, User } from '../../structures';
|
||||
import { type DMChannelStructure, Transformers } from '../../client/transformers';
|
||||
import { BaseChannel } from '../../structures';
|
||||
import type { MessageCreateBodyRequest } from '../types/write';
|
||||
import { BaseShorter } from './base';
|
||||
|
||||
@ -6,30 +7,34 @@ export class UsersShorter extends BaseShorter {
|
||||
async createDM(userId: string, force = false) {
|
||||
if (!force) {
|
||||
const dm = await this.client.cache.channels?.get(userId);
|
||||
if (dm) return dm as DMChannel;
|
||||
if (dm) return dm as DMChannelStructure;
|
||||
}
|
||||
const data = await this.client.proxy.users('@me').channels.post({
|
||||
body: { recipient_id: userId },
|
||||
});
|
||||
await this.client.cache.channels?.set(userId, '@me', data);
|
||||
return new DMChannel(this.client, data);
|
||||
return Transformers.DMChannel(this.client, data);
|
||||
}
|
||||
|
||||
async deleteDM(userId: string, reason?: string) {
|
||||
const res = await this.client.proxy.channels(userId).delete({ reason });
|
||||
await this.client.cache.channels?.removeIfNI(BaseChannel.__intent__('@me'), res.id, '@me');
|
||||
return new DMChannel(this.client, res);
|
||||
return Transformers.DMChannel(this.client, res);
|
||||
}
|
||||
|
||||
async fetch(userId: string, force = false) {
|
||||
return Transformers.User(this.client, await this.raw(userId, force));
|
||||
}
|
||||
|
||||
async raw(userId: string, force = false) {
|
||||
if (!force) {
|
||||
const user = await this.client.cache.users?.get(userId);
|
||||
const user = await this.client.cache.users?.raw(userId);
|
||||
if (user) return user;
|
||||
}
|
||||
|
||||
const data = await this.client.proxy.users(userId).get();
|
||||
await this.client.cache.users?.patch(userId, data);
|
||||
return new User(this.client, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
async write(userId: string, body: MessageCreateBodyRequest) {
|
||||
|
@ -7,19 +7,18 @@ import type {
|
||||
import { resolveFiles } from '../../builders';
|
||||
import {
|
||||
MessagesMethods,
|
||||
Webhook,
|
||||
WebhookMessage,
|
||||
type MessageWebhookMethodEditParams,
|
||||
type MessageWebhookMethodWriteParams,
|
||||
} from '../../structures';
|
||||
import { BaseShorter } from './base';
|
||||
import { Transformers } from '../../client/transformers';
|
||||
|
||||
export class WebhookShorter extends BaseShorter {
|
||||
async create(channelId: string, body: RESTPostAPIChannelWebhookJSONBody) {
|
||||
const webhook = await this.client.proxy.channels(channelId).webhooks.post({
|
||||
body,
|
||||
});
|
||||
return new Webhook(this.client, webhook);
|
||||
return Transformers.Webhook(this.client, webhook);
|
||||
}
|
||||
/**
|
||||
* Deletes a webhook.
|
||||
@ -50,12 +49,12 @@ export class WebhookShorter extends BaseShorter {
|
||||
return this.client.proxy
|
||||
.webhooks(webhookId)(options.token)
|
||||
.patch({ body, reason: options.reason, auth: false })
|
||||
.then(webhook => new Webhook(this.client, webhook));
|
||||
.then(webhook => Transformers.Webhook(this.client, webhook));
|
||||
}
|
||||
return this.client.proxy
|
||||
.webhooks(webhookId)
|
||||
.patch({ body, reason: options.reason })
|
||||
.then(webhook => new Webhook(this.client, webhook));
|
||||
.then(webhook => Transformers.Webhook(this.client, webhook));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,7 +70,7 @@ export class WebhookShorter extends BaseShorter {
|
||||
} else {
|
||||
webhook = await this.client.proxy.webhooks(webhookId).get();
|
||||
}
|
||||
return new Webhook(this.client, webhook);
|
||||
return Transformers.Webhook(this.client, webhook);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -92,7 +91,7 @@ export class WebhookShorter extends BaseShorter {
|
||||
return this.client.proxy
|
||||
.webhooks(webhookId)(token)
|
||||
.post({ ...payload, files: parsedFiles, body: transformedBody })
|
||||
.then(m => (m?.id ? new WebhookMessage(this.client, m, webhookId, token) : null));
|
||||
.then(m => (m?.id ? Transformers.WebhookMessage(this.client, m, webhookId, token) : null));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,7 +118,7 @@ export class WebhookShorter extends BaseShorter {
|
||||
.webhooks(webhookId)(token)
|
||||
.messages(messageId)
|
||||
.patch({ ...json, auth: false, files: parsedFiles, body: transformedBody })
|
||||
.then(m => new WebhookMessage(this.client, m, webhookId, token));
|
||||
.then(m => Transformers.WebhookMessage(this.client, m, webhookId, token));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -147,17 +146,17 @@ export class WebhookShorter extends BaseShorter {
|
||||
.webhooks(webhookId)(token)
|
||||
.messages(messageId)
|
||||
.get({ auth: false, query: { threadId } });
|
||||
return message ? new WebhookMessage(this.client, message, webhookId, token) : undefined;
|
||||
return message ? Transformers.WebhookMessage(this.client, message, webhookId, token) : undefined;
|
||||
}
|
||||
|
||||
async listFromGuild(guildId: string) {
|
||||
const webhooks = await this.client.proxy.guilds(guildId).webhooks.get();
|
||||
return webhooks.map(webhook => new Webhook(this.client, webhook));
|
||||
return webhooks.map(webhook => Transformers.Webhook(this.client, webhook));
|
||||
}
|
||||
|
||||
async listFromChannel(channelId: string) {
|
||||
const webhooks = await this.client.proxy.channels(channelId).webhooks.get();
|
||||
return webhooks.map(webhook => new Webhook(this.client, webhook));
|
||||
return webhooks.map(webhook => Transformers.Webhook(this.client, webhook));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,10 @@ export type IsStrictlyUndefined<T> = AuxIsStrictlyUndefined<T> extends true
|
||||
|
||||
export type If<T extends boolean, A, B = null> = T extends true ? A : B extends null ? A | null : B;
|
||||
|
||||
export type NulleableCoalising<A, B> = NonFalsy<A> extends never ? B : A;
|
||||
|
||||
export type TupleOr<A, T> = ValueOf<A> extends never ? A : TupleOr<ArrayFirsElement<T>, Tail<T>>;
|
||||
|
||||
export type PickPartial<T, K extends keyof T> = {
|
||||
[P in keyof T]?: T[P] | undefined;
|
||||
} & {
|
||||
|
@ -1,14 +1,20 @@
|
||||
import type { ButtonStyle, ComponentType } from 'discord-api-types/v10';
|
||||
import { Button, type ButtonStylesForID } from '../builders';
|
||||
import type {
|
||||
APIButtonComponentWithCustomId,
|
||||
APIButtonComponentWithSKUId,
|
||||
APIButtonComponentWithURL,
|
||||
ButtonStyle,
|
||||
ComponentType,
|
||||
} from 'discord-api-types/v10';
|
||||
import { Button } from '../builders';
|
||||
import { BaseComponent } from './BaseComponent';
|
||||
|
||||
export class LinkButtonComponent extends BaseComponent<ComponentType.Button> {
|
||||
declare data: APIButtonComponentWithURL;
|
||||
get style() {
|
||||
return this.data.style as ButtonStyle.Link;
|
||||
return this.data.style;
|
||||
}
|
||||
|
||||
get url(): string {
|
||||
// @ts-expect-error
|
||||
return this.data.url;
|
||||
}
|
||||
|
||||
@ -25,15 +31,20 @@ export class LinkButtonComponent extends BaseComponent<ComponentType.Button> {
|
||||
}
|
||||
|
||||
toBuilder() {
|
||||
return new Button<false>(this.data as never);
|
||||
return new Button(this.data);
|
||||
}
|
||||
}
|
||||
|
||||
export type ButtonStyleExludeLink = Exclude<ButtonStyle, ButtonStyle.Link>;
|
||||
|
||||
export class ButtonComponent extends BaseComponent<ComponentType.Button> {
|
||||
declare data: APIButtonComponentWithCustomId;
|
||||
get style() {
|
||||
return this.data.style as ButtonStylesForID;
|
||||
return this.data.style;
|
||||
}
|
||||
|
||||
get customId() {
|
||||
return this.data.custom_id;
|
||||
}
|
||||
|
||||
get label() {
|
||||
@ -49,6 +60,25 @@ export class ButtonComponent extends BaseComponent<ComponentType.Button> {
|
||||
}
|
||||
|
||||
toBuilder() {
|
||||
return new Button<true>(this.data as never);
|
||||
return new Button(this.data);
|
||||
}
|
||||
}
|
||||
|
||||
export class SKUButtonComponent extends BaseComponent<ComponentType.Button> {
|
||||
declare data: APIButtonComponentWithSKUId;
|
||||
get style() {
|
||||
return this.data.style;
|
||||
}
|
||||
|
||||
get skuId() {
|
||||
return this.data.sku_id;
|
||||
}
|
||||
|
||||
get disabled() {
|
||||
return this.data.disabled;
|
||||
}
|
||||
|
||||
toBuilder() {
|
||||
return new Button(this.data as never);
|
||||
}
|
||||
}
|
||||
|
@ -4,15 +4,11 @@ import type {
|
||||
ButtonInteraction,
|
||||
ChannelSelectMenuInteraction,
|
||||
ComponentCommand,
|
||||
Guild,
|
||||
GuildMember,
|
||||
MentionableSelectMenuInteraction,
|
||||
Message,
|
||||
ReturnCache,
|
||||
RoleSelectMenuInteraction,
|
||||
StringSelectMenuInteraction,
|
||||
UserSelectMenuInteraction,
|
||||
WebhookMessage,
|
||||
} from '..';
|
||||
import type { CommandMetadata, ExtendContext, GlobalMetadata, RegisteredMiddlewares, UsingClient } from '../commands';
|
||||
import { BaseContext } from '../commands/basecontext';
|
||||
@ -24,6 +20,12 @@ import type {
|
||||
UnionToTuple,
|
||||
When,
|
||||
} from '../common';
|
||||
import type {
|
||||
GuildMemberStructure,
|
||||
GuildStructure,
|
||||
MessageStructure,
|
||||
WebhookMessageStructure,
|
||||
} from '../client/transformers';
|
||||
|
||||
export interface ComponentContext<
|
||||
Type extends keyof ContextComponentCommandInteractionMap = keyof ContextComponentCommandInteractionMap,
|
||||
@ -109,7 +111,7 @@ export class ComponentContext<
|
||||
editOrReply<FR extends boolean = false>(
|
||||
body: InteractionCreateBodyRequest | InteractionMessageUpdateBodyRequest,
|
||||
fetchReply?: FR,
|
||||
): Promise<When<FR, WebhookMessage | Message, void | WebhookMessage | Message>> {
|
||||
): Promise<When<FR, WebhookMessageStructure | MessageStructure, void | WebhookMessageStructure | MessageStructure>> {
|
||||
return this.interaction.editOrReply(body as InteractionCreateBodyRequest, fetchReply);
|
||||
}
|
||||
|
||||
@ -143,8 +145,8 @@ export class ComponentContext<
|
||||
* @param mode - The mode to fetch the member.
|
||||
* @returns A promise that resolves to the bot member.
|
||||
*/
|
||||
me(mode?: 'rest' | 'flow'): Promise<GuildMember>;
|
||||
me(mode?: 'cache'): ReturnCache<GuildMember | undefined>;
|
||||
me(mode?: 'rest' | 'flow'): Promise<GuildMemberStructure>;
|
||||
me(mode?: 'cache'): ReturnCache<GuildMemberStructure | undefined>;
|
||||
me(mode: 'cache' | 'rest' | 'flow' = 'cache') {
|
||||
if (!this.guildId)
|
||||
return mode === 'cache' ? (this.client.cache.adapter.isAsync ? Promise.resolve() : undefined) : Promise.resolve();
|
||||
@ -161,8 +163,8 @@ export class ComponentContext<
|
||||
* @param mode - The mode to fetch the guild.
|
||||
* @returns A promise that resolves to the guild.
|
||||
*/
|
||||
guild(mode?: 'rest' | 'flow'): Promise<Guild<'cached' | 'api'> | undefined>;
|
||||
guild(mode?: 'cache'): ReturnCache<Guild<'cached'> | undefined>;
|
||||
guild(mode?: 'rest' | 'flow'): Promise<GuildStructure<'cached' | 'api'> | undefined>;
|
||||
guild(mode?: 'cache'): ReturnCache<GuildStructure<'cached'> | undefined>;
|
||||
guild(mode: 'cache' | 'rest' | 'flow' = 'cache') {
|
||||
if (!this.guildId)
|
||||
return (
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type { ComponentCallback, ListenerOptions, ModalSubmitCallback } from '../builders/types';
|
||||
import { LimitedCollection } from '../collection';
|
||||
import { BaseCommand, type RegisteredMiddlewares, type UsingClient } from '../commands';
|
||||
import type { FileLoaded } from '../commands/handler';
|
||||
import { BaseHandler, magicImport, type Logger, type OnFailCallback } from '../common';
|
||||
import type { ComponentInteraction, ModalSubmitInteraction, StringSelectMenuInteraction } from '../structures';
|
||||
import { ComponentCommand, InteractionCommandType } from './componentcommand';
|
||||
@ -17,12 +18,15 @@ type COMPONENTS = {
|
||||
__run: (customId: string | string[] | RegExp, callback: ComponentCallback) => any;
|
||||
};
|
||||
|
||||
export type CollectorInteraction = ComponentInteraction | StringSelectMenuInteraction;
|
||||
export type ComponentCommands = ComponentCommand | ModalCommand;
|
||||
|
||||
export class ComponentHandler extends BaseHandler {
|
||||
onFail: OnFailCallback = err => this.logger.warn('<Client>.components.onFail', err);
|
||||
readonly values = new Map<string, COMPONENTS>();
|
||||
// 10 minutes timeout, because discord dont send an event when the user cancel the modal
|
||||
readonly modals = new LimitedCollection<string, ModalSubmitCallback>({ expire: 60e3 * 10 });
|
||||
readonly commands: (ComponentCommand | ModalCommand)[] = [];
|
||||
readonly commands: ComponentCommands[] = [];
|
||||
protected filter = (path: string) => path.endsWith('.js') || (!path.endsWith('.d.ts') && path.endsWith('.ts'));
|
||||
|
||||
constructor(
|
||||
@ -36,9 +40,10 @@ export class ComponentHandler extends BaseHandler {
|
||||
messageId: string,
|
||||
options: ListenerOptions = {},
|
||||
): {
|
||||
run<
|
||||
T extends ComponentInteraction | StringSelectMenuInteraction = ComponentInteraction | StringSelectMenuInteraction,
|
||||
>(customId: string | string[] | RegExp, callback: ComponentCallback<T>): any;
|
||||
run<T extends CollectorInteraction = CollectorInteraction>(
|
||||
customId: string | string[] | RegExp,
|
||||
callback: ComponentCallback<T>,
|
||||
): any;
|
||||
stop(reason?: string): any;
|
||||
} {
|
||||
this.values.set(messageId, {
|
||||
@ -149,22 +154,29 @@ export class ComponentHandler extends BaseHandler {
|
||||
this.deleteValue(id, 'messageDelete');
|
||||
}
|
||||
|
||||
async load(componentsDir: string, instances?: { new (): ModalCommand | ComponentCommand }[]) {
|
||||
const paths =
|
||||
instances?.map(x => {
|
||||
const i = new x();
|
||||
return { file: x, path: i.__filePath ?? '*' };
|
||||
}) ?? (await this.loadFilesK<{ new (): ModalCommand | ComponentCommand }>(await this.getFiles(componentsDir)));
|
||||
stablishDefaults(component: ComponentCommands) {
|
||||
component.props ??= this.client.options.commands?.defaults?.props ?? {};
|
||||
const is = component instanceof ModalCommand ? 'modals' : 'components';
|
||||
component.onInternalError ??= this.client.options?.[is]?.defaults?.onInternalError;
|
||||
component.onMiddlewaresError ??= this.client.options?.[is]?.defaults?.onMiddlewaresError;
|
||||
component.onRunError ??= this.client.options?.[is]?.defaults?.onRunError;
|
||||
component.onAfterRun ??= this.client.options?.[is]?.defaults?.onAfterRun;
|
||||
}
|
||||
|
||||
for (const value of paths) {
|
||||
async load(componentsDir: string) {
|
||||
const paths = await this.loadFilesK<FileLoaded<new () => ComponentCommands>>(await this.getFiles(componentsDir));
|
||||
|
||||
for (const { components, file } of paths.map(x => ({ components: this.onFile(x.file), file: x }))) {
|
||||
if (!components) continue;
|
||||
for (const value of components) {
|
||||
let component;
|
||||
try {
|
||||
component = this.callback(value.file);
|
||||
component = this.callback(value);
|
||||
if (!component) continue;
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message.includes('is not a constructor')) {
|
||||
this.logger.warn(
|
||||
`${value.path
|
||||
`${file.path
|
||||
.split(process.cwd())
|
||||
.slice(1)
|
||||
.join(process.cwd())} doesn't export the class by \`export default <ComponentCommand>\``,
|
||||
@ -173,16 +185,12 @@ export class ComponentHandler extends BaseHandler {
|
||||
continue;
|
||||
}
|
||||
if (!(component instanceof ModalCommand || component instanceof ComponentCommand)) continue;
|
||||
component.props ??= this.client.options.commands?.defaults?.props ?? {};
|
||||
const is = component instanceof ModalCommand ? 'modals' : 'components';
|
||||
component.onInternalError ??= this.client.options?.[is]?.defaults?.onInternalError;
|
||||
component.onMiddlewaresError ??= this.client.options?.[is]?.defaults?.onMiddlewaresError;
|
||||
component.onRunError ??= this.client.options?.[is]?.defaults?.onRunError;
|
||||
component.onAfterRun ??= this.client.options?.[is]?.defaults?.onAfterRun;
|
||||
component.__filePath = value.path;
|
||||
this.stablishDefaults(component);
|
||||
component.__filePath = file.path;
|
||||
this.commands.push(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async reload(path: string) {
|
||||
if (!this.client.components) return;
|
||||
@ -217,15 +225,7 @@ export class ComponentHandler extends BaseHandler {
|
||||
}
|
||||
}
|
||||
|
||||
async executeComponent(context: ComponentContext) {
|
||||
for (const i of this.commands) {
|
||||
try {
|
||||
if (
|
||||
i.type === InteractionCommandType.COMPONENT &&
|
||||
i.cType === context.interaction.componentType &&
|
||||
(await i.filter(context))
|
||||
) {
|
||||
context.command = i;
|
||||
async execute(i: ComponentCommands, context: ComponentContext | ModalContext) {
|
||||
try {
|
||||
const resultRunGlobalMiddlewares = await BaseCommand.__runMiddlewares(
|
||||
context,
|
||||
@ -236,7 +236,7 @@ export class ComponentHandler extends BaseHandler {
|
||||
return;
|
||||
}
|
||||
if ('error' in resultRunGlobalMiddlewares) {
|
||||
return i.onMiddlewaresError?.(context, resultRunGlobalMiddlewares.error ?? 'Unknown error');
|
||||
return i.onMiddlewaresError?.(context as never, resultRunGlobalMiddlewares.error ?? 'Unknown error');
|
||||
}
|
||||
|
||||
const resultRunMiddlewares = await BaseCommand.__runMiddlewares(context, i.middlewares, false);
|
||||
@ -244,24 +244,36 @@ export class ComponentHandler extends BaseHandler {
|
||||
return;
|
||||
}
|
||||
if ('error' in resultRunMiddlewares) {
|
||||
return i.onMiddlewaresError?.(context, resultRunMiddlewares.error ?? 'Unknown error');
|
||||
return i.onMiddlewaresError?.(context as never, resultRunMiddlewares.error ?? 'Unknown error');
|
||||
}
|
||||
|
||||
try {
|
||||
await i.run(context);
|
||||
await i.onAfterRun?.(context, undefined);
|
||||
await i.run(context as never);
|
||||
await i.onAfterRun?.(context as never, undefined);
|
||||
} catch (error) {
|
||||
await i.onRunError?.(context, error);
|
||||
await i.onAfterRun?.(context, error);
|
||||
await i.onRunError?.(context as never, error);
|
||||
await i.onAfterRun?.(context as never, error);
|
||||
}
|
||||
} catch (error) {
|
||||
try {
|
||||
await i.onInternalError?.(this.client, error);
|
||||
} catch {
|
||||
} catch (e) {
|
||||
// supress error
|
||||
this.logger.error(e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
async executeComponent(context: ComponentContext) {
|
||||
for (const i of this.commands) {
|
||||
try {
|
||||
if (
|
||||
i.type === InteractionCommandType.COMPONENT &&
|
||||
i.cType === context.interaction.componentType &&
|
||||
(await i.filter(context))
|
||||
) {
|
||||
context.command = i;
|
||||
await this.execute(i, context);
|
||||
}
|
||||
} catch (e) {
|
||||
await this.onFail(e);
|
||||
@ -274,42 +286,7 @@ export class ComponentHandler extends BaseHandler {
|
||||
try {
|
||||
if (i.type === InteractionCommandType.MODAL && (await i.filter(context))) {
|
||||
context.command = i;
|
||||
try {
|
||||
const resultRunGlobalMiddlewares = await BaseCommand.__runMiddlewares(
|
||||
context,
|
||||
(context.client.options?.globalMiddlewares ?? []) as keyof RegisteredMiddlewares,
|
||||
true,
|
||||
);
|
||||
if (resultRunGlobalMiddlewares.pass) {
|
||||
return;
|
||||
}
|
||||
if ('error' in resultRunGlobalMiddlewares) {
|
||||
return i.onMiddlewaresError?.(context, resultRunGlobalMiddlewares.error ?? 'Unknown error');
|
||||
}
|
||||
|
||||
const resultRunMiddlewares = await BaseCommand.__runMiddlewares(context, i.middlewares, false);
|
||||
if (resultRunMiddlewares.pass) {
|
||||
return;
|
||||
}
|
||||
if ('error' in resultRunMiddlewares) {
|
||||
return i.onMiddlewaresError?.(context, resultRunMiddlewares.error ?? 'Unknown error');
|
||||
}
|
||||
|
||||
try {
|
||||
await i.run(context);
|
||||
await i.onAfterRun?.(context, undefined);
|
||||
} catch (error) {
|
||||
await i.onRunError?.(context, error);
|
||||
await i.onAfterRun?.(context, error);
|
||||
}
|
||||
} catch (error) {
|
||||
try {
|
||||
await i.onInternalError?.(this.client, error);
|
||||
} catch {
|
||||
// supress error
|
||||
}
|
||||
}
|
||||
break;
|
||||
await this.execute(i, context);
|
||||
}
|
||||
} catch (e) {
|
||||
await this.onFail(e);
|
||||
@ -317,9 +294,11 @@ export class ComponentHandler extends BaseHandler {
|
||||
}
|
||||
}
|
||||
|
||||
setHandlers({ callback }: { callback: ComponentHandler['callback'] }) {
|
||||
this.callback = callback;
|
||||
onFile(file: FileLoaded<new () => ComponentCommands>): (new () => ComponentCommands)[] | undefined {
|
||||
return file.default ? [file.default] : undefined;
|
||||
}
|
||||
|
||||
callback = (file: { new (): ModalCommand | ComponentCommand }): ModalCommand | ComponentCommand | false => new file();
|
||||
callback(file: { new (): ComponentCommands }): ComponentCommands | false {
|
||||
return new file();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { type APIMessageActionRowComponent, ButtonStyle, ComponentType } from 'discord-api-types/v10';
|
||||
import { BaseComponent } from './BaseComponent';
|
||||
import { ButtonComponent, LinkButtonComponent } from './ButtonComponent';
|
||||
import { ButtonComponent, LinkButtonComponent, SKUButtonComponent } from './ButtonComponent';
|
||||
import { ChannelSelectMenuComponent } from './ChannelSelectMenuComponent';
|
||||
import { MentionableSelectMenuComponent } from './MentionableSelectMenuComponent';
|
||||
import { RoleSelectMenuComponent } from './RoleSelectMenuComponent';
|
||||
@ -11,6 +11,7 @@ import { UserSelectMenuComponent } from './UserSelectMenuComponent';
|
||||
export type MessageComponents =
|
||||
| ButtonComponent
|
||||
| LinkButtonComponent
|
||||
| SKUButtonComponent
|
||||
| RoleSelectMenuComponent
|
||||
| UserSelectMenuComponent
|
||||
| StringSelectMenuComponent
|
||||
@ -39,6 +40,9 @@ export function componentFactory(
|
||||
if (component.style === ButtonStyle.Link) {
|
||||
return new LinkButtonComponent(component);
|
||||
}
|
||||
if (component.style === ButtonStyle.Premium) {
|
||||
return new SKUButtonComponent(component);
|
||||
}
|
||||
return new ButtonComponent(component);
|
||||
case ComponentType.ChannelSelect:
|
||||
return new ChannelSelectMenuComponent(component);
|
||||
|
@ -1,14 +1,5 @@
|
||||
import { MessageFlags } from 'discord-api-types/v10';
|
||||
import type {
|
||||
AllChannels,
|
||||
Guild,
|
||||
GuildMember,
|
||||
Message,
|
||||
ModalCommand,
|
||||
ModalSubmitInteraction,
|
||||
ReturnCache,
|
||||
WebhookMessage,
|
||||
} from '..';
|
||||
import type { AllChannels, ModalCommand, ModalSubmitInteraction, ReturnCache } from '..';
|
||||
import type { CommandMetadata, ExtendContext, GlobalMetadata, RegisteredMiddlewares, UsingClient } from '../commands';
|
||||
import { BaseContext } from '../commands/basecontext';
|
||||
import type {
|
||||
@ -18,6 +9,12 @@ import type {
|
||||
UnionToTuple,
|
||||
When,
|
||||
} from '../common';
|
||||
import type {
|
||||
GuildMemberStructure,
|
||||
GuildStructure,
|
||||
MessageStructure,
|
||||
WebhookMessageStructure,
|
||||
} from '../client/transformers';
|
||||
|
||||
export interface ModalContext extends BaseContext, ExtendContext {}
|
||||
|
||||
@ -90,7 +87,7 @@ export class ModalContext<M extends keyof RegisteredMiddlewares = never> extends
|
||||
editOrReply<FR extends boolean = false>(
|
||||
body: InteractionCreateBodyRequest | InteractionMessageUpdateBodyRequest,
|
||||
fetchReply?: FR,
|
||||
): Promise<When<FR, WebhookMessage | Message, void | WebhookMessage | Message>> {
|
||||
): Promise<When<FR, WebhookMessageStructure | MessageStructure, void | WebhookMessageStructure | MessageStructure>> {
|
||||
return this.interaction.editOrReply(body as InteractionCreateBodyRequest, fetchReply);
|
||||
}
|
||||
|
||||
@ -125,8 +122,8 @@ export class ModalContext<M extends keyof RegisteredMiddlewares = never> extends
|
||||
* @param mode - The mode to fetch the member.
|
||||
* @returns A promise that resolves to the bot member.
|
||||
*/
|
||||
me(mode?: 'rest' | 'flow'): Promise<GuildMember>;
|
||||
me(mode?: 'cache'): ReturnCache<GuildMember | undefined>;
|
||||
me(mode?: 'rest' | 'flow'): Promise<GuildMemberStructure>;
|
||||
me(mode?: 'cache'): ReturnCache<GuildMemberStructure | undefined>;
|
||||
me(mode: 'cache' | 'rest' | 'flow' = 'cache') {
|
||||
if (!this.guildId)
|
||||
return mode === 'cache' ? (this.client.cache.adapter.isAsync ? Promise.resolve() : undefined) : Promise.resolve();
|
||||
@ -143,8 +140,8 @@ export class ModalContext<M extends keyof RegisteredMiddlewares = never> extends
|
||||
* @param mode - The mode to fetch the guild.
|
||||
* @returns A promise that resolves to the guild.
|
||||
*/
|
||||
guild(mode?: 'rest' | 'flow'): Promise<Guild<'cached' | 'api'> | undefined>;
|
||||
guild(mode?: 'cache'): ReturnCache<Guild<'cached'> | undefined>;
|
||||
guild(mode?: 'rest' | 'flow'): Promise<GuildStructure<'cached' | 'api'> | undefined>;
|
||||
guild(mode?: 'cache'): ReturnCache<GuildStructure<'cached'> | undefined>;
|
||||
guild(mode: 'cache' | 'rest' | 'flow' = 'cache') {
|
||||
if (!this.guildId)
|
||||
return (
|
||||
|
@ -9,6 +9,7 @@ import { BaseHandler, ReplaceRegex, magicImport, type MakeRequired, type SnakeCa
|
||||
import type { ClientEvents } from '../events/hooks';
|
||||
import * as RawEvents from '../events/hooks';
|
||||
import type { ClientEvent, CustomEvents, CustomEventsKeys, ClientNameEvents } from './event';
|
||||
import type { FileLoaded } from '../commands/handler';
|
||||
|
||||
export type EventValue = MakeRequired<ClientEvent, '__filePath'> & { fired?: boolean };
|
||||
|
||||
@ -25,20 +26,23 @@ export class EventHandler extends BaseHandler {
|
||||
|
||||
values: Partial<Record<GatewayEvents | CustomEventsKeys, EventValue>> = {};
|
||||
|
||||
async load(eventsDir: string, instances?: { file: ClientEvent; path: string }[]) {
|
||||
async load(eventsDir: string) {
|
||||
const discordEvents = Object.keys(RawEvents).map(x => ReplaceRegex.camel(x.toLowerCase())) as ClientNameEvents[];
|
||||
const paths = await this.loadFilesK<{ file: ClientEvent }>(await this.getFiles(eventsDir));
|
||||
|
||||
for (const i of instances ?? (await this.loadFilesK<ClientEvent>(await this.getFiles(eventsDir)))) {
|
||||
const instance = this.callback(i.file);
|
||||
for (const { events, file } of paths.map(x => ({ events: this.onFile(x.file), file: x }))) {
|
||||
if (!events) continue;
|
||||
for (const i of events) {
|
||||
const instance = this.callback(i);
|
||||
if (!instance) continue;
|
||||
if (typeof instance?.run !== 'function') {
|
||||
this.logger.warn(
|
||||
i.path.split(process.cwd()).slice(1).join(process.cwd()),
|
||||
file.path.split(process.cwd()).slice(1).join(process.cwd()),
|
||||
'Missing run function, use `export default {...}` syntax',
|
||||
);
|
||||
continue;
|
||||
}
|
||||
instance.__filePath = i.path;
|
||||
instance.__filePath = file.path;
|
||||
this.values[
|
||||
discordEvents.includes(instance.data.name)
|
||||
? (ReplaceRegex.snake(instance.data.name).toUpperCase() as GatewayEvents)
|
||||
@ -46,6 +50,7 @@ export class EventHandler extends BaseHandler {
|
||||
] = instance as EventValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async execute(name: GatewayEvents, ...args: [GatewayDispatchPayload, Client<true> | WorkerClient<true>, number]) {
|
||||
switch (name) {
|
||||
@ -53,7 +58,7 @@ export class EventHandler extends BaseHandler {
|
||||
{
|
||||
const { d: data } = args[0] as GatewayMessageCreateDispatch;
|
||||
if (args[1].components?.values.has(data.interaction_metadata?.id ?? data.id)) {
|
||||
args[1].components.values.get(data.interaction_metadata?.id ?? data.id)!.messageId = data.id;
|
||||
args[1].components.values.get(data.interaction_metadata!.id ?? data.id)!.messageId = data.id;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -81,29 +86,33 @@ export class EventHandler extends BaseHandler {
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
this.runEvent(args[0].t, args[1], args[0].d, args[2]),
|
||||
this.client.collectors.run(args[0].t, args[0].d),
|
||||
this.runEvent(args[0].t as never, args[1], args[0].d, args[2]),
|
||||
this.client.collectors.run(args[0].t as never, args[0].d as never),
|
||||
]);
|
||||
}
|
||||
|
||||
async runEvent(name: GatewayEvents, client: Client | WorkerClient, packet: any, shardId: number) {
|
||||
async runEvent(name: GatewayEvents, client: Client | WorkerClient, packet: any, shardId: number, runCache = true) {
|
||||
const Event = this.values[name];
|
||||
if (!Event) {
|
||||
return this.client.cache.onPacket({
|
||||
return runCache
|
||||
? this.client.cache.onPacket({
|
||||
t: name,
|
||||
d: packet,
|
||||
} as GatewayDispatchPayload);
|
||||
} as GatewayDispatchPayload)
|
||||
: undefined;
|
||||
}
|
||||
try {
|
||||
if (Event.data.once && Event.fired) {
|
||||
return this.client.cache.onPacket({
|
||||
return runCache
|
||||
? this.client.cache.onPacket({
|
||||
t: name,
|
||||
d: packet,
|
||||
} as GatewayDispatchPayload);
|
||||
} as GatewayDispatchPayload)
|
||||
: undefined;
|
||||
}
|
||||
Event.fired = true;
|
||||
const hook = await RawEvents[name]?.(client, packet as never);
|
||||
if (name !== 'RAW')
|
||||
if (runCache)
|
||||
await this.client.cache.onPacket({
|
||||
t: name,
|
||||
d: packet,
|
||||
@ -153,8 +162,8 @@ export class EventHandler extends BaseHandler {
|
||||
}
|
||||
}
|
||||
|
||||
setHandlers({ callback }: { callback: EventHandler['callback'] }) {
|
||||
this.callback = callback;
|
||||
onFile(file: FileLoaded<ClientEvent>): ClientEvent[] | undefined {
|
||||
return file.default ? [file.default] : undefined;
|
||||
}
|
||||
|
||||
callback = (file: ClientEvent): ClientEvent | false => file;
|
||||
|
@ -5,8 +5,8 @@ import type {
|
||||
GatewayAutoModerationRuleUpdateDispatchData,
|
||||
} from 'discord-api-types/v10';
|
||||
import { toCamelCase } from '../../common';
|
||||
import { AutoModerationRule } from '../../structures';
|
||||
import type { UsingClient } from '../../commands';
|
||||
import { Transformers } from '../../client/transformers';
|
||||
|
||||
export const AUTO_MODERATION_ACTION_EXECUTION = (
|
||||
_self: UsingClient,
|
||||
@ -16,13 +16,13 @@ export const AUTO_MODERATION_ACTION_EXECUTION = (
|
||||
};
|
||||
|
||||
export const AUTO_MODERATION_RULE_CREATE = (self: UsingClient, data: GatewayAutoModerationRuleCreateDispatchData) => {
|
||||
return new AutoModerationRule(self, data);
|
||||
return Transformers.AutoModerationRule(self, data);
|
||||
};
|
||||
|
||||
export const AUTO_MODERATION_RULE_DELETE = (self: UsingClient, data: GatewayAutoModerationRuleDeleteDispatchData) => {
|
||||
return new AutoModerationRule(self, data);
|
||||
return Transformers.AutoModerationRule(self, data);
|
||||
};
|
||||
|
||||
export const AUTO_MODERATION_RULE_UPDATE = (self: UsingClient, data: GatewayAutoModerationRuleUpdateDispatchData) => {
|
||||
return new AutoModerationRule(self, data);
|
||||
return Transformers.AutoModerationRule(self, data);
|
||||
};
|
||||
|
@ -1,10 +1,10 @@
|
||||
import type { ClientUserStructure } from '../../client/transformers';
|
||||
import type { UsingClient } from '../../commands';
|
||||
import type { ClientUser } from '../../structures';
|
||||
|
||||
export const BOT_READY = (_self: UsingClient, me: ClientUser) => {
|
||||
export const BOT_READY = (_self: UsingClient, me: ClientUserStructure) => {
|
||||
return me;
|
||||
};
|
||||
|
||||
export const WORKER_READY = (_self: UsingClient, me: ClientUser) => {
|
||||
export const WORKER_READY = (_self: UsingClient, me: ClientUserStructure) => {
|
||||
return me;
|
||||
};
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type { GatewayDispatchPayload, GatewayReadyDispatchData, GatewayResumedDispatch } from 'discord-api-types/v10';
|
||||
import { ClientUser } from '../../structures';
|
||||
import type { UsingClient } from '../../commands';
|
||||
import { Transformers } from '../../client/transformers';
|
||||
|
||||
export const READY = (self: UsingClient, data: GatewayReadyDispatchData) => {
|
||||
return new ClientUser(self, data.user, data.application);
|
||||
return Transformers.ClientUser(self, data.user, data.application);
|
||||
};
|
||||
|
||||
export const RESUMED = (_self: UsingClient, _data: GatewayResumedDispatch['d']) => {
|
||||
|
@ -22,23 +22,28 @@ import type {
|
||||
GatewayGuildUpdateDispatchData,
|
||||
} from 'discord-api-types/v10';
|
||||
import { toCamelCase } from '../../common';
|
||||
import { Guild, GuildEmoji, GuildMember, GuildRole, Sticker, User } from '../../structures';
|
||||
import type { UsingClient } from '../../commands';
|
||||
import {
|
||||
type GuildMemberStructure,
|
||||
type GuildRoleStructure,
|
||||
type GuildStructure,
|
||||
Transformers,
|
||||
} from '../../client/transformers';
|
||||
|
||||
export const GUILD_AUDIT_LOG_ENTRY_CREATE = (_self: UsingClient, data: GatewayGuildAuditLogEntryCreateDispatchData) => {
|
||||
return toCamelCase(data);
|
||||
};
|
||||
|
||||
export const GUILD_BAN_ADD = (self: UsingClient, data: GatewayGuildBanAddDispatchData) => {
|
||||
return { ...toCamelCase(data), user: new User(self, data.user) };
|
||||
return { ...toCamelCase(data), user: Transformers.User(self, data.user) };
|
||||
};
|
||||
|
||||
export const GUILD_BAN_REMOVE = (self: UsingClient, data: GatewayGuildBanRemoveDispatchData) => {
|
||||
return { ...toCamelCase(data), user: new User(self, data.user) };
|
||||
return { ...toCamelCase(data), user: Transformers.User(self, data.user) };
|
||||
};
|
||||
|
||||
export const GUILD_CREATE = (self: UsingClient, data: GatewayGuildCreateDispatchData) => {
|
||||
return new Guild<'create'>(self, data);
|
||||
return Transformers.Guild<'create'>(self, data);
|
||||
};
|
||||
|
||||
export const GUILD_DELETE = async (self: UsingClient, data: GatewayGuildDeleteDispatchData) => {
|
||||
@ -48,7 +53,7 @@ export const GUILD_DELETE = async (self: UsingClient, data: GatewayGuildDeleteDi
|
||||
export const GUILD_EMOJIS_UPDATE = (self: UsingClient, data: GatewayGuildEmojisUpdateDispatchData) => {
|
||||
return {
|
||||
...toCamelCase(data),
|
||||
emojis: data.emojis.map(x => new GuildEmoji(self, x, data.guild_id)),
|
||||
emojis: data.emojis.map(x => Transformers.GuildEmoji(self, x, data.guild_id)),
|
||||
};
|
||||
};
|
||||
|
||||
@ -57,26 +62,26 @@ export const GUILD_INTEGRATIONS_UPDATE = (_self: UsingClient, data: GatewayGuild
|
||||
};
|
||||
|
||||
export const GUILD_MEMBER_ADD = (self: UsingClient, data: GatewayGuildMemberAddDispatchData) => {
|
||||
return new GuildMember(self, data, data.user!, data.guild_id);
|
||||
return Transformers.GuildMember(self, data, data.user!, data.guild_id);
|
||||
};
|
||||
|
||||
export const GUILD_MEMBER_REMOVE = (self: UsingClient, data: GatewayGuildMemberRemoveDispatchData) => {
|
||||
return { ...toCamelCase(data), user: new User(self, data.user) };
|
||||
return { ...toCamelCase(data), user: Transformers.User(self, data.user) };
|
||||
};
|
||||
|
||||
export const GUILD_MEMBERS_CHUNK = (self: UsingClient, data: GatewayGuildMembersChunkDispatchData) => {
|
||||
return {
|
||||
...toCamelCase(data),
|
||||
members: data.members.map(x => new GuildMember(self, x, x.user!, data.guild_id)),
|
||||
members: data.members.map(x => Transformers.GuildMember(self, x, x.user!, data.guild_id)),
|
||||
};
|
||||
};
|
||||
|
||||
export const GUILD_MEMBER_UPDATE = async (
|
||||
self: UsingClient,
|
||||
data: GatewayGuildMemberUpdateDispatchData,
|
||||
): Promise<[member: GuildMember, old?: GuildMember]> => {
|
||||
): Promise<[member: GuildMemberStructure, old?: GuildMemberStructure]> => {
|
||||
const oldData = await self.cache.members?.get(data.user.id, data.guild_id);
|
||||
return [new GuildMember(self, data, data.user, data.guild_id), oldData];
|
||||
return [Transformers.GuildMember(self, data, data.user, data.guild_id), oldData];
|
||||
};
|
||||
|
||||
export const GUILD_SCHEDULED_EVENT_CREATE = (
|
||||
@ -115,7 +120,7 @@ export const GUILD_SCHEDULED_EVENT_USER_REMOVE = (
|
||||
};
|
||||
|
||||
export const GUILD_ROLE_CREATE = (self: UsingClient, data: GatewayGuildRoleCreateDispatchData) => {
|
||||
return new GuildRole(self, data.role, data.guild_id);
|
||||
return Transformers.GuildRole(self, data.role, data.guild_id);
|
||||
};
|
||||
|
||||
export const GUILD_ROLE_DELETE = async (self: UsingClient, data: GatewayGuildRoleDeleteDispatchData) => {
|
||||
@ -125,20 +130,20 @@ export const GUILD_ROLE_DELETE = async (self: UsingClient, data: GatewayGuildRol
|
||||
export const GUILD_ROLE_UPDATE = async (
|
||||
self: UsingClient,
|
||||
data: GatewayGuildRoleUpdateDispatchData,
|
||||
): Promise<[role: GuildRole, old?: GuildRole]> => {
|
||||
return [new GuildRole(self, data.role, data.guild_id), await self.cache.roles?.get(data.role.id)];
|
||||
): Promise<[role: GuildRoleStructure, old?: GuildRoleStructure]> => {
|
||||
return [Transformers.GuildRole(self, data.role, data.guild_id), await self.cache.roles?.get(data.role.id)];
|
||||
};
|
||||
|
||||
export const GUILD_STICKERS_UPDATE = (self: UsingClient, data: GatewayGuildStickersUpdateDispatchData) => {
|
||||
return {
|
||||
...toCamelCase(data),
|
||||
stickers: data.stickers.map(x => new Sticker(self, x)),
|
||||
stickers: data.stickers.map(x => Transformers.Sticker(self, x)),
|
||||
};
|
||||
};
|
||||
|
||||
export const GUILD_UPDATE = async (
|
||||
self: UsingClient,
|
||||
data: GatewayGuildUpdateDispatchData,
|
||||
): Promise<[guild: Guild, old?: Guild<'cached'>]> => {
|
||||
return [new Guild(self, data), await self.cache.guilds?.get(data.id)];
|
||||
): Promise<[guild: GuildStructure, old?: GuildStructure<'cached'>]> => {
|
||||
return [Transformers.Guild(self, data), await self.cache.guilds?.get(data.id)];
|
||||
};
|
||||
|
@ -4,14 +4,14 @@ import type {
|
||||
GatewayIntegrationUpdateDispatchData,
|
||||
} from 'discord-api-types/v10';
|
||||
import { toCamelCase } from '../../common';
|
||||
import { User } from '../../structures';
|
||||
import type { UsingClient } from '../../commands';
|
||||
import { Transformers } from '../../client/transformers';
|
||||
|
||||
export const INTEGRATION_CREATE = (self: UsingClient, data: GatewayIntegrationCreateDispatchData) => {
|
||||
return data.user
|
||||
? {
|
||||
...toCamelCase(data),
|
||||
user: new User(self, data.user!),
|
||||
user: Transformers.User(self, data.user!),
|
||||
}
|
||||
: toCamelCase(data);
|
||||
};
|
||||
@ -20,7 +20,7 @@ export const INTEGRATION_UPDATE = (self: UsingClient, data: GatewayIntegrationUp
|
||||
return data.user
|
||||
? {
|
||||
...toCamelCase(data),
|
||||
user: new User(self, data.user!),
|
||||
user: Transformers.User(self, data.user!),
|
||||
}
|
||||
: toCamelCase(data);
|
||||
};
|
||||
|
@ -11,17 +11,17 @@ import type {
|
||||
GatewayMessageUpdateDispatchData,
|
||||
} from 'discord-api-types/v10';
|
||||
import { type MakeRequired, type PartialClass, toCamelCase, type ObjectToLower } from '../../common';
|
||||
import { Message } from '../../structures';
|
||||
import type { UsingClient } from '../../commands';
|
||||
import { type MessageStructure, Transformers } from '../../client/transformers';
|
||||
|
||||
export const MESSAGE_CREATE = (self: UsingClient, data: GatewayMessageCreateDispatchData) => {
|
||||
return new Message(self, data);
|
||||
return Transformers.Message(self, data);
|
||||
};
|
||||
|
||||
export const MESSAGE_DELETE = async (
|
||||
self: UsingClient,
|
||||
data: GatewayMessageDeleteDispatchData,
|
||||
): Promise<Message | ObjectToLower<GatewayMessageDeleteDispatchData>> => {
|
||||
): Promise<MessageStructure | ObjectToLower<GatewayMessageDeleteDispatchData>> => {
|
||||
return (await self.cache.messages?.get(data.id)) ?? toCamelCase(data);
|
||||
};
|
||||
|
||||
@ -57,7 +57,7 @@ export const MESSAGE_UPDATE = async (
|
||||
): Promise<
|
||||
[
|
||||
message: MakeRequired<
|
||||
PartialClass<Message>,
|
||||
PartialClass<MessageStructure>, //sus
|
||||
| 'id'
|
||||
| 'channelId'
|
||||
| 'createdAt'
|
||||
@ -71,14 +71,10 @@ export const MESSAGE_UPDATE = async (
|
||||
| 'user'
|
||||
| 'author'
|
||||
>,
|
||||
old: undefined | Message,
|
||||
old: undefined | MessageStructure,
|
||||
]
|
||||
> => {
|
||||
return [new Message(self, data as APIMessage), await self.cache.messages?.get(data.id)];
|
||||
};
|
||||
|
||||
export const MESSAGE_POLL_VOTE_ADD = (_: UsingClient, data: GatewayMessagePollVoteDispatchData) => {
|
||||
return toCamelCase(data);
|
||||
return [Transformers.Message(self, data as APIMessage), await self.cache.messages?.get(data.id)];
|
||||
};
|
||||
|
||||
export const MESSAGE_POLL_VOTE_REMOVE = (_: UsingClient, data: GatewayMessagePollVoteDispatchData) => {
|
||||
|
@ -3,5 +3,5 @@ import type { UsingClient } from '../../commands';
|
||||
import { toCamelCase } from '../../common';
|
||||
|
||||
export const PRESENCE_UPDATE = async (self: UsingClient, data: GatewayPresenceUpdateDispatchData) => {
|
||||
return [toCamelCase(data), await self.cache.presences?.get(data.user.id)];
|
||||
return [toCamelCase(data), await self.cache.presences?.get(data.user.id)] as const;
|
||||
};
|
||||
|
@ -7,15 +7,15 @@ import type {
|
||||
GatewayThreadUpdateDispatchData,
|
||||
} from 'discord-api-types/v10';
|
||||
import { toCamelCase } from '../../common';
|
||||
import { ThreadChannel } from '../../structures';
|
||||
import type { UsingClient } from '../../commands';
|
||||
import { type ThreadChannelStructure, Transformers } from '../../client/transformers';
|
||||
|
||||
export const THREAD_CREATE = (self: UsingClient, data: GatewayThreadCreateDispatchData) => {
|
||||
return new ThreadChannel(self, data);
|
||||
return Transformers.ThreadChannel(self, data);
|
||||
};
|
||||
|
||||
export const THREAD_DELETE = (self: UsingClient, data: GatewayThreadDeleteDispatchData) => {
|
||||
return new ThreadChannel(self, data);
|
||||
return Transformers.ThreadChannel(self, data);
|
||||
};
|
||||
|
||||
export const THREAD_LIST_SYNC = (_self: UsingClient, data: GatewayThreadListSyncDispatchData) => {
|
||||
@ -33,6 +33,6 @@ export const THREAD_MEMBERS_UPDATE = (_self: UsingClient, data: GatewayThreadMem
|
||||
export const THREAD_UPDATE = async (
|
||||
self: UsingClient,
|
||||
data: GatewayThreadUpdateDispatchData,
|
||||
): Promise<[thread: ThreadChannel, old?: ThreadChannel]> => {
|
||||
return [new ThreadChannel(self, data), await self.cache.threads?.get(data.id)];
|
||||
): Promise<[thread: ThreadChannelStructure, old?: ThreadChannelStructure]> => {
|
||||
return [Transformers.ThreadChannel(self, data), await self.cache.threads?.get(data.id)];
|
||||
};
|
||||
|
@ -1,13 +1,13 @@
|
||||
import type { GatewayTypingStartDispatchData } from 'discord-api-types/v10';
|
||||
import { toCamelCase } from '../../common';
|
||||
import { GuildMember } from '../../structures';
|
||||
import type { UsingClient } from '../../commands';
|
||||
import { Transformers } from '../../client/transformers';
|
||||
|
||||
export const TYPING_START = (self: UsingClient, data: GatewayTypingStartDispatchData) => {
|
||||
return data.member
|
||||
? {
|
||||
...toCamelCase(data),
|
||||
member: new GuildMember(self, data.member, data.member.user!, data.guild_id!),
|
||||
member: Transformers.GuildMember(self, data.member, data.member.user!, data.guild_id!),
|
||||
}
|
||||
: toCamelCase(data);
|
||||
};
|
||||
|
@ -1,10 +1,10 @@
|
||||
import type { GatewayUserUpdateDispatchData } from 'discord-api-types/v10';
|
||||
import { User } from '../../structures';
|
||||
import type { UsingClient } from '../../commands';
|
||||
import { Transformers, type UserStructure } from '../../client/transformers';
|
||||
|
||||
export const USER_UPDATE = async (
|
||||
self: UsingClient,
|
||||
data: GatewayUserUpdateDispatchData,
|
||||
): Promise<[user: User, old?: User]> => {
|
||||
return [new User(self, data), await self.cache.users?.get(data.id)];
|
||||
): Promise<[user: UserStructure, old?: UserStructure]> => {
|
||||
return [Transformers.User(self, data), await self.cache.users?.get(data.id)];
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { GatewayVoiceServerUpdateDispatchData, GatewayVoiceStateUpdateDispatchData } from '../../types';
|
||||
import { toCamelCase } from '../../common';
|
||||
import { VoiceState } from '../../structures';
|
||||
import type { UsingClient } from '../../commands';
|
||||
import { Transformers, type VoiceStateStructure } from '../../client/transformers';
|
||||
|
||||
export const VOICE_SERVER_UPDATE = (_self: UsingClient, data: GatewayVoiceServerUpdateDispatchData) => {
|
||||
return toCamelCase(data);
|
||||
@ -10,7 +10,7 @@ export const VOICE_SERVER_UPDATE = (_self: UsingClient, data: GatewayVoiceServer
|
||||
export const VOICE_STATE_UPDATE = async (
|
||||
self: UsingClient,
|
||||
data: GatewayVoiceStateUpdateDispatchData,
|
||||
): Promise<[VoiceState] | [state: VoiceState, old?: VoiceState]> => {
|
||||
if (!data.guild_id) return [new VoiceState(self, data)];
|
||||
return [new VoiceState(self, data), await self.cache.voiceStates?.get(data.user_id, data.guild_id)];
|
||||
): Promise<[state: VoiceStateStructure] | [state: VoiceStateStructure, old?: VoiceStateStructure]> => {
|
||||
if (!data.guild_id) return [Transformers.VoiceState(self, data)];
|
||||
return [Transformers.VoiceState(self, data), await self.cache.voiceStates?.get(data.user_id, data.guild_id)];
|
||||
};
|
||||
|
@ -26,7 +26,7 @@ export { ShardManager, WorkerManager } from './websocket/discord';
|
||||
export * from './structures';
|
||||
//
|
||||
export * from './client';
|
||||
//
|
||||
///
|
||||
|
||||
export function throwError(msg: string): never {
|
||||
throw new Error(msg);
|
||||
|
@ -23,8 +23,8 @@ import type { UsingClient } from '../commands';
|
||||
import { Formatter, type MessageCreateBodyRequest, type ObjectToLower, type ToClass } from '../common';
|
||||
import type { ImageOptions, MethodContext } from '../common/types/options';
|
||||
import type { GuildMemberResolvable } from '../common/types/resolvables';
|
||||
import { User } from './User';
|
||||
import { PermissionsBitField } from './extra/Permissions';
|
||||
import { Transformers, type UserStructure } from '../client/transformers';
|
||||
|
||||
export interface BaseGuildMember extends DiscordBase, ObjectToLower<Omit<APIGuildMember, 'user' | 'roles'>> {}
|
||||
export class BaseGuildMember extends DiscordBase {
|
||||
@ -127,17 +127,17 @@ export interface GuildMember extends ObjectToLower<Omit<APIGuildMember, 'user' |
|
||||
* @link https://discord.com/developers/docs/resources/guild#guild-member-object
|
||||
*/
|
||||
export class GuildMember extends BaseGuildMember {
|
||||
user: User;
|
||||
user: UserStructure;
|
||||
private __me?: GuildMember;
|
||||
constructor(
|
||||
client: UsingClient,
|
||||
data: GuildMemberData,
|
||||
user: APIUser | User,
|
||||
user: APIUser,
|
||||
/** the choosen guild id */
|
||||
readonly guildId: string,
|
||||
) {
|
||||
super(client, data, user.id, guildId);
|
||||
this.user = user instanceof User ? user : new User(client, user);
|
||||
this.user = Transformers.User(client, user);
|
||||
}
|
||||
|
||||
get tag() {
|
||||
@ -242,7 +242,7 @@ export class InteractionGuildMember extends (GuildMember as unknown as ToClass<
|
||||
constructor(
|
||||
client: UsingClient,
|
||||
data: APIInteractionDataResolvedGuildMember,
|
||||
user: APIUser | User,
|
||||
user: APIUser,
|
||||
/** the choosen guild id */
|
||||
guildId: string,
|
||||
) {
|
||||
|
@ -40,7 +40,7 @@ import {
|
||||
import { mix } from 'ts-mixer';
|
||||
import type { RawFile } from '../api';
|
||||
import { ActionRow, Embed, Modal, PollBuilder, resolveAttachment, resolveFiles } from '../builders';
|
||||
import { OptionResolver, type ContextOptionsResolved, type UsingClient } from '../commands';
|
||||
import type { ContextOptionsResolved, UsingClient } from '../commands';
|
||||
import type { ObjectToLower, OmitInsert, ToClass, When } from '../common';
|
||||
import type {
|
||||
ComponentInteractionMessageUpdate,
|
||||
@ -51,13 +51,19 @@ import type {
|
||||
MessageWebhookCreateBodyRequest,
|
||||
ModalCreateBodyRequest,
|
||||
} from '../common/types/write';
|
||||
import { InteractionGuildMember, type AllChannels } from './';
|
||||
import { GuildRole } from './GuildRole';
|
||||
import { Message, type WebhookMessage } from './Message';
|
||||
import { User } from './User';
|
||||
import type { AllChannels } from './';
|
||||
import channelFrom from './channels';
|
||||
import { DiscordBase } from './extra/DiscordBase';
|
||||
import { PermissionsBitField } from './extra/Permissions';
|
||||
import {
|
||||
type GuildRoleStructure,
|
||||
type InteractionGuildMemberStructure,
|
||||
type MessageStructure,
|
||||
Transformers,
|
||||
type UserStructure,
|
||||
type WebhookMessageStructure,
|
||||
type OptionResolverStructure,
|
||||
} from '../client/transformers';
|
||||
|
||||
export type ReplyInteractionBody =
|
||||
| { type: InteractionResponseType.Modal; data: ModalCreateBodyRequest }
|
||||
@ -82,10 +88,10 @@ export class BaseInteraction<
|
||||
FromGuild extends boolean = boolean,
|
||||
Type extends APIInteraction = APIInteraction,
|
||||
> extends DiscordBase<Type> {
|
||||
user: User;
|
||||
member!: When<FromGuild, InteractionGuildMember, undefined>;
|
||||
user: UserStructure;
|
||||
member!: When<FromGuild, InteractionGuildMemberStructure, undefined>;
|
||||
channel?: AllChannels;
|
||||
message?: Message;
|
||||
message?: MessageStructure;
|
||||
replied?: Promise<boolean> | boolean;
|
||||
appPermissions?: PermissionsBitField;
|
||||
|
||||
@ -96,15 +102,15 @@ export class BaseInteraction<
|
||||
) {
|
||||
super(client, interaction);
|
||||
if (interaction.member) {
|
||||
this.member = new InteractionGuildMember(
|
||||
this.member = Transformers.InteractionGuildMember(
|
||||
client,
|
||||
interaction.member,
|
||||
interaction.member!.user,
|
||||
interaction.member.user,
|
||||
interaction.guild_id!,
|
||||
) as never;
|
||||
}
|
||||
if (interaction.message) {
|
||||
this.message = new Message(client, interaction.message);
|
||||
this.message = Transformers.Message(client, interaction.message);
|
||||
}
|
||||
if (interaction.app_permissions) {
|
||||
this.appPermissions = new PermissionsBitField(Number(interaction.app_permissions));
|
||||
@ -112,7 +118,7 @@ export class BaseInteraction<
|
||||
if (interaction.channel) {
|
||||
this.channel = channelFrom(interaction.channel, client);
|
||||
}
|
||||
this.user = this.member?.user ?? new User(client, interaction.user!);
|
||||
this.user = this.member?.user ?? Transformers.User(client, interaction.user!);
|
||||
}
|
||||
|
||||
static transformBodyRequest(
|
||||
@ -315,17 +321,17 @@ export class AutocompleteInteraction<FromGuild extends boolean = boolean> extend
|
||||
> {
|
||||
declare type: InteractionType.ApplicationCommandAutocomplete;
|
||||
declare data: ObjectToLower<APIApplicationCommandAutocompleteInteraction['data']>;
|
||||
options: OptionResolver;
|
||||
options: OptionResolverStructure;
|
||||
constructor(
|
||||
client: UsingClient,
|
||||
interaction: APIApplicationCommandAutocompleteInteraction,
|
||||
resolver?: OptionResolver,
|
||||
resolver?: OptionResolverStructure,
|
||||
protected __reply?: __InternalReplyFunction,
|
||||
) {
|
||||
super(client, interaction);
|
||||
this.options =
|
||||
resolver ??
|
||||
new OptionResolver(
|
||||
Transformers.OptionResolver(
|
||||
client,
|
||||
interaction.data.options,
|
||||
undefined,
|
||||
@ -363,7 +369,7 @@ export class Interaction<
|
||||
async write<FR extends boolean = false>(
|
||||
body: InteractionCreateBodyRequest,
|
||||
fetchReply?: FR,
|
||||
): Promise<When<FR, WebhookMessage, void>> {
|
||||
): Promise<When<FR, WebhookMessageStructure, void>> {
|
||||
(await this.reply({
|
||||
type: InteractionResponseType.ChannelMessageWithSource,
|
||||
data: body,
|
||||
@ -382,7 +388,7 @@ export class Interaction<
|
||||
async editOrReply<FR extends boolean = false>(
|
||||
body: InteractionCreateBodyRequest,
|
||||
fetchReply?: FR,
|
||||
): Promise<When<FR, WebhookMessage, void>>;
|
||||
): Promise<When<FR, WebhookMessageStructure, void>>;
|
||||
async editOrReply<FR extends true = true>(body: InteractionMessageUpdateBodyRequest, fetchReply?: FR) {
|
||||
if (await this.replied) {
|
||||
const { content, embeds, allowed_mentions, components, files, attachments } = body;
|
||||
@ -444,7 +450,7 @@ export class ComponentInteraction<
|
||||
declare channelId: string;
|
||||
declare channel: AllChannels;
|
||||
declare type: InteractionType.MessageComponent;
|
||||
declare message: Message;
|
||||
declare message: MessageStructure;
|
||||
|
||||
update(data: ComponentInteractionMessageUpdate) {
|
||||
return this.reply({
|
||||
@ -536,9 +542,9 @@ export class ChannelSelectMenuInteraction extends SelectMenuInteraction {
|
||||
}
|
||||
|
||||
export class MentionableSelectMenuInteraction extends SelectMenuInteraction {
|
||||
roles: GuildRole[];
|
||||
members: InteractionGuildMember[];
|
||||
users: User[];
|
||||
roles: GuildRoleStructure[];
|
||||
members: InteractionGuildMemberStructure[];
|
||||
users: UserStructure[];
|
||||
constructor(
|
||||
client: UsingClient,
|
||||
interaction: APIMessageComponentSelectMenuInteraction,
|
||||
@ -547,25 +553,24 @@ export class MentionableSelectMenuInteraction extends SelectMenuInteraction {
|
||||
super(client, interaction);
|
||||
const resolved = (interaction.data as APIMessageMentionableSelectInteractionData).resolved;
|
||||
this.roles = resolved.roles
|
||||
? this.values.map(x => new GuildRole(this.client, resolved.roles![x], this.guildId!))
|
||||
? this.values.map(x => Transformers.GuildRole(this.client, resolved.roles![x], this.guildId!))
|
||||
: [];
|
||||
this.members = resolved.members
|
||||
? this.values.map(
|
||||
x =>
|
||||
new InteractionGuildMember(
|
||||
? this.values.map(x =>
|
||||
Transformers.InteractionGuildMember(
|
||||
this.client,
|
||||
resolved.members![x],
|
||||
this.users!.find(u => u.id === x)!,
|
||||
resolved.users![this.values!.find(u => u === x)!]!,
|
||||
this.guildId!,
|
||||
),
|
||||
)
|
||||
: [];
|
||||
this.users = resolved.users ? this.values.map(x => new User(this.client, resolved.users![x])) : [];
|
||||
this.users = resolved.users ? this.values.map(x => Transformers.User(this.client, resolved.users![x])) : [];
|
||||
}
|
||||
}
|
||||
|
||||
export class RoleSelectMenuInteraction extends SelectMenuInteraction {
|
||||
roles: GuildRole[];
|
||||
roles: GuildRoleStructure[];
|
||||
constructor(
|
||||
client: UsingClient,
|
||||
interaction: APIMessageComponentSelectMenuInteraction,
|
||||
@ -573,13 +578,13 @@ export class RoleSelectMenuInteraction extends SelectMenuInteraction {
|
||||
) {
|
||||
super(client, interaction);
|
||||
const resolved = (interaction.data as APIMessageRoleSelectInteractionData).resolved;
|
||||
this.roles = this.values.map(x => new GuildRole(this.client, resolved.roles[x], this.guildId!));
|
||||
this.roles = this.values.map(x => Transformers.GuildRole(this.client, resolved.roles[x], this.guildId!));
|
||||
}
|
||||
}
|
||||
|
||||
export class UserSelectMenuInteraction extends SelectMenuInteraction {
|
||||
members: InteractionGuildMember[];
|
||||
users: User[];
|
||||
members: InteractionGuildMemberStructure[];
|
||||
users: UserStructure[];
|
||||
constructor(
|
||||
client: UsingClient,
|
||||
interaction: APIMessageComponentSelectMenuInteraction,
|
||||
@ -587,14 +592,13 @@ export class UserSelectMenuInteraction extends SelectMenuInteraction {
|
||||
) {
|
||||
super(client, interaction);
|
||||
const resolved = (interaction.data as APIMessageUserSelectInteractionData).resolved;
|
||||
this.users = this.values.map(x => new User(this.client, resolved.users[x]));
|
||||
this.users = this.values.map(x => Transformers.User(this.client, resolved.users[x]));
|
||||
this.members = resolved.members
|
||||
? this.values.map(
|
||||
x =>
|
||||
new InteractionGuildMember(
|
||||
? this.values.map(x =>
|
||||
Transformers.InteractionGuildMember(
|
||||
this.client,
|
||||
resolved.members![x],
|
||||
this.users!.find(u => u.id === x)!,
|
||||
resolved.users[this.values!.find(u => u === x)!]!,
|
||||
this.guildId!,
|
||||
),
|
||||
)
|
||||
|
@ -13,12 +13,16 @@ import type { EmojiResolvable } from '../common/types/resolvables';
|
||||
import type { MessageCreateBodyRequest, MessageUpdateBodyRequest } from '../common/types/write';
|
||||
import type { ActionRowMessageComponents } from '../components';
|
||||
import { MessageActionRowComponent } from '../components/ActionRow';
|
||||
import { GuildMember } from './GuildMember';
|
||||
import { User } from './User';
|
||||
import type { MessageWebhookMethodEditParams, MessageWebhookMethodWriteParams } from './Webhook';
|
||||
import { DiscordBase } from './extra/DiscordBase';
|
||||
import { messageLink } from './extra/functions';
|
||||
import { Embed, Poll } from '..';
|
||||
import { Embed } from '..';
|
||||
import {
|
||||
type PollStructure,
|
||||
Transformers,
|
||||
type GuildMemberStructure,
|
||||
type UserStructure,
|
||||
} from '../client/transformers';
|
||||
|
||||
export type MessageData = APIMessage | GatewayMessageCreateDispatchData;
|
||||
|
||||
@ -27,14 +31,14 @@ export interface BaseMessage
|
||||
ObjectToLower<Omit<MessageData, 'timestamp' | 'author' | 'mentions' | 'components' | 'poll' | 'embeds'>> {
|
||||
timestamp?: number;
|
||||
guildId?: string;
|
||||
author: User;
|
||||
member?: GuildMember;
|
||||
author: UserStructure;
|
||||
member?: GuildMemberStructure;
|
||||
components: MessageActionRowComponent<ActionRowMessageComponents>[];
|
||||
poll?: Poll;
|
||||
poll?: PollStructure;
|
||||
mentions: {
|
||||
roles: string[];
|
||||
channels: APIChannelMention[];
|
||||
users: (GuildMember | User)[];
|
||||
users: (GuildMemberStructure | UserStructure)[];
|
||||
};
|
||||
}
|
||||
export class BaseMessage extends DiscordBase {
|
||||
@ -83,18 +87,17 @@ export class BaseMessage extends DiscordBase {
|
||||
}
|
||||
|
||||
if ('author' in data && data.author) {
|
||||
this.author = new User(this.client, data.author);
|
||||
this.author = Transformers.User(this.client, data.author);
|
||||
}
|
||||
|
||||
if ('member' in data && data.member) {
|
||||
this.member = new GuildMember(this.client, data.member, this.author, this.guildId!);
|
||||
this.member = Transformers.GuildMember(this.client, data.member, data.author, this.guildId!);
|
||||
}
|
||||
|
||||
if (data.mentions?.length) {
|
||||
this.mentions.users = this.guildId
|
||||
? data.mentions.map(
|
||||
m =>
|
||||
new GuildMember(
|
||||
? data.mentions.map(m =>
|
||||
Transformers.GuildMember(
|
||||
this.client,
|
||||
{
|
||||
...(m as APIUser & { member?: Omit<APIGuildMember, 'user'> }).member!,
|
||||
@ -104,11 +107,11 @@ export class BaseMessage extends DiscordBase {
|
||||
this.guildId!,
|
||||
),
|
||||
)
|
||||
: data.mentions.map(u => new User(this.client, u));
|
||||
: data.mentions.map(u => Transformers.User(this.client, u));
|
||||
}
|
||||
|
||||
if (data.poll) {
|
||||
this.poll = new Poll(this.client, data.poll, this.channelId, this.id);
|
||||
this.poll = Transformers.Poll(this.client, data.poll, this.channelId, this.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,17 +6,17 @@ import type {
|
||||
import type { RawFile, UsingClient } from '..';
|
||||
import type { Attachment, AttachmentBuilder } from '../builders';
|
||||
import type { MethodContext, ObjectToLower } from '../common';
|
||||
import { User } from './User';
|
||||
import { DiscordBase } from './extra/DiscordBase';
|
||||
import { Transformers, type UserStructure } from '../client/transformers';
|
||||
|
||||
export interface Sticker extends DiscordBase, ObjectToLower<Omit<APISticker, 'user'>> {}
|
||||
|
||||
export class Sticker extends DiscordBase {
|
||||
user?: User;
|
||||
user?: UserStructure;
|
||||
constructor(client: UsingClient, data: APISticker) {
|
||||
super(client, data);
|
||||
if (data.user) {
|
||||
this.user = new User(this.client, data.user);
|
||||
this.user = Transformers.User(this.client, data.user);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { GuildMember, type UsingClient } from '../';
|
||||
import type { UsingClient } from '../';
|
||||
import type { VoiceStateResource } from '../cache/resources/voice-states';
|
||||
import { type GuildMemberStructure, Transformers } from '../client/transformers';
|
||||
import type { ObjectToLower } from '../common';
|
||||
import type { GatewayVoiceState } from '../types';
|
||||
import { Base } from './extra/Base';
|
||||
@ -7,12 +8,13 @@ import { Base } from './extra/Base';
|
||||
export interface VoiceState extends Base, ObjectToLower<Omit<VoiceStateResource, 'member'>> {}
|
||||
|
||||
export class VoiceState extends Base {
|
||||
protected withMember?: GuildMember;
|
||||
protected withMember?: GuildMemberStructure;
|
||||
constructor(client: UsingClient, data: GatewayVoiceState) {
|
||||
super(client);
|
||||
const { member, ...rest } = data;
|
||||
this.__patchThis(rest);
|
||||
if (member?.user && data.guild_id) this.withMember = new GuildMember(client, member, member.user, data.guild_id);
|
||||
if (member?.user && data.guild_id)
|
||||
this.withMember = Transformers.GuildMember(client, member, member.user, data.guild_id);
|
||||
}
|
||||
|
||||
isMuted() {
|
||||
|
@ -17,9 +17,8 @@ import type {
|
||||
MethodContext,
|
||||
ObjectToLower,
|
||||
} from '../common';
|
||||
import { AnonymousGuild } from './AnonymousGuild';
|
||||
import { User } from './User';
|
||||
import { DiscordBase } from './extra/DiscordBase';
|
||||
import { type AnonymousGuildStructure, Transformers, type UserStructure } from '../client/transformers';
|
||||
|
||||
export interface Webhook extends DiscordBase, ObjectToLower<Omit<APIWebhook, 'user' | 'source_guild'>> {}
|
||||
|
||||
@ -28,9 +27,9 @@ export interface Webhook extends DiscordBase, ObjectToLower<Omit<APIWebhook, 'us
|
||||
*/
|
||||
export class Webhook extends DiscordBase {
|
||||
/** The user associated with the webhook, if applicable. */
|
||||
user?: User;
|
||||
user?: UserStructure;
|
||||
/** The source guild of the webhook, if applicable. */
|
||||
sourceGuild?: Partial<AnonymousGuild>;
|
||||
sourceGuild?: Partial<AnonymousGuildStructure>;
|
||||
/** Methods related to interacting with messages through the webhook. */
|
||||
messages!: ReturnType<typeof Webhook.messages>;
|
||||
/**
|
||||
@ -42,11 +41,11 @@ export class Webhook extends DiscordBase {
|
||||
super(client, data);
|
||||
|
||||
if (data.user) {
|
||||
this.user = new User(this.client, data.user);
|
||||
this.user = Transformers.User(this.client, data.user);
|
||||
}
|
||||
|
||||
if (data.source_guild) {
|
||||
this.sourceGuild = new AnonymousGuild(this.client, data.source_guild);
|
||||
this.sourceGuild = Transformers.AnonymousGuild(this.client, data.source_guild);
|
||||
}
|
||||
|
||||
Object.assign(this, {
|
||||
|
@ -42,6 +42,22 @@ import type { GuildRole } from './GuildRole';
|
||||
import { DiscordBase } from './extra/DiscordBase';
|
||||
import { channelLink } from './extra/functions';
|
||||
import { Collection, Formatter, type RawFile } from '..';
|
||||
import {
|
||||
type BaseChannelStructure,
|
||||
type BaseGuildChannelStructure,
|
||||
type CategoryChannelStructure,
|
||||
type DMChannelStructure,
|
||||
type DirectoryChannelStructure,
|
||||
type ForumChannelStructure,
|
||||
type GuildMemberStructure,
|
||||
type MediaChannelStructure,
|
||||
type NewsChannelStructure,
|
||||
type StageChannelStructure,
|
||||
type TextGuildChannelStructure,
|
||||
type ThreadChannelStructure,
|
||||
Transformers,
|
||||
type VoiceChannelStructure,
|
||||
} from '../client/transformers';
|
||||
|
||||
export class BaseChannel<T extends ChannelType> extends DiscordBase<APIChannelBase<ChannelType>> {
|
||||
declare type: T;
|
||||
@ -293,32 +309,32 @@ export class TextBaseGuildChannel extends BaseGuildChannel {}
|
||||
export default function channelFrom(data: APIChannelBase<ChannelType>, client: UsingClient): AllChannels {
|
||||
switch (data.type) {
|
||||
case ChannelType.GuildStageVoice:
|
||||
return new StageChannel(client, data);
|
||||
return Transformers.StageChannel(client, data);
|
||||
case ChannelType.GuildMedia:
|
||||
return new MediaChannel(client, data);
|
||||
return Transformers.MediaChannel(client, data);
|
||||
case ChannelType.DM:
|
||||
return new DMChannel(client, data);
|
||||
return Transformers.DMChannel(client, data);
|
||||
case ChannelType.GuildForum:
|
||||
return new ForumChannel(client, data);
|
||||
return Transformers.ForumChannel(client, data);
|
||||
case ChannelType.AnnouncementThread:
|
||||
case ChannelType.PrivateThread:
|
||||
case ChannelType.PublicThread:
|
||||
return new ThreadChannel(client, data);
|
||||
return Transformers.ThreadChannel(client, data);
|
||||
case ChannelType.GuildDirectory:
|
||||
return new DirectoryChannel(client, data);
|
||||
return Transformers.DirectoryChannel(client, data);
|
||||
case ChannelType.GuildVoice:
|
||||
return new VoiceChannel(client, data);
|
||||
return Transformers.VoiceChannel(client, data);
|
||||
case ChannelType.GuildText:
|
||||
return new TextGuildChannel(client, data as APIGuildChannel<ChannelType>);
|
||||
return Transformers.TextGuildChannel(client, data as APIGuildChannel<ChannelType>);
|
||||
case ChannelType.GuildCategory:
|
||||
return new CategoryChannel(client, data);
|
||||
return Transformers.CategoryChannel(client, data);
|
||||
case ChannelType.GuildAnnouncement:
|
||||
return new NewsChannel(client, data);
|
||||
return Transformers.NewsChannel(client, data);
|
||||
default:
|
||||
if ('guild_id' in data) {
|
||||
return new BaseGuildChannel(client, data as APIGuildChannel<ChannelType>);
|
||||
return Transformers.BaseGuildChannel(client, data as APIGuildChannel<ChannelType>);
|
||||
}
|
||||
return new BaseChannel(client, data);
|
||||
return Transformers.BaseChannel(client, data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -389,7 +405,7 @@ export class VoiceChannelMethods extends DiscordBase {
|
||||
}
|
||||
|
||||
public async members(force?: boolean) {
|
||||
const collection = new Collection<string, GuildMember>();
|
||||
const collection = new Collection<string, GuildMemberStructure>();
|
||||
|
||||
const states = await this.states();
|
||||
|
||||
@ -571,30 +587,39 @@ export class NewsChannel extends BaseChannel<ChannelType.GuildAnnouncement> {
|
||||
export class DirectoryChannel extends BaseChannel<ChannelType.GuildDirectory> {}
|
||||
|
||||
export type AllGuildChannels =
|
||||
| TextGuildChannel
|
||||
| VoiceChannel
|
||||
| MediaChannel
|
||||
| ForumChannel
|
||||
| ThreadChannel
|
||||
| CategoryChannel
|
||||
| NewsChannel
|
||||
| DirectoryChannel
|
||||
| StageChannel;
|
||||
| TextGuildChannelStructure
|
||||
| VoiceChannelStructure
|
||||
| MediaChannelStructure
|
||||
| ForumChannelStructure
|
||||
| ThreadChannelStructure
|
||||
| CategoryChannelStructure
|
||||
| NewsChannelStructure
|
||||
| DirectoryChannelStructure
|
||||
| StageChannelStructure;
|
||||
|
||||
export type AllTextableChannels = TextGuildChannel | VoiceChannel | DMChannel | NewsChannel | ThreadChannel;
|
||||
export type AllGuildTextableChannels = TextGuildChannel | VoiceChannel | NewsChannel | ThreadChannel;
|
||||
export type AllGuildVoiceChannels = VoiceChannel | StageChannel;
|
||||
export type AllTextableChannels =
|
||||
| TextGuildChannelStructure
|
||||
| VoiceChannelStructure
|
||||
| DMChannelStructure
|
||||
| NewsChannelStructure
|
||||
| ThreadChannelStructure;
|
||||
export type AllGuildTextableChannels =
|
||||
| TextGuildChannelStructure
|
||||
| VoiceChannelStructure
|
||||
| NewsChannelStructure
|
||||
| ThreadChannelStructure;
|
||||
export type AllGuildVoiceChannels = VoiceChannelStructure | StageChannelStructure;
|
||||
|
||||
export type AllChannels =
|
||||
| BaseChannel<ChannelType>
|
||||
| BaseGuildChannel
|
||||
| TextGuildChannel
|
||||
| DMChannel
|
||||
| VoiceChannel
|
||||
| MediaChannel
|
||||
| ForumChannel
|
||||
| ThreadChannel
|
||||
| CategoryChannel
|
||||
| NewsChannel
|
||||
| DirectoryChannel
|
||||
| StageChannel;
|
||||
| BaseChannelStructure
|
||||
| BaseGuildChannelStructure
|
||||
| TextGuildChannelStructure
|
||||
| DMChannelStructure
|
||||
| VoiceChannelStructure
|
||||
| MediaChannelStructure
|
||||
| ForumChannelStructure
|
||||
| ThreadChannelStructure
|
||||
| CategoryChannelStructure
|
||||
| NewsChannelStructure
|
||||
| DirectoryChannelStructure
|
||||
| StageChannelStructure;
|
||||
|
@ -1,22 +1,15 @@
|
||||
import type { APIUser, GatewayActivity, GatewayPresenceUpdateDispatchData } from 'discord-api-types/v10';
|
||||
|
||||
type FixedGatewayPresenceUpdateDispatchData =
|
||||
| (Omit<GatewayPresenceUpdateDispatchData, 'user'> & { user_id: string; user: undefined })
|
||||
| (Omit<GatewayPresenceUpdateDispatchData, 'user'> & {
|
||||
user: Partial<APIUser> & Pick<APIUser, 'id'>;
|
||||
user_id: undefined;
|
||||
});
|
||||
import type { GatewayActivity, GatewayPresenceUpdateDispatchData } from 'discord-api-types/v10';
|
||||
|
||||
export class PresenceUpdateHandler {
|
||||
presenceUpdate = new Map<string, { timeout: NodeJS.Timeout; presence: FixedGatewayPresenceUpdateDispatchData }>();
|
||||
presenceUpdate = new Map<string, { timeout: NodeJS.Timeout; presence: GatewayPresenceUpdateDispatchData }>();
|
||||
|
||||
check(presence: FixedGatewayPresenceUpdateDispatchData) {
|
||||
if (!this.presenceUpdate.has(presence.user?.id ?? presence.user_id!)) {
|
||||
check(presence: GatewayPresenceUpdateDispatchData) {
|
||||
if (!this.presenceUpdate.has(presence.user.id)) {
|
||||
this.setPresence(presence);
|
||||
return true;
|
||||
}
|
||||
|
||||
const data = this.presenceUpdate.get(presence.user?.id ?? presence.user_id!)!;
|
||||
const data = this.presenceUpdate.get(presence.user.id)!;
|
||||
|
||||
if (this.presenceEquals(data.presence, presence)) {
|
||||
return false;
|
||||
@ -29,19 +22,16 @@ export class PresenceUpdateHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
setPresence(presence: FixedGatewayPresenceUpdateDispatchData) {
|
||||
this.presenceUpdate.set(presence.user?.id ?? presence.user_id!, {
|
||||
setPresence(presence: GatewayPresenceUpdateDispatchData) {
|
||||
this.presenceUpdate.set(presence.user.id, {
|
||||
presence,
|
||||
timeout: setTimeout(() => {
|
||||
this.presenceUpdate.delete(presence.user?.id ?? presence.user_id!);
|
||||
this.presenceUpdate.delete(presence.user.id);
|
||||
}, 1.5e3),
|
||||
});
|
||||
}
|
||||
|
||||
presenceEquals(
|
||||
oldPresence: FixedGatewayPresenceUpdateDispatchData,
|
||||
newPresence: FixedGatewayPresenceUpdateDispatchData,
|
||||
) {
|
||||
presenceEquals(oldPresence: GatewayPresenceUpdateDispatchData, newPresence: GatewayPresenceUpdateDispatchData) {
|
||||
return (
|
||||
newPresence &&
|
||||
oldPresence.status === newPresence.status &&
|
||||
|
@ -27,12 +27,16 @@ export type WorkerSendCacheRequest = CreateWorkerMessage<
|
||||
nonce: string;
|
||||
method:
|
||||
| 'scan'
|
||||
| 'bulkGet'
|
||||
| 'get'
|
||||
| 'bulkSet'
|
||||
| 'set'
|
||||
| 'bulkPatch'
|
||||
| 'patch'
|
||||
| 'values'
|
||||
| 'keys'
|
||||
| 'count'
|
||||
| 'bulkRemove'
|
||||
| 'remove'
|
||||
| 'flush'
|
||||
| 'contains'
|
||||
|
@ -235,7 +235,7 @@ export class WorkerManager extends Map<
|
||||
}
|
||||
break;
|
||||
case 'PRESENCE_UPDATE':
|
||||
if (!this.presenceUpdateHandler.check(message.payload.d as any)) {
|
||||
if (!this.presenceUpdateHandler.check(message.payload.d)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
Loading…
x
Reference in New Issue
Block a user