mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-02 17:00:08 +00:00
fix: start deris
This commit is contained in:
7
packages/client/.c8rc.json
Normal file
7
packages/client/.c8rc.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"all": true,
|
||||
"src": "src",
|
||||
"reporter": ["text", "lcov"],
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["tests"]
|
||||
}
|
||||
10
packages/client/.mocharc.json
Normal file
10
packages/client/.mocharc.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"require": "ts-node/register",
|
||||
"loader": "ts-node/esm",
|
||||
"recursive": true,
|
||||
"timeout": 2000,
|
||||
"watch-extensions": "ts",
|
||||
"watch-files": ["src", "tests"],
|
||||
"enable-source-maps": true,
|
||||
"parallel": false
|
||||
}
|
||||
31
packages/client/.swcrc
Normal file
31
packages/client/.swcrc
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"minify": true,
|
||||
"jsc": {
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"decorators": true,
|
||||
"dynamicImport": true
|
||||
},
|
||||
"transform": {
|
||||
"legacyDecorator": true,
|
||||
"decoratorMetadata": true
|
||||
},
|
||||
"target": "es2022",
|
||||
"keepClassNames": true,
|
||||
"loose": true,
|
||||
"minify": {
|
||||
"compress": {
|
||||
"unused": true
|
||||
},
|
||||
"mangle": true
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "es6",
|
||||
"strict": false,
|
||||
"strictMode": true,
|
||||
"lazy": false,
|
||||
"noInterop": false
|
||||
},
|
||||
"sourceMaps": "inline"
|
||||
}
|
||||
47
packages/client/package.json
Normal file
47
packages/client/package.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "@discordeno/client",
|
||||
"version": "18.0.0-alpha.1",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"type": "module",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/discordeno/discordeno.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "swc --delete-dir-on-start src --out-dir dist",
|
||||
"build:type": "tsc --declaration --emitDeclarationOnly --declarationDir dist",
|
||||
"release-build": "yarn build && yarn build:type",
|
||||
"fmt": "eslint --fix \"src/**/*.ts*\"",
|
||||
"lint": "eslint \"src/**/*.ts*\"",
|
||||
"test:unit-coverage": "c8 mocha --no-warnings 'tests/**/*.spec.ts'",
|
||||
"test:unit": "c8 --r lcov mocha --no-warnings 'tests/**/*.spec.ts' && node ../../scripts/coveragePathFixing.js client",
|
||||
"test:deno-unit": "swc tests --delete-dir-on-start --out-dir denoTestsDist && node ../../scripts/fixDenoTestExtension.js && deno test -A --import-map ../../denoImportMap.json denoTestsDist",
|
||||
"test:unit:watch": "mocha --no-warnings --watch --parallel 'tests/**/*.spec.ts'",
|
||||
"test:type": "tsc --noEmit",
|
||||
"test:test-type": "tsc --project tsconfig.test.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@discordeno/gateway": "18.0.0-alpha.1",
|
||||
"@discordeno/rest": "18.0.0-alpha.1",
|
||||
"@discordeno/utils": "18.0.0-alpha.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@discordeno/types": "18.0.0-alpha.1",
|
||||
"@swc/cli": "^0.1.57",
|
||||
"@swc/core": "^1.3.21",
|
||||
"@types/chai": "^4",
|
||||
"@types/mocha": "^10",
|
||||
"@types/node": "^18.11.9",
|
||||
"@types/sinon": "^10.0.13",
|
||||
"c8": "^7.12.0",
|
||||
"chai": "^4.3.7",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-config-discordeno": "*",
|
||||
"mocha": "^10.1.0",
|
||||
"sinon": "^15.0.0",
|
||||
"tsconfig": "*",
|
||||
"typescript": "^4.9.3"
|
||||
}
|
||||
}
|
||||
77
packages/client/src/Base.ts
Normal file
77
packages/client/src/Base.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import type { BigString } from './Client.js'
|
||||
|
||||
export class Base {
|
||||
/** Internal storage of the id done in bigint to preserve memory */
|
||||
_id: bigint
|
||||
|
||||
constructor(id: BigString) {
|
||||
this._id = BigInt(id)
|
||||
}
|
||||
|
||||
/** The snowflake id */
|
||||
get id(): string {
|
||||
return this._id.toString()
|
||||
}
|
||||
|
||||
set id(value: BigString) {
|
||||
this._id = BigInt(value)
|
||||
}
|
||||
|
||||
get createdAt(): number {
|
||||
return Number(this._id / 4194304n + 1420070400000n)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the timestamp in milliseconds associated with a Discord ID/snowflake.
|
||||
* @deprecated Recommend using Object.createdAt or Client.snowflakeToTimestamp if you want to get a timestamp from a id. This is not desired but supported only to keep a similar api to eris.
|
||||
*/
|
||||
static getCreatedAt(id: string): number {
|
||||
return Base.getDiscordEpoch(id) + 1420070400000
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of milliseconds since epoch represented by an ID/snowflake
|
||||
* @deprecated Recommend using Object.createdAt or Client.snowflakeToTimestamp if you want to get a timestamp from a id. This is not desired but supported only to keep a similar api to eris.
|
||||
*/
|
||||
static getDiscordEpoch(id: string): number {
|
||||
// @ts-expect-error some eris magic at play here
|
||||
return Math.floor(id / 4194304)
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `[${this.constructor.name} ${this.id}]`
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
const json = {
|
||||
id: this.id,
|
||||
createdAt: this.createdAt,
|
||||
}
|
||||
for (const prop of props) {
|
||||
// @ts-expect-error some eris magic at play here
|
||||
const value = this[prop]
|
||||
const type = typeof value
|
||||
if (value === undefined) {
|
||||
continue
|
||||
} else if ((type !== 'object' && type !== 'function' && type !== 'bigint') || value === null) {
|
||||
// @ts-expect-error some eris magic at play here
|
||||
json[prop] = value
|
||||
} else if (value.toJSON !== undefined) {
|
||||
// @ts-expect-error some eris magic at play here
|
||||
json[prop] = value.toJSON()
|
||||
} else if (value.values !== undefined) {
|
||||
// @ts-expect-error some eris magic at play here
|
||||
json[prop] = [...value.values()]
|
||||
} else if (type === 'bigint') {
|
||||
// @ts-expect-error some eris magic at play here
|
||||
json[prop] = value.toString()
|
||||
} else if (type === 'object') {
|
||||
// @ts-expect-error some eris magic at play here
|
||||
json[prop] = value
|
||||
}
|
||||
}
|
||||
return json
|
||||
}
|
||||
}
|
||||
|
||||
export default Base
|
||||
2345
packages/client/src/Client.ts
Normal file
2345
packages/client/src/Client.ts
Normal file
File diff suppressed because it is too large
Load Diff
140
packages/client/src/Collection.ts
Normal file
140
packages/client/src/Collection.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
export class Collection<K, V> extends Map<K, V> {
|
||||
limit: number | undefined;
|
||||
|
||||
set(key: K, value: V): this {
|
||||
// When this collection is limitd make sure we can add first
|
||||
if ((this.limit ?? this.limit === 0) && this.size >= this.limit) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return super.set(key, value);
|
||||
}
|
||||
|
||||
forceSet(key: K, value: V): this {
|
||||
return super.set(key, value);
|
||||
}
|
||||
|
||||
array(): V[] {
|
||||
return [...this.values()];
|
||||
}
|
||||
|
||||
/** Retrieve the value of the first element in this collection */
|
||||
first(): V | undefined {
|
||||
return this.values().next().value;
|
||||
}
|
||||
|
||||
last(): V | undefined {
|
||||
return [...this.values()][this.size - 1];
|
||||
}
|
||||
|
||||
random(): V | undefined {
|
||||
const array = [...this.values()];
|
||||
return array[Math.floor(Math.random() * array.length)];
|
||||
}
|
||||
|
||||
find(callback: (value: V, key: K) => boolean): V | undefined {
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!;
|
||||
if (callback(value, key)) return value;
|
||||
}
|
||||
}
|
||||
|
||||
filter(callback: (value: V, key: K) => boolean, returnArray?: true): V[];
|
||||
filter(callback: (value: V, key: K) => boolean, returnArray: false): Collection<K, V>;
|
||||
filter(callback: (value: V, key: K) => boolean, returnArray = true): Collection<K, V> | V[] {
|
||||
const relevant = new Collection<K, V>();
|
||||
this.forEach((value, key) => {
|
||||
if (callback(value, key)) relevant.set(key, value);
|
||||
});
|
||||
|
||||
return returnArray ? relevant.array() : relevant;
|
||||
}
|
||||
|
||||
map<T>(callback: (value: V, key: K) => T): T[] {
|
||||
const results = [];
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!;
|
||||
results.push(callback(value, key));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
some(callback: (value: V, key: K) => boolean): boolean {
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!;
|
||||
if (callback(value, key)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
every(callback: (value: V, key: K) => boolean): boolean {
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!;
|
||||
if (!callback(value, key)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
reduce<T>(callback: (accumulator: T, value: V, key: K) => T, initialValue?: T): T {
|
||||
let accumulator: T = initialValue!;
|
||||
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!;
|
||||
accumulator = callback(accumulator, value, key);
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a object to the collection.
|
||||
* @deprecated Recommend using Collection.set(). Keeping for the sake of Eris API.
|
||||
* @deprecated extra parameter. No longer used, keeping for sake of Eris API.
|
||||
*/
|
||||
add(obj: V & { id: K }, extra?: unknown, replace?: boolean): V {
|
||||
if (this.limit === 0) return obj;
|
||||
|
||||
const existing = this.get(obj.id);
|
||||
if (existing && !replace) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
this.set(obj.id, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
remove(obj: { id: K }): V | undefined {
|
||||
const item = this.get(obj.id);
|
||||
if (!item) return;
|
||||
|
||||
this.delete(obj.id);
|
||||
return item;
|
||||
}
|
||||
|
||||
update(obj: V & { id: K }, extra?: unknown, replace?: boolean): V {
|
||||
const item = this.get(obj.id);
|
||||
if (!item) {
|
||||
this.set(obj.id, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
// @ts-expect-error some eris magic at play here
|
||||
item.update?.(obj, extra);
|
||||
return item;
|
||||
}
|
||||
|
||||
toRecord(): Record<string, V> {
|
||||
const record: Record<string, V> = {};
|
||||
for (const [key, value] of this.entries()) {
|
||||
// @ts-expect-error should work fine
|
||||
const finalKey = typeof key === 'string' ? key : key.toString();
|
||||
record[finalKey] = value;
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
}
|
||||
|
||||
export default Collection;
|
||||
136
packages/client/src/Endpoints.ts
Normal file
136
packages/client/src/Endpoints.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import type { BigString } from './Client.js'
|
||||
|
||||
export const ORIGINAL_INTERACTION_RESPONSE = (appID: BigString, interactToken: string) => `/webhooks/${appID}/${interactToken}`
|
||||
export const COMMAND = (applicationID: BigString, commandID: BigString) => `/applications/${applicationID}/commands/${commandID}`
|
||||
export const COMMANDS = (applicationID: BigString) => `/applications/${applicationID}/commands`
|
||||
export const COMMAND_PERMISSIONS = (applicationID: BigString, guildID: BigString, commandID: BigString) =>
|
||||
`/applications/${applicationID}/guilds/${guildID}/commands/${commandID}/permissions`
|
||||
export const CHANNEL = (chanID: BigString) => `/channels/${chanID}`
|
||||
export const CHANNEL_BULK_DELETE = (chanID: BigString) => `/channels/${chanID}/messages/bulk-delete`
|
||||
export const CHANNEL_CALL_RING = (chanID: BigString) => `/channels/${chanID}/call/ring`
|
||||
export const CHANNEL_CROSSPOST = (chanID: BigString, msgID: BigString) => `/channels/${chanID}/messages/${msgID}/crosspost`
|
||||
export const CHANNEL_FOLLOW = (chanID: BigString) => `/channels/${chanID}/followers`
|
||||
export const CHANNEL_INVITES = (chanID: BigString) => `/channels/${chanID}/invites`
|
||||
export const CHANNEL_MESSAGE_REACTION = (chanID: BigString, msgID: BigString, reaction: string) =>
|
||||
`/channels/${chanID}/messages/${msgID}/reactions/${reaction}`
|
||||
export const CHANNEL_MESSAGE_REACTION_USER = (chanID: BigString, msgID: BigString, reaction: string, userID: BigString) =>
|
||||
`/channels/${chanID}/messages/${msgID}/reactions/${reaction}/${userID}`
|
||||
export const CHANNEL_MESSAGE_REACTIONS = (chanID: BigString, msgID: BigString) => `/channels/${chanID}/messages/${msgID}/reactions`
|
||||
export const CHANNEL_MESSAGE = (chanID: BigString, msgID: BigString) => `/channels/${chanID}/messages/${msgID}`
|
||||
export const CHANNEL_MESSAGES = (chanID: BigString) => `/channels/${chanID}/messages`
|
||||
export const CHANNEL_MESSAGES_SEARCH = (chanID: BigString) => `/channels/${chanID}/messages/search`
|
||||
export const CHANNEL_PERMISSION = (chanID: BigString, overID: BigString) => `/channels/${chanID}/permissions/${overID}`
|
||||
export const CHANNEL_PERMISSIONS = (chanID: BigString) => `/channels/${chanID}/permissions`
|
||||
export const CHANNEL_PIN = (chanID: BigString, msgID: BigString) => `/channels/${chanID}/pins/${msgID}`
|
||||
export const CHANNEL_PINS = (chanID: BigString) => `/channels/${chanID}/pins`
|
||||
export const CHANNEL_RECIPIENT = (groupID: BigString, userID: BigString) => `/channels/${groupID}/recipients/${userID}`
|
||||
export const CHANNEL_TYPING = (chanID: BigString) => `/channels/${chanID}/typing`
|
||||
export const CHANNEL_WEBHOOKS = (chanID: BigString) => `/channels/${chanID}/webhooks`
|
||||
export const CHANNELS = '/channels'
|
||||
export const CUSTOM_EMOJI_GUILD = (emojiID: BigString) => `/emojis/${emojiID}/guild`
|
||||
export const DISCOVERY_CATEGORIES = '/discovery/categories'
|
||||
export const DISCOVERY_VALIDATION = '/discovery/valid-term'
|
||||
export const GATEWAY = '/gateway'
|
||||
export const GATEWAY_BOT = '/gateway/bot'
|
||||
export const GUILD = (guildID: BigString) => `/guilds/${guildID}`
|
||||
export const GUILD_AUDIT_LOGS = (guildID: BigString) => `/guilds/${guildID}/audit-logs`
|
||||
export const GUILD_BAN = (guildID: BigString, memberID: BigString) => `/guilds/${guildID}/bans/${memberID}`
|
||||
export const GUILD_BANS = (guildID: BigString) => `/guilds/${guildID}/bans`
|
||||
export const GUILD_CHANNELS = (guildID: BigString) => `/guilds/${guildID}/channels`
|
||||
export const GUILD_COMMAND = (applicationID: BigString, guildID: BigString, commandID: BigString) =>
|
||||
`/applications/${applicationID}/guilds/${guildID}/commands/${commandID}`
|
||||
export const GUILD_COMMAND_PERMISSIONS = (applicationID: BigString, guildID: BigString) =>
|
||||
`/applications/${applicationID}/guilds/${guildID}/commands/permissions`
|
||||
export const GUILD_COMMANDS = (applicationID: BigString, guildID: BigString) => `/applications/${applicationID}/guilds/${guildID}/commands`
|
||||
export const GUILD_DISCOVERY = (guildID: BigString) => `/guilds/${guildID}/discovery-metadata`
|
||||
export const GUILD_DISCOVERY_CATEGORY = (guildID: BigString, categoryID: BigString) => `/guilds/${guildID}/discovery-categories/${categoryID}`
|
||||
export const GUILD_EMOJI = (guildID: BigString, emojiID: BigString) => `/guilds/${guildID}/emojis/${emojiID}`
|
||||
export const GUILD_EMOJIS = (guildID: BigString) => `/guilds/${guildID}/emojis`
|
||||
export const GUILD_INTEGRATION = (guildID: BigString, inteID: BigString) => `/guilds/${guildID}/integrations/${inteID}`
|
||||
export const GUILD_INTEGRATION_SYNC = (guildID: BigString, inteID: BigString) => `/guilds/${guildID}/integrations/${inteID}/sync`
|
||||
export const GUILD_INTEGRATIONS = (guildID: BigString) => `/guilds/${guildID}/integrations`
|
||||
export const GUILD_INVITES = (guildID: BigString) => `/guilds/${guildID}/invites`
|
||||
export const GUILD_VANITY_URL = (guildID: BigString) => `/guilds/${guildID}/vanity-url`
|
||||
export const GUILD_MEMBER = (guildID: BigString, memberID: BigString) => `/guilds/${guildID}/members/${memberID}`
|
||||
export const GUILD_MEMBER_NICK = (guildID: BigString, memberID: BigString) => `/guilds/${guildID}/members/${memberID}/nick`
|
||||
export const GUILD_MEMBER_ROLE = (guildID: BigString, memberID: BigString, roleID: BigString) =>
|
||||
`/guilds/${guildID}/members/${memberID}/roles/${roleID}`
|
||||
export const GUILD_MEMBERS = (guildID: BigString) => `/guilds/${guildID}/members`
|
||||
export const GUILD_MEMBERS_SEARCH = (guildID: BigString) => `/guilds/${guildID}/members/search`
|
||||
export const GUILD_MESSAGES_SEARCH = (guildID: BigString) => `/guilds/${guildID}/messages/search`
|
||||
export const GUILD_PREVIEW = (guildID: BigString) => `/guilds/${guildID}/preview`
|
||||
export const GUILD_PRUNE = (guildID: BigString) => `/guilds/${guildID}/prune`
|
||||
export const GUILD_ROLE = (guildID: BigString, roleID: BigString) => `/guilds/${guildID}/roles/${roleID}`
|
||||
export const GUILD_ROLES = (guildID: BigString) => `/guilds/${guildID}/roles`
|
||||
export const GUILD_STICKER = (guildID: BigString, stickerID: BigString) => `/guilds/${guildID}/stickers/${stickerID}`
|
||||
export const GUILD_STICKERS = (guildID: BigString) => `/guilds/${guildID}/stickers`
|
||||
export const GUILD_TEMPLATE = (code: string) => `/guilds/templates/${code}`
|
||||
export const GUILD_TEMPLATES = (guildID: BigString) => `/guilds/${guildID}/templates`
|
||||
export const GUILD_TEMPLATE_GUILD = (guildID: BigString, code: string) => `/guilds/${guildID}/templates/${code}`
|
||||
export const GUILD_VOICE_REGIONS = (guildID: BigString) => `/guilds/${guildID}/regions`
|
||||
export const GUILD_WEBHOOKS = (guildID: BigString) => `/guilds/${guildID}/webhooks`
|
||||
export const GUILD_WELCOME_SCREEN = (guildID: BigString) => `/guilds/${guildID}/welcome-screen`
|
||||
export const GUILD_WIDGET = (guildID: BigString) => `/guilds/${guildID}/widget.json`
|
||||
export const GUILD_WIDGET_SETTINGS = (guildID: BigString) => `/guilds/${guildID}/widget`
|
||||
export const GUILD_VOICE_STATE = (guildID: BigString, user: BigString) => `/guilds/${guildID}/voice-states/${user}`
|
||||
export const GUILDS = '/guilds'
|
||||
export const INTERACTION_RESPOND = (interactID: BigString, interactToken: string) => `/interactions/${interactID}/${interactToken}/callback`
|
||||
export const INVITE = (inviteID: string) => `/invites/${inviteID}`
|
||||
export const OAUTH2_APPLICATION = (appID: BigString) => `/oauth2/applications/${appID}`
|
||||
export const STAGE_INSTANCE = (channelID: BigString) => `/stage-instances/${channelID}`
|
||||
export const STAGE_INSTANCES = '/stage-instances'
|
||||
export const STICKER = (stickerID: BigString) => `/stickers/${stickerID}`
|
||||
export const STICKER_PACKS = '/sticker-packs'
|
||||
export const THREAD_MEMBER = (channelID: BigString, userID: BigString) => `/channels/${channelID}/thread-members/${userID}`
|
||||
export const THREAD_MEMBERS = (channelID: BigString) => `/channels/${channelID}/thread-members`
|
||||
export const THREAD_WITH_MESSAGE = (channelID: BigString, msgID: BigString) => `/channels/${channelID}/messages/${msgID}/threads`
|
||||
export const THREAD_WITHOUT_MESSAGE = (channelID: BigString) => `/channels/${channelID}/threads`
|
||||
export const THREADS_ACTIVE = (channelID: BigString) => `/channels/${channelID}/threads/active`
|
||||
export const THREADS_ARCHIVED = (channelID: BigString, type: string) => `/channels/${channelID}/threads/archived/${type}`
|
||||
export const THREADS_ARCHIVED_JOINED = (channelID: BigString) => `/channels/${channelID}/users/@me/threads/archived/private`
|
||||
export const THREADS_GUILD_ACTIVE = (guildID: BigString) => `/guilds/${guildID}/threads/active`
|
||||
export const USER = (userID: BigString) => `/users/${userID}`
|
||||
export const USER_BILLING = (userID: BigString) => `/users/${userID}/billing`
|
||||
export const USER_BILLING_PAYMENTS = (userID: BigString) => `/users/${userID}/billing/payments`
|
||||
export const USER_BILLING_PREMIUM_SUBSCRIPTION = (userID: BigString) => `/users/${userID}/billing/premium-subscription`
|
||||
export const USER_CHANNELS = (userID: BigString) => `/users/${userID}/channels`
|
||||
export const USER_CONNECTIONS = (userID: BigString) => `/users/${userID}/connections`
|
||||
export const USER_CONNECTION_PLATFORM = (userID: BigString, platform: string, id: string) => `/users/${userID}/connections/${platform}/${id}`
|
||||
export const USER_GUILD = (userID: BigString, guildID: BigString) => `/users/${userID}/guilds/${guildID}`
|
||||
export const USER_GUILDS = (userID: BigString) => `/users/${userID}/guilds`
|
||||
export const USER_MFA_CODES = (userID: BigString) => `/users/${userID}/mfa/codes`
|
||||
export const USER_MFA_TOTP_DISABLE = (userID: BigString) => `/users/${userID}/mfa/totp/disable`
|
||||
export const USER_MFA_TOTP_ENABLE = (userID: BigString) => `/users/${userID}/mfa/totp/enable`
|
||||
export const USER_NOTE = (userID: BigString, targetID: BigString) => `/users/${userID}/note/${targetID}`
|
||||
export const USER_PROFILE = (userID: BigString) => `/users/${userID}/profile`
|
||||
export const USER_RELATIONSHIP = (userID: BigString, relID: BigString) => `/users/${userID}/relationships/${relID}`
|
||||
export const USER_SETTINGS = (userID: BigString) => `/users/${userID}/settings`
|
||||
export const USERS = '/users'
|
||||
export const VOICE_REGIONS = '/voice/regions'
|
||||
export const WEBHOOK = (hookID: BigString) => `/webhooks/${hookID}`
|
||||
export const WEBHOOK_MESSAGE = (hookID: BigString, token: string, msgID: BigString) => `/webhooks/${hookID}/${token}/messages/${msgID}`
|
||||
export const WEBHOOK_SLACK = (hookID: BigString) => `/webhooks/${hookID}/slack`
|
||||
export const WEBHOOK_TOKEN = (hookID: BigString, token: string) => `/webhooks/${hookID}/${token}`
|
||||
export const WEBHOOK_TOKEN_SLACK = (hookID: BigString, token: string) => `/webhooks/${hookID}/${token}/slack`
|
||||
|
||||
// CDN Endpoints
|
||||
export const ACHIEVEMENT_ICON = (applicationID: BigString, achievementID: BigString, icon: string) =>
|
||||
`/app-assets/${applicationID}/achievements/${achievementID}/icons/${icon}`
|
||||
export const APPLICATION_ASSET = (applicationID: BigString, asset: string) => `/app-assets/${applicationID}/${asset}`
|
||||
export const APPLICATION_ICON = (applicationID: BigString, icon: string) => `/app-icons/${applicationID}/${icon}`
|
||||
export const BANNER = (guildOrUserID: BigString, hash: string) => `/banners/${guildOrUserID}/${hash}`
|
||||
export const CHANNEL_ICON = (chanID: BigString, chanIcon: string) => `/channel-icons/${chanID}/${chanIcon}`
|
||||
export const CUSTOM_EMOJI = (emojiID: BigString) => `/emojis/${emojiID}`
|
||||
export const DEFAULT_USER_AVATAR = (userDiscriminator: string) => `/embed/avatars/${userDiscriminator}`
|
||||
export const GUILD_AVATAR = (guildID: BigString, userID: BigString, guildAvatar: string) =>
|
||||
`/guilds/${guildID}/users/${userID}/avatars/${guildAvatar}`
|
||||
export const GUILD_DISCOVERY_SPLASH = (guildID: BigString, guildDiscoverySplash: string) => `/discovery-splashes/${guildID}/${guildDiscoverySplash}`
|
||||
export const GUILD_ICON = (guildID: BigString, guildIcon: string) => `/icons/${guildID}/${guildIcon}`
|
||||
export const GUILD_SPLASH = (guildID: BigString, guildSplash: string) => `/splashes/${guildID}/${guildSplash}`
|
||||
export const ROLE_ICON = (roleID: BigString, roleIcon: string) => `/role-icons/${roleID}/${roleIcon}`
|
||||
export const TEAM_ICON = (teamID: BigString, teamIcon: string) => `/team-icons/${teamID}/${teamIcon}`
|
||||
export const USER_AVATAR = (userID: BigString, userAvatar: string) => `/avatars/${userID}/${userAvatar}`
|
||||
|
||||
// Client Endpoints
|
||||
export const MESSAGE_LINK = (guildID: BigString, channelID: BigString, messageID: BigString) => `/channels/${guildID}/${channelID}/${messageID}`
|
||||
117
packages/client/src/RequestHandler.ts
Normal file
117
packages/client/src/RequestHandler.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import type { RequestMethods, RestManager } from '@discordeno/rest'
|
||||
import { createRestManager } from '@discordeno/rest'
|
||||
import Base from './Base.js'
|
||||
import type Client from './Client.js'
|
||||
import type { FileContent, RequestHandlerOptions } from './typings.js'
|
||||
|
||||
// TODO: make dynamic based on package.json file
|
||||
const version = '18.0.0-alpha.1'
|
||||
|
||||
export class RequestHandler {
|
||||
/** The client manager. */
|
||||
client: Client
|
||||
/** The options this manager was configured with. */
|
||||
options: RequestHandlerOptions
|
||||
/** The user agent used to make requests. */
|
||||
userAgent: string
|
||||
/** The rate limits currently in cache. */
|
||||
ratelimits: Record<string, unknown>
|
||||
/** The latency information for this manager. */
|
||||
latencyRef: {
|
||||
latency: number
|
||||
raw: number[]
|
||||
timeOffset: number
|
||||
timeOffsets: number[]
|
||||
lastTimeOffsetCheck: number
|
||||
}
|
||||
|
||||
/** Whether or not the manager is globally blocked. */
|
||||
globalBlock: boolean
|
||||
/** The ready queue */
|
||||
readyQueue: unknown[]
|
||||
/** The internal rest manager from dd. */
|
||||
discordeno: RestManager
|
||||
|
||||
constructor(client: Client, options: RequestHandlerOptions) {
|
||||
this.options = options = Object.assign(
|
||||
{
|
||||
// agent: client.options.agent || null,
|
||||
agent: null,
|
||||
baseURL: client.BASE_URL,
|
||||
decodeReasons: true,
|
||||
disableLatencyCompensation: false,
|
||||
domain: 'discord.com',
|
||||
// latencyThreshold: client.options.latencyThreshold || 30000,
|
||||
latencyThreshold: 30000,
|
||||
// ratelimiterOffset: client.options.ratelimiterOffset || 0,
|
||||
ratelimiterOffset: 0,
|
||||
// requestTimeout: client.options.requestTimeout || 15000,
|
||||
requestTimeout: 15000,
|
||||
},
|
||||
options,
|
||||
)
|
||||
|
||||
this.client = client
|
||||
this.discordeno = createRestManager({
|
||||
token: this.client.token,
|
||||
baseUrl: options.baseURL ?? this.client.options.proxyURL,
|
||||
})
|
||||
|
||||
this.userAgent = `DiscordBot (https://github.com/discordeno/discordeno, ${version})`
|
||||
this.ratelimits = {}
|
||||
this.latencyRef = {
|
||||
latency: this.options.ratelimiterOffset ?? 0,
|
||||
raw: new Array(10).fill(this.options.ratelimiterOffset),
|
||||
timeOffset: 0,
|
||||
timeOffsets: new Array(10).fill(0),
|
||||
lastTimeOffsetCheck: 0,
|
||||
}
|
||||
this.globalBlock = false
|
||||
this.readyQueue = []
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `.client` instead
|
||||
*/
|
||||
get _client(): Client {
|
||||
return this.client
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Useless, handled by discordeno itself. Kept for Eris api compatibility.
|
||||
*/
|
||||
globalUnblock(): void {}
|
||||
|
||||
warnUser(): void {
|
||||
// LOG IT ENOUGH TIMES TO MAKE USER SEE IT CLEARLY
|
||||
for (let i = 0; i < 10; i++) {
|
||||
console.warn(
|
||||
'[WARNING] Using internal RestManager since no proxy rest manager was provided. THIS IS NOT RECOMMENDED. Please use a proxy rest manager. If you need help setting it up, join discord.gg/ddeno',
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an API request
|
||||
* @deprecated Use a proxy rest instead.
|
||||
*/
|
||||
async request(method: RequestMethods, url: string, auth?: boolean, body?: any, file?: FileContent): Promise<unknown> {
|
||||
if (file) body.file = file
|
||||
|
||||
return await this.discordeno.makeRequest(method, url, body)
|
||||
}
|
||||
|
||||
routefy(url: string, method: RequestMethods): string {
|
||||
return this.discordeno.simplifyUrl(url, method)
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return '[RequestHandler]'
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return Base.prototype.toJSON.call(this, ['globalBlock', 'latencyRef', 'options', 'ratelimits', 'readyQueue', 'userAgent', ...props])
|
||||
}
|
||||
}
|
||||
|
||||
export default RequestHandler
|
||||
146
packages/client/src/Structures/Invite.ts
Normal file
146
packages/client/src/Structures/Invite.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
/* eslint-disable no-useless-call */
|
||||
|
||||
import type { DiscordInvite, DiscordInviteCreate, DiscordMemberWithUser, TargetTypes } from '@discordeno/types'
|
||||
import Base from '../Base.js'
|
||||
import type Client from '../Client.js'
|
||||
import type Channel from './channels/Channel.js'
|
||||
import Guild from './guilds/Guild.js'
|
||||
import Member from './guilds/Member.js'
|
||||
import User from './users/User.js'
|
||||
|
||||
export class Invite {
|
||||
/** The client object. */
|
||||
client: Client
|
||||
/** The invite code (unique Id) */
|
||||
code: string
|
||||
/** The channel this invite is for */
|
||||
channel?: Channel
|
||||
/** The guild this invite is for. */
|
||||
guild?: Guild
|
||||
/** The user who created this invite. */
|
||||
inviter?: User
|
||||
/** The amount of times this invite has been used. */
|
||||
uses: number | null = null
|
||||
/** The amount of times this invite can be used. */
|
||||
maxUses: number | null = null
|
||||
/** How long the invite is valid for (in seconds) */
|
||||
maxAge: number | null = null
|
||||
/** Whether or not the invite is temporary (invited users will be kicked on disconnect unless they're assigned a role) */
|
||||
temporary: boolean = false
|
||||
/** The time at which the invite was created */
|
||||
createdAt?: number
|
||||
|
||||
presenceCount?: number | null
|
||||
memberCount?: number | null
|
||||
|
||||
stageInstance?: {
|
||||
members: Member[]
|
||||
participantCount: number
|
||||
speakerCount: number
|
||||
topic: string
|
||||
} | null
|
||||
|
||||
targetApplicationID?: string | null
|
||||
targetType?: TargetTypes | null
|
||||
targetUser?: User | null
|
||||
|
||||
constructor(data: DiscordInvite | DiscordInviteCreate, client: Client) {
|
||||
// super();
|
||||
this.client = client
|
||||
this.code = data.code
|
||||
this.channel = data.channel
|
||||
|
||||
if (data.inviter) {
|
||||
this.inviter = new User(data.inviter, client)
|
||||
client.users.set(this.inviter.id, this.inviter)
|
||||
}
|
||||
|
||||
if (this.isInviteCreate(data)) {
|
||||
this.uses = data.uses !== undefined ? data.uses : null
|
||||
this.maxUses = data.max_uses !== undefined ? data.max_uses : null
|
||||
this.maxAge = data.max_age !== undefined ? data.max_age : null
|
||||
this.temporary = data.temporary !== undefined ? data.temporary : false
|
||||
this.createdAt = Date.parse(data.created_at)
|
||||
} else {
|
||||
if (data.guild) {
|
||||
if (client.guilds.has(data.guild.id!)) {
|
||||
if (data.channel) {
|
||||
// @ts-expect-error should work i think dumb partials
|
||||
const channel = new GuildChannel(data.channel, client)
|
||||
client.guilds.get(data.guild.id!)?.channels.set(channel.id, channel)
|
||||
}
|
||||
} else {
|
||||
this.guild = new Guild(data.guild, client)
|
||||
}
|
||||
}
|
||||
|
||||
this.presenceCount = data.approximate_presence_count !== undefined ? data.approximate_presence_count : null
|
||||
this.memberCount = data.approximate_member_count !== undefined ? data.approximate_member_count : null
|
||||
if (data.stage_instance !== undefined) {
|
||||
this.stageInstance = {
|
||||
members: data.stage_instance.members.map((m) => {
|
||||
const member = new Member(m as DiscordMemberWithUser, this.guild, client)
|
||||
this.guild.members.set(member.id, member)
|
||||
return member
|
||||
}),
|
||||
participantCount: data.stage_instance.participant_count,
|
||||
speakerCount: data.stage_instance.speaker_count,
|
||||
topic: data.stage_instance.topic,
|
||||
}
|
||||
} else {
|
||||
this.stageInstance = null
|
||||
}
|
||||
}
|
||||
|
||||
this.targetApplicationID = data.target_application !== undefined ? data.target_application.id : null
|
||||
this.targetType = data.target_type !== undefined ? data.target_type : null
|
||||
this.targetUser = data.target_user !== undefined ? new User(data.target_user, client) : null
|
||||
if (this.targetUser) client.users.set(this.targetUser.id, this.targetUser)
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use .client
|
||||
*/
|
||||
get _client(): Client {
|
||||
return this.client
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use .createdAt
|
||||
*/
|
||||
get _createdAt(): number | undefined {
|
||||
return this.createdAt
|
||||
}
|
||||
|
||||
/** Delete the invite */
|
||||
async delete(reason?: string): Promise<void> {
|
||||
return await this.client.deleteInvite.call(this.client, this.code, reason)
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `[Invite ${this.code}]`
|
||||
}
|
||||
|
||||
toJSON(props = []): Record<string, any> {
|
||||
return Base.prototype.toJSON([
|
||||
'channel',
|
||||
'code',
|
||||
'createdAt',
|
||||
'guild',
|
||||
'maxAge',
|
||||
'maxUses',
|
||||
'memberCount',
|
||||
'presenceCount',
|
||||
'revoked',
|
||||
'temporary',
|
||||
'uses',
|
||||
...props,
|
||||
])
|
||||
}
|
||||
|
||||
isInviteCreate(data: DiscordInvite | DiscordInviteCreate): data is DiscordInviteCreate {
|
||||
return Reflect.has(data, 'created_at')
|
||||
}
|
||||
}
|
||||
|
||||
export default Invite
|
||||
438
packages/client/src/Structures/Message.ts
Normal file
438
packages/client/src/Structures/Message.ts
Normal file
@@ -0,0 +1,438 @@
|
||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable no-useless-call */
|
||||
|
||||
import type { DiscordEmbed, DiscordMessageActivity, DiscordStickerItem } from '@discordeno/types'
|
||||
import {
|
||||
MessageTypes,
|
||||
type DiscordApplication,
|
||||
type DiscordAttachment,
|
||||
type DiscordMemberWithUser,
|
||||
type DiscordMessage,
|
||||
type DiscordMessageComponents,
|
||||
type InteractionTypes,
|
||||
} from '@discordeno/types'
|
||||
import Base from '../Base.js'
|
||||
import type Client from '../Client.js'
|
||||
import { MESSAGE_LINK } from '../Endpoints.js'
|
||||
import { MessageFlags, type GetMessageReactionOptions, type MessageContentEdit, type MessageWebhookContent } from '../typings.js'
|
||||
import type NewsChannel from './channels/News.js'
|
||||
import type PrivateChannel from './channels/Private.js'
|
||||
import type TextChannel from './channels/Text.js'
|
||||
import type NewsThreadChannel from './channels/threads/NewsThread.js'
|
||||
import type PublicThreadChannel from './channels/threads/PublicThread.js'
|
||||
import type Guild from './guilds/Guild.js'
|
||||
import Member from './guilds/Member.js'
|
||||
import User from './users/User.js'
|
||||
|
||||
export class Message extends Base {
|
||||
/** The client manager. */
|
||||
client: Client
|
||||
/** Timestamp of message creation */
|
||||
timestamp: number
|
||||
/** The type of the message */
|
||||
type: MessageTypes
|
||||
/** The channel the message is in. Can be partial with only the id if the channel is not cached. */
|
||||
channel: PrivateChannel | TextChannel | NewsChannel | { id: string }
|
||||
/** The message content. */
|
||||
content: string
|
||||
/** An object containing the reactions on the message. Each key is a reaction emoji and each value is an object with properties `me` (Boolean) and `count` (Number) for that specific reaction emoji. */
|
||||
reactions: Record<string, { me: boolean; count: number }>
|
||||
/** The ID of the guild this message is in (undefined if in DMs) */
|
||||
guildID?: string
|
||||
/** ID of the webhook that sent the message */
|
||||
webhookID?: string
|
||||
/** An object containing the reference to the original message if it is a crossposted message or reply */
|
||||
messageReference?: {
|
||||
/** The id of the original message this message was crossposted from */
|
||||
messageID?: string
|
||||
/** The id of the channel this message was crossposted from */
|
||||
channelID?: string
|
||||
/** The id of the guild this message was crossposted from */
|
||||
guildID?: string
|
||||
} | null
|
||||
|
||||
/** The flags that are enabled on this message. */
|
||||
flags: number
|
||||
/** The message author */
|
||||
author: User
|
||||
/** The message author with server-specific data */
|
||||
member?: Member
|
||||
/** The message that was replied to. If undefined, message data was not received. If null, the message was deleted. */
|
||||
referencedMessage?: Message | null
|
||||
/** An object containing info about the interaction the message is responding to, if applicable */
|
||||
interaction?: {
|
||||
/** The id of the interaction */
|
||||
id: string
|
||||
/** The type of interaction */
|
||||
type: InteractionTypes
|
||||
/** The name of the command */
|
||||
name: string
|
||||
/** The user who invoked the interaction */
|
||||
user: User
|
||||
/** The member who invoked the interaction */
|
||||
member?: Member
|
||||
}
|
||||
|
||||
/** Array of mentioned users */
|
||||
mentions: User[] = []
|
||||
/** Array of mentioned roles' ids */
|
||||
roleMentions: string[] = []
|
||||
/** Array of attachments */
|
||||
attachments: DiscordAttachment[] = []
|
||||
/** Array of embeds */
|
||||
embeds: DiscordEmbed[] = []
|
||||
/** The stickers sent with the message */
|
||||
stickerItems: DiscordStickerItem[] = []
|
||||
/** An array of component objects */
|
||||
components: DiscordMessageComponents = []
|
||||
/** The activity specified in the message */
|
||||
activity?: DiscordMessageActivity
|
||||
/** The application of the activity in the message */
|
||||
application?: Partial<DiscordApplication>
|
||||
/** The ID of the interaction's application */
|
||||
applicationID?: string
|
||||
/** Timestamp of latest message edit */
|
||||
editedTimestamp?: number
|
||||
/** Whether the message mentions everyone/here or not */
|
||||
mentionEveryone: boolean = false
|
||||
/** Whether the message is pinned or not */
|
||||
pinned: boolean = false
|
||||
/** Whether to play the message using TTS or not */
|
||||
tts: boolean = false
|
||||
|
||||
constructor(data: DiscordMessage, client: Client) {
|
||||
super(data.id)
|
||||
|
||||
this.client = client
|
||||
this.timestamp = Date.parse(data.timestamp)
|
||||
|
||||
this.type = data.type || MessageTypes.Default
|
||||
this.timestamp = Date.parse(data.timestamp)
|
||||
this.channel = this.client.getChannel(data.channel_id) || {
|
||||
id: data.channel_id,
|
||||
}
|
||||
this.content = ''
|
||||
this.reactions = {}
|
||||
this.guildID = data.guild_id
|
||||
this.webhookID = data.webhook_id
|
||||
|
||||
if (data.message_reference) {
|
||||
this.messageReference = {
|
||||
messageID: data.message_reference.message_id,
|
||||
channelID: data.message_reference.channel_id,
|
||||
guildID: data.message_reference.guild_id,
|
||||
}
|
||||
} else {
|
||||
this.messageReference = null
|
||||
}
|
||||
|
||||
this.flags = data.flags || 0
|
||||
|
||||
this.author = new User(data.author, client)
|
||||
if (!data.webhook_id) {
|
||||
this.client.users.set(this.author.id, this.author)
|
||||
}
|
||||
|
||||
if (data.referenced_message) {
|
||||
const channel = this.client.getChannel(data.referenced_message.channel_id) as TextChannel
|
||||
this.referencedMessage = new Message(data.referenced_message, this.client)
|
||||
|
||||
if (channel) {
|
||||
channel.messages.set(this.referencedMessage.id, this.referencedMessage)
|
||||
}
|
||||
} else {
|
||||
this.referencedMessage = data.referenced_message
|
||||
}
|
||||
|
||||
if (data.interaction) {
|
||||
this.interaction = {
|
||||
id: data.interaction.id,
|
||||
type: data.interaction.type,
|
||||
name: data.interaction.name,
|
||||
user: new User(data.interaction.user, client),
|
||||
}
|
||||
|
||||
if (data.interaction.member) {
|
||||
data.interaction.member.user = data.interaction.user
|
||||
|
||||
if (this.guild) {
|
||||
this.interaction.member = new Member(
|
||||
// @ts-expect-error some eris magic at play here
|
||||
data.interaction.member,
|
||||
this.guild,
|
||||
client,
|
||||
)
|
||||
this.guild.members.set(this.interaction.member.id, this.interaction.member)
|
||||
} else {
|
||||
// @ts-expect-error some eris magic at play here
|
||||
interactionMember = data.interaction.member
|
||||
}
|
||||
} else if (this.guild?.members.has(data.interaction.user.id)) {
|
||||
this.interaction.member = this.guild.members.get(data.interaction.user.id)
|
||||
}
|
||||
}
|
||||
|
||||
if (this.guild) {
|
||||
if (data.member) {
|
||||
data.member.user = data.author
|
||||
this.member = new Member(data.member as DiscordMemberWithUser, this.guild, client)
|
||||
this.guild.members.set(this.member.id, this.member)
|
||||
} else if (this.guild.members.has(this.author.id)) {
|
||||
this.member = this.guild.members.get(this.author.id)
|
||||
}
|
||||
}
|
||||
|
||||
this.update(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `.client` instead.
|
||||
*/
|
||||
get _client(): Client {
|
||||
return this.client
|
||||
}
|
||||
|
||||
get guild(): Guild | undefined {
|
||||
return this.guildID ? this.client.guilds.get(this.guildID) : undefined
|
||||
}
|
||||
|
||||
update(data: DiscordMessage) {
|
||||
if (data.pinned !== undefined) this.pinned = !!data.pinned
|
||||
|
||||
if (data.tts !== undefined) this.tts = data.tts
|
||||
if (data.attachments !== undefined) this.attachments = data.attachments
|
||||
if (data.embeds !== undefined) this.embeds = data.embeds
|
||||
if (data.flags !== undefined) this.flags = data.flags
|
||||
if (data.activity !== undefined) this.activity = data.activity
|
||||
if (data.components !== undefined) this.components = data.components
|
||||
if (data.application !== undefined) this.application = data.application
|
||||
|
||||
if (data.edited_timestamp) this.editedTimestamp = Date.parse(data.edited_timestamp)
|
||||
if (data.application_id !== undefined) this.applicationID = data.application_id
|
||||
if (data.sticker_items !== undefined) this.stickerItems = data.sticker_items
|
||||
|
||||
if (data.content !== undefined) {
|
||||
this.content = data.content || ''
|
||||
this.mentionEveryone = !!data.mention_everyone
|
||||
this.mentions = []
|
||||
|
||||
for (const mention of data.mentions ?? []) {
|
||||
const user = new User(mention, this.client)
|
||||
this.client.users.set(user.id, user)
|
||||
|
||||
if (mention.member && this.guild) {
|
||||
mention.member.user = mention
|
||||
this.guild.members.set(mention.id, new Member(mention.member as DiscordMemberWithUser, this.guild, this.client))
|
||||
}
|
||||
}
|
||||
|
||||
if (data.mention_roles) this.roleMentions = data.mention_roles
|
||||
}
|
||||
|
||||
if (data.reactions) {
|
||||
for (const reaction of data.reactions ?? []) {
|
||||
this.reactions[reaction.emoji.id ? `${reaction.emoji.name}:${reaction.emoji.id}` : reaction.emoji.name!] = {
|
||||
count: reaction.count,
|
||||
me: reaction.me,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get channelMentions(): string[] {
|
||||
return (this.content.match(/<#[0-9]+>/g) ?? []).map((mention) => mention.substring(2, mention.length - 1))
|
||||
}
|
||||
|
||||
get cleanContent() {
|
||||
let cleanContent = this.content?.replace(/<a?(:\w+:)[0-9]+>/g, '$1') || ''
|
||||
|
||||
let authorName = this.author.username
|
||||
if (this.guild) {
|
||||
const member = this.guild.members.get(this.author.id)
|
||||
if (member?.nick) {
|
||||
authorName = member.nick
|
||||
}
|
||||
}
|
||||
cleanContent = cleanContent.replace(new RegExp(`<@!?${this.author.id}>`, 'g'), '@\u200b' + authorName)
|
||||
|
||||
if (this.mentions) {
|
||||
this.mentions.forEach((mention) => {
|
||||
if (this.guild) {
|
||||
const member = this.guild.members.get(mention.id)
|
||||
if (member?.nick) {
|
||||
cleanContent = cleanContent.replace(new RegExp(`<@!?${mention.id}>`, 'g'), '@\u200b' + member.nick)
|
||||
}
|
||||
}
|
||||
cleanContent = cleanContent.replace(new RegExp(`<@!?${mention.id}>`, 'g'), '@\u200b' + mention.username)
|
||||
})
|
||||
}
|
||||
|
||||
if (this.guild && this.roleMentions) {
|
||||
for (const roleID of this.roleMentions) {
|
||||
const role = this.guild.roles.get(roleID)
|
||||
const roleName = role ? role.name : 'deleted-role'
|
||||
cleanContent = cleanContent.replace(new RegExp(`<@&${roleID}>`, 'g'), '@\u200b' + roleName)
|
||||
}
|
||||
}
|
||||
|
||||
this.channelMentions.forEach((id) => {
|
||||
const channel = this.client.getChannel(id) as TextChannel
|
||||
if (channel?.name && channel.mention) {
|
||||
cleanContent = cleanContent.replace(channel.mention, '#' + channel.name)
|
||||
}
|
||||
})
|
||||
|
||||
return cleanContent.replace(/@everyone/g, '@\u200beveryone').replace(/@here/g, '@\u200bhere')
|
||||
}
|
||||
|
||||
get jumpLink() {
|
||||
return `${this.client.CLIENT_URL}${MESSAGE_LINK(this.guildID ?? '@me', this.channel.id, this.id)}`
|
||||
}
|
||||
|
||||
/** Add a reaction to a message */
|
||||
async addReaction(reaction: string): Promise<void> {
|
||||
if (this.flags & MessageFlags.EPHEMERAL) {
|
||||
throw new Error('Ephemeral messages cannot have reactions')
|
||||
}
|
||||
return await this.client.addMessageReaction.call(this.client, this.channel.id, this.id, reaction)
|
||||
}
|
||||
|
||||
/** Create a thread with this message */
|
||||
async createThreadWithMessage(options: {
|
||||
name: string
|
||||
autoArchiveDuration: 60 | 1440 | 4320 | 10080
|
||||
}): Promise<NewsThreadChannel | PublicThreadChannel> {
|
||||
return await this.client.createThreadWithMessage.call(this.client, this.channel.id, this.id, options)
|
||||
}
|
||||
|
||||
/** Crosspost (publish) a message to subscribed channels (NewsChannel only) */
|
||||
async crosspost(): Promise<Message> {
|
||||
if (this.flags & MessageFlags.EPHEMERAL) {
|
||||
throw new Error('Ephemeral messages cannot be crossposted')
|
||||
}
|
||||
|
||||
return await this.client.crosspostMessage.call(this.client, this.channel.id, this.id)
|
||||
}
|
||||
|
||||
/** Delete the message */
|
||||
async delete(reason?: string): Promise<void> {
|
||||
if (this.flags & MessageFlags.EPHEMERAL) {
|
||||
throw new Error('Ephemeral messages cannot be deleted')
|
||||
}
|
||||
|
||||
return await this.client.deleteMessage.call(this.client, this.channel.id, this.id, reason)
|
||||
}
|
||||
|
||||
/** Delete the message as a webhook */
|
||||
async deleteWebhook(token: string): Promise<void> {
|
||||
if (!this.webhookID) throw new Error('Message is not a webhook')
|
||||
if (this.flags & MessageFlags.EPHEMERAL) throw new Error('Ephemeral messages cannot be deleted')
|
||||
|
||||
return await this.client.deleteWebhookMessage.call(this.client, this.webhookID, token, this.id)
|
||||
}
|
||||
|
||||
/** Edit the message */
|
||||
async edit(content: MessageContentEdit): Promise<Message> {
|
||||
if (this.flags & MessageFlags.EPHEMERAL) {
|
||||
throw new Error('Ephemeral messages cannot be edited via this method')
|
||||
}
|
||||
|
||||
return await this.client.editMessage.call(this.client, this.channel.id, this.id, content)
|
||||
}
|
||||
|
||||
/** Edit the message as a webhook */
|
||||
async editWebhook(token: string, options: MessageWebhookContent): Promise<Message> {
|
||||
if (!this.webhookID) {
|
||||
throw new Error('Message is not a webhook')
|
||||
}
|
||||
|
||||
return await this.client.editWebhookMessage.call(this.client, this.webhookID, token, this.id, options)
|
||||
}
|
||||
|
||||
/** Get a list of users who reacted with a specific reaction */
|
||||
async getReaction(reaction: string, options?: GetMessageReactionOptions): Promise<User[]> {
|
||||
if (this.flags & MessageFlags.EPHEMERAL) {
|
||||
throw new Error('Ephemeral messages cannot have reactions')
|
||||
}
|
||||
|
||||
return await this.client.getMessageReaction.call(this.client, this.channel.id, this.id, reaction, options)
|
||||
}
|
||||
|
||||
/** Pin the message */
|
||||
async pin(): Promise<void> {
|
||||
if (this.flags & MessageFlags.EPHEMERAL) {
|
||||
throw new Error('Ephemeral messages cannot be pinned')
|
||||
}
|
||||
|
||||
return await this.client.pinMessage.call(this.client, this.channel.id, this.id)
|
||||
}
|
||||
|
||||
/** Remove a reaction from a message */
|
||||
async removeReaction(reaction: string): Promise<void> {
|
||||
if (this.flags & MessageFlags.EPHEMERAL) {
|
||||
throw new Error('Ephemeral messages cannot have reactions')
|
||||
}
|
||||
|
||||
return await this.client.removeMessageReaction.call(this.client, this.channel.id, this.id, reaction)
|
||||
}
|
||||
|
||||
/** Remove all reactions from a message for a single emoji */
|
||||
async removeReactionEmoji(reaction: string): Promise<void> {
|
||||
if (this.flags & MessageFlags.EPHEMERAL) {
|
||||
throw new Error('Ephemeral messages cannot have reactions')
|
||||
}
|
||||
|
||||
return await this.client.removeMessageReactionEmoji.call(this.client, this.channel.id, this.id, reaction)
|
||||
}
|
||||
|
||||
/** Remove all reactions from a message */
|
||||
async removeReactions(): Promise<void> {
|
||||
if (this.flags & MessageFlags.EPHEMERAL) {
|
||||
throw new Error('Ephemeral messages cannot have reactions')
|
||||
}
|
||||
|
||||
return await this.client.removeMessageReactions.call(this.client, this.channel.id, this.id)
|
||||
}
|
||||
|
||||
/** Unpin the message */
|
||||
async unpin(): Promise<void> {
|
||||
if (this.flags & MessageFlags.EPHEMERAL) {
|
||||
throw new Error('Ephemeral messages cannot be pinned')
|
||||
}
|
||||
|
||||
return await this.client.unpinMessage.call(this.client, this.channel.id, this.id)
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON([
|
||||
'activity',
|
||||
'application',
|
||||
'attachments',
|
||||
'author',
|
||||
'content',
|
||||
'editedTimestamp',
|
||||
'embeds',
|
||||
'flags',
|
||||
'guildID',
|
||||
'hit',
|
||||
'member',
|
||||
'mentionEveryone',
|
||||
'mentions',
|
||||
'messageReference',
|
||||
'pinned',
|
||||
'reactions',
|
||||
'referencedMesssage',
|
||||
'roleMentions',
|
||||
'stickers',
|
||||
'stickerItems',
|
||||
'timestamp',
|
||||
'tts',
|
||||
'type',
|
||||
'webhookID',
|
||||
...props,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export default Message
|
||||
57
packages/client/src/Structures/Permission.ts
Normal file
57
packages/client/src/Structures/Permission.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import { BitwisePermissionFlags } from '@discordeno/types'
|
||||
import { Base } from '../Base.js'
|
||||
import type { BigString } from '../Client.js'
|
||||
|
||||
export class Permission {
|
||||
allow: bigint
|
||||
deny: bigint
|
||||
_json?: Record<string, boolean>
|
||||
|
||||
constructor(allow: BigString | number = 0, deny: BigString | number = 0) {
|
||||
this.allow = BigInt(allow)
|
||||
this.deny = BigInt(deny)
|
||||
}
|
||||
|
||||
get isAdmin(): boolean {
|
||||
return !!(this.allow & BigInt(BitwisePermissionFlags.ADMINISTRATOR))
|
||||
}
|
||||
|
||||
get json() {
|
||||
if (!this._json) {
|
||||
this._json = {}
|
||||
for (const key of Object.keys(BitwisePermissionFlags)) {
|
||||
if (typeof key === 'number') continue
|
||||
|
||||
const perm = key as keyof typeof BitwisePermissionFlags
|
||||
|
||||
if (this.allow & BigInt(BitwisePermissionFlags[perm])) {
|
||||
this._json[perm] = true
|
||||
} else if (this.deny & BigInt(BitwisePermissionFlags[perm])) {
|
||||
this._json[perm] = false
|
||||
}
|
||||
}
|
||||
}
|
||||
return this._json
|
||||
}
|
||||
|
||||
/** Check if this permission allows a specific permission */
|
||||
has(permission: bigint | keyof typeof BitwisePermissionFlags): boolean {
|
||||
if (this.isAdmin) return true
|
||||
|
||||
if (typeof permission === 'bigint') {
|
||||
return (this.allow & permission) === permission
|
||||
}
|
||||
return !!(this.allow & BigInt(BitwisePermissionFlags[permission]))
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `[${this.constructor.name} +${this.allow} -${this.deny}]`
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return Base.prototype.toJSON.call(['allow', 'deny', ...props])
|
||||
}
|
||||
}
|
||||
|
||||
export default Permission
|
||||
22
packages/client/src/Structures/PermissionOverwrite.ts
Normal file
22
packages/client/src/Structures/PermissionOverwrite.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { DiscordOverwrite, OverwriteTypes } from '@discordeno/types'
|
||||
import { Base } from '../Base.js'
|
||||
import type { BigString } from '../Client.js'
|
||||
import Permission from './Permission.js'
|
||||
|
||||
export class PermissionOverwrite extends Permission {
|
||||
id: BigString
|
||||
type: OverwriteTypes
|
||||
|
||||
constructor(data: DiscordOverwrite) {
|
||||
super(data.allow, data.deny)
|
||||
|
||||
this.id = data.id
|
||||
this.type = data.type
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return Base.prototype.toJSON.call(['id', 'type', ...props])
|
||||
}
|
||||
}
|
||||
|
||||
export default PermissionOverwrite
|
||||
12
packages/client/src/Structures/channels/Category.ts
Normal file
12
packages/client/src/Structures/channels/Category.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import type { BigString } from '@discordeno/types'
|
||||
import type Collection from '../../Collection.js'
|
||||
import type { AnyGuildChannel } from '../../typings.js'
|
||||
import GuildChannel from './Guild.js'
|
||||
|
||||
export class CategoryChannel extends GuildChannel {
|
||||
get channels(): Collection<BigString, Exclude<AnyGuildChannel, CategoryChannel>> {
|
||||
return this.guild?.channels.filter((c) => c.parentID === this.id) as unknown as Collection<BigString, Exclude<AnyGuildChannel, CategoryChannel>>
|
||||
}
|
||||
}
|
||||
|
||||
export default CategoryChannel
|
||||
81
packages/client/src/Structures/channels/Channel.ts
Normal file
81
packages/client/src/Structures/channels/Channel.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { ChannelTypes, type DiscordChannel } from "@discordeno/types";
|
||||
import Base from "../../Base.js";
|
||||
import type Client from "../../Client.js";
|
||||
import type { AnyChannel } from "../../typings.js";
|
||||
import CategoryChannel from "./Category.js";
|
||||
import GuildChannel from "./Guild.js";
|
||||
import NewsChannel from "./News.js";
|
||||
import PrivateChannel from "./Private.js";
|
||||
import StageChannel from "./Stage.js";
|
||||
import TextChannel from "./Text.js";
|
||||
import TextVoiceChannel from "./TextVoice.js";
|
||||
import NewsThreadChannel from "./threads/NewsThread.js";
|
||||
import PrivateThreadChannel from "./threads/PrivateThread.js";
|
||||
import PublicThreadChannel from "./threads/PublicThread.js";
|
||||
|
||||
|
||||
export class Channel extends Base {
|
||||
type: ChannelTypes;
|
||||
client: Client;
|
||||
|
||||
constructor(data: DiscordChannel | Pick<DiscordChannel, "id" | "permissions" | "name" | "type">, client: Client) {
|
||||
super(data.id);
|
||||
this.type = data.type;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
get mention(): string {
|
||||
return `<#${this.id}>`;
|
||||
}
|
||||
|
||||
static from(data: DiscordChannel, client: Client): AnyChannel {
|
||||
switch (data.type) {
|
||||
case ChannelTypes.GuildText: {
|
||||
return new TextChannel(data, client);
|
||||
}
|
||||
case ChannelTypes.DM: {
|
||||
return new PrivateChannel(data, client);
|
||||
}
|
||||
case ChannelTypes.GuildVoice: {
|
||||
return new TextVoiceChannel(data, client);
|
||||
}
|
||||
case ChannelTypes.GuildCategory: {
|
||||
return new CategoryChannel(data, client);
|
||||
}
|
||||
case ChannelTypes.GuildAnnouncement: {
|
||||
return new NewsChannel(data, client);
|
||||
}
|
||||
case ChannelTypes.AnnouncementThread: {
|
||||
return new NewsThreadChannel(data, client);
|
||||
}
|
||||
case ChannelTypes.PublicThread: {
|
||||
return new PublicThreadChannel(data, client);
|
||||
}
|
||||
case ChannelTypes.PrivateThread: {
|
||||
return new PrivateThreadChannel(data, client);
|
||||
}
|
||||
case ChannelTypes.GuildStageVoice: {
|
||||
return new StageChannel(data, client);
|
||||
}
|
||||
}
|
||||
if (data.guild_id) {
|
||||
if (data.last_message_id !== undefined) {
|
||||
client.emit("warn", new Error(`Unknown guild text channel type: ${data.type}\n${JSON.stringify(data)}`));
|
||||
return new TextChannel(data, client);
|
||||
}
|
||||
client.emit("warn", new Error(`Unknown guild channel type: ${data.type}\n${JSON.stringify(data)}`));
|
||||
return new GuildChannel(data, client);
|
||||
}
|
||||
client.emit("warn", new Error(`Unknown channel type: ${data.type}\n${JSON.stringify(data)}`));
|
||||
return new Channel(data, client);
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON([
|
||||
"type",
|
||||
...props,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
export default Channel;
|
||||
113
packages/client/src/Structures/channels/Guild.ts
Normal file
113
packages/client/src/Structures/channels/Guild.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable no-useless-call */
|
||||
import type { BigString, DiscordChannel, OverwriteTypes } from '@discordeno/types'
|
||||
import { BitwisePermissionFlags } from '@discordeno/types'
|
||||
import { Collection } from '@discordeno/utils'
|
||||
import type Client from '../../Client.js'
|
||||
import type { EditChannelOptions, EditChannelPositionOptions } from '../../typings.js'
|
||||
import type Guild from '../guilds/Guild.js'
|
||||
import Permission from '../Permission.js'
|
||||
import PermissionOverwrite from '../PermissionOverwrite.js'
|
||||
import Channel from './Channel.js'
|
||||
import type Member from './threads/Member.js'
|
||||
import ThreadChannel from './threads/Thread.js'
|
||||
|
||||
export class GuildChannel extends Channel {
|
||||
position: number
|
||||
name: string
|
||||
parentID?: BigString | null
|
||||
guild: Guild
|
||||
nsfw: boolean
|
||||
permissionOverwrites = new Collection<BigString, PermissionOverwrite>()
|
||||
|
||||
constructor(data: DiscordChannel, client: Client) {
|
||||
super(data, client)
|
||||
|
||||
this.position = data.position ?? 0
|
||||
this.guild = client.guilds.get(data.guild_id!)!
|
||||
this.name = data.name ?? ''
|
||||
this.parentID = data.parent_id
|
||||
this.nsfw = !!data.nsfw
|
||||
}
|
||||
|
||||
update(data: DiscordChannel): void {
|
||||
if (data.type !== undefined) {
|
||||
this.type = data.type
|
||||
}
|
||||
if (data.name !== undefined) {
|
||||
this.name = data.name
|
||||
}
|
||||
if (data.position !== undefined) {
|
||||
this.position = data.position
|
||||
}
|
||||
if (data.parent_id !== undefined) {
|
||||
this.parentID = data.parent_id
|
||||
}
|
||||
this.nsfw = !!data.nsfw
|
||||
if (data.permission_overwrites) {
|
||||
data.permission_overwrites.forEach((overwrite) => {
|
||||
const perms = new PermissionOverwrite(overwrite)
|
||||
this.permissionOverwrites.set(perms.id, perms)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/** Delete the channel */
|
||||
async delete(reason?: string): Promise<void> {
|
||||
return await this.client.deleteChannel.call(this.client, this.id, reason)
|
||||
}
|
||||
|
||||
/** Delete a channel permission overwrite */
|
||||
async deletePermission(overwriteID: BigString, reason?: string): Promise<void> {
|
||||
return await this.client.deleteChannelPermission.call(this.client, this.id, overwriteID, reason)
|
||||
}
|
||||
|
||||
/** Edit the channel's properties */
|
||||
async edit(options: EditChannelOptions, reason?: string) {
|
||||
return await this.client.editChannel.call(this.client, this.id, options, reason)
|
||||
}
|
||||
|
||||
/** Create a channel permission overwrite */
|
||||
async editPermission(overwriteID: BigString, allow: bigint | number, deny: bigint | number, type: OverwriteTypes, reason?: string): Promise<void> {
|
||||
return await this.client.editChannelPermission.call(this.client, this.id, overwriteID, allow, deny, type, reason)
|
||||
}
|
||||
|
||||
/** Edit the channel's position. Note that channel position numbers are lowest on top and highest at the bottom. */
|
||||
async editPosition(position: number, options?: EditChannelPositionOptions): Promise<void> {
|
||||
return await this.client.editChannelPosition.call(this.client, this.id, position, options)
|
||||
}
|
||||
|
||||
/** Get the channel-specific permissions of a member */
|
||||
permissionsOf(memberID: BigString | Member): Permission {
|
||||
const member = ['string', 'bigint'].includes(typeof memberID) ? this.guild.members.get(memberID as BigString)! : (memberID as Member)
|
||||
let permission = this.guild.permissionsOf(member).allow
|
||||
if (permission & BigInt(BitwisePermissionFlags.ADMINISTRATOR)) {
|
||||
return new Permission(BitwisePermissionFlags.ADMINISTRATOR)
|
||||
}
|
||||
const channel = this instanceof ThreadChannel ? this.guild.channels.get(this.parentID) : this
|
||||
let overwrite = channel?.permissionOverwrites.get(this.guild.id)
|
||||
if (overwrite) {
|
||||
permission = (permission & ~overwrite.deny) | overwrite.allow
|
||||
}
|
||||
let deny = 0n
|
||||
let allow = 0n
|
||||
for (const roleID of member.roles) {
|
||||
if ((overwrite = channel?.permissionOverwrites.get(roleID))) {
|
||||
deny |= overwrite.deny
|
||||
allow |= overwrite.allow
|
||||
}
|
||||
}
|
||||
permission = (permission & ~deny) | allow
|
||||
overwrite = channel?.permissionOverwrites.get(member.id)
|
||||
if (overwrite) {
|
||||
permission = (permission & ~overwrite.deny) | overwrite.allow
|
||||
}
|
||||
return new Permission(permission)
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON(['name', 'nsfw', 'parentID', 'permissionOverwrites', 'position', ...props])
|
||||
}
|
||||
}
|
||||
|
||||
export default GuildChannel
|
||||
28
packages/client/src/Structures/channels/News.ts
Normal file
28
packages/client/src/Structures/channels/News.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/* eslint-disable no-useless-call */
|
||||
/* eslint-disable @typescript-eslint/return-await */
|
||||
import type { BigString, DiscordChannel } from '@discordeno/types'
|
||||
import type Client from '../../Client.js'
|
||||
import type { ChannelFollow } from '../../typings.js'
|
||||
import type Message from '../Message.js'
|
||||
import TextChannel from './Text.js'
|
||||
|
||||
export class NewsChannel extends TextChannel {
|
||||
constructor(data: DiscordChannel, client: Client, messageLimit?: number) {
|
||||
super(data, client, messageLimit)
|
||||
|
||||
this.rateLimitPerUser = 0
|
||||
this.update(data)
|
||||
}
|
||||
|
||||
/** Crosspost (publish) a message to subscribed channels */
|
||||
async crosspostMessage(messageID: BigString): Promise<Message> {
|
||||
return await this.client.crosspostMessage.call(this.client, this.id, messageID)
|
||||
}
|
||||
|
||||
/** Follow this channel in another channel. This creates a webhook in the target channel */
|
||||
async follow(webhookChannelID: BigString): Promise<ChannelFollow> {
|
||||
return await this.client.followChannel.call(this.client, this.id, webhookChannelID)
|
||||
}
|
||||
}
|
||||
|
||||
export default NewsChannel
|
||||
104
packages/client/src/Structures/channels/Private.ts
Normal file
104
packages/client/src/Structures/channels/Private.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
/* eslint-disable no-useless-call */
|
||||
import { ChannelTypes, type BigString, type DiscordChannel, type GetMessagesOptions } from '@discordeno/types'
|
||||
import type Client from '../../Client.js'
|
||||
import Collection from '../../Collection.js'
|
||||
import type { FileContent, GetMessageReactionOptions, MessageContent, MessageContentEdit } from '../../typings.js'
|
||||
import type Message from '../Message.js'
|
||||
import User from '../users/User.js'
|
||||
import Channel from './Channel.js'
|
||||
|
||||
export class PrivateChannel extends Channel {
|
||||
/** The ID of the last message in this channel */
|
||||
lastMessageID?: string | null
|
||||
// TODO: THIS A THING IN DMS????
|
||||
/** The rate limit per user. */
|
||||
rateLimitPerUser?: number
|
||||
/** Collection of Messages in this channel */
|
||||
messages: Collection<string, Message>
|
||||
/** The recipient in this private channel */
|
||||
recipient?: User
|
||||
|
||||
constructor(data: DiscordChannel, client: Client) {
|
||||
super(data, client)
|
||||
|
||||
this.lastMessageID = data.last_message_id
|
||||
this.rateLimitPerUser = data.rate_limit_per_user
|
||||
if (this.type === ChannelTypes.DM || this.type === undefined) {
|
||||
if (data.recipients?.[0]) this.recipient = new User(data.recipients[0], client)
|
||||
}
|
||||
this.messages = new Collection()
|
||||
this.messages.limit = client.options.messageLimit
|
||||
}
|
||||
|
||||
/** Add a reaction to a message */
|
||||
async addMessageReaction(messageID: BigString, reaction: string): Promise<void> {
|
||||
return await this.client.addMessageReaction.call(this.client, this.id, messageID, reaction)
|
||||
}
|
||||
|
||||
/** Create a message in a text channel */
|
||||
async createMessage(content: MessageContent, file: FileContent | FileContent[]): Promise<Message> {
|
||||
return await this.client.createMessage.call(this.client, this.id, content, file)
|
||||
}
|
||||
|
||||
// TODO: REASONS ARE A THING FOR AUDIT LOGS IN DMS???
|
||||
/** Delete a message */
|
||||
async deleteMessage(messageID: BigString, reason?: string): Promise<void> {
|
||||
return await this.client.deleteMessage.call(this.client, this.id, messageID, reason)
|
||||
}
|
||||
|
||||
/** Edit a message */
|
||||
async editMessage(messageID: BigString, content: MessageContentEdit): Promise<Message> {
|
||||
return await this.client.editMessage.call(this.client, this.id, messageID, content)
|
||||
}
|
||||
|
||||
/** Get a previous message in a text channel */
|
||||
async getMessage(messageID: BigString): Promise<Message> {
|
||||
return await this.client.getMessage.call(this.client, this.id, messageID)
|
||||
}
|
||||
|
||||
/** Get a list of users who reacted with a specific reaction */
|
||||
async getMessageReaction(messageID: BigString, reaction: string, options: GetMessageReactionOptions): Promise<User[]> {
|
||||
return await this.client.getMessageReaction.call(this.client, this.id, messageID, reaction, options)
|
||||
}
|
||||
|
||||
/** Get a previous message in a text channel */
|
||||
async getMessages(options: GetMessagesOptions): Promise<Message[]> {
|
||||
return await this.client.getMessages.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
/** Get all the pins in a text channel */
|
||||
async getPins(): Promise<Message[]> {
|
||||
return await this.client.getPins.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Leave the channel */
|
||||
async leave(): Promise<void> {
|
||||
return await this.client.deleteChannel.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Pin a message */
|
||||
async pinMessage(messageID: BigString): Promise<void> {
|
||||
return await this.client.pinMessage.call(this.client, this.id, messageID)
|
||||
}
|
||||
|
||||
/** Remove a reaction from a message */
|
||||
async removeMessageReaction(messageID: BigString, reaction: string): Promise<void> {
|
||||
return await this.client.removeMessageReaction.call(this.client, this.id, messageID, reaction)
|
||||
}
|
||||
|
||||
/** Send typing status in a text channel */
|
||||
async sendTyping(): Promise<void> {
|
||||
return await this.client.sendChannelTyping.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Unpin a message */
|
||||
async unpinMessage(messageID: BigString): Promise<void> {
|
||||
return await this.client.unpinMessage.call(this.client, this.id, messageID)
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON(['call', 'lastCall', 'lastMessageID', 'messages', 'recipient', ...props])
|
||||
}
|
||||
}
|
||||
|
||||
export default PrivateChannel
|
||||
46
packages/client/src/Structures/channels/Stage.ts
Normal file
46
packages/client/src/Structures/channels/Stage.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/* eslint-disable no-useless-call */
|
||||
/* eslint-disable @typescript-eslint/return-await */
|
||||
import type { DiscordChannel } from "@discordeno/types"
|
||||
import type { StageInstanceOptions } from "../../typings.js"
|
||||
import type StageInstance from "../guilds/StageInstance.js"
|
||||
import VoiceChannel from "./Voice.js"
|
||||
|
||||
|
||||
export class StageChannel extends VoiceChannel {
|
||||
/** The topic of the channel */
|
||||
topic?: string | null
|
||||
|
||||
update(data: DiscordChannel): void {
|
||||
super.update(data)
|
||||
|
||||
if (data.topic !== undefined) {
|
||||
this.topic = data.topic
|
||||
}
|
||||
}
|
||||
|
||||
/** Create a stage instance */
|
||||
async createInstance(options: StageInstanceOptions): Promise<StageInstance> {
|
||||
return await this.client.createStageInstance.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
/** Delete the stage instance for this channel */
|
||||
async deleteInstance(): Promise<void> {
|
||||
return await this.client.deleteStageInstance.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Update the stage instance for this channel */
|
||||
async editInstance(options: StageInstanceOptions): Promise<StageInstance> {
|
||||
return await this.client.editStageInstance.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
/** Get the stage instance for this channel */
|
||||
async getInstance(): Promise<StageInstance> {
|
||||
return await this.client.getStageInstance.call(this.client, this.id)
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON(['topic', ...props])
|
||||
}
|
||||
}
|
||||
|
||||
export default StageChannel
|
||||
375
packages/client/src/Structures/channels/Text.ts
Normal file
375
packages/client/src/Structures/channels/Text.ts
Normal file
@@ -0,0 +1,375 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable no-useless-call */
|
||||
/* eslint-disable @typescript-eslint/return-await */
|
||||
import type { BigString, DiscordChannel, GetMessagesOptions } from '@discordeno/types'
|
||||
import type Client from '../../Client.js'
|
||||
import Collection from '../../Collection.js'
|
||||
import type {
|
||||
CreateChannelInviteOptions,
|
||||
CreateThreadOptions,
|
||||
CreateThreadWithoutMessageOptions,
|
||||
FileContent,
|
||||
GetArchivedThreadsOptions,
|
||||
GetMessageReactionOptions,
|
||||
ListedChannelThreads,
|
||||
MessageContent,
|
||||
PurgeChannelOptions,
|
||||
} from '../../typings.js'
|
||||
import type Invite from '../Invite.js'
|
||||
import type Message from '../Message.js'
|
||||
import GuildChannel from './Guild.js'
|
||||
import type PrivateThreadChannel from './threads/PrivateThread.js'
|
||||
import type PublicThreadChannel from './threads/PublicThread.js'
|
||||
|
||||
export class TextChannel extends GuildChannel {
|
||||
/** Collection of Messages in this channel */
|
||||
messages: Collection<string, Message>
|
||||
/** The ratelimit of the channel, in seconds. 0 means no ratelimit is enabled */
|
||||
rateLimitPerUser: number | null
|
||||
/** The ID of the last message in this channel */
|
||||
lastMessageID?: string | null
|
||||
/** The timestamp of the last pinned message */
|
||||
lastPinTimestamp?: number | null
|
||||
/** Default duration for newly created threads, in minutes, to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 */
|
||||
defaultAutoArchiveDuration?: number
|
||||
/** The channel topic (0-4096 characters for GUILD_FORUM channels, 0-1024 characters for all others) */
|
||||
topic?: string | null
|
||||
|
||||
constructor(data: DiscordChannel, client: Client, messageLimit?: number) {
|
||||
super(data, client)
|
||||
|
||||
this.messages = new Collection()
|
||||
if (messageLimit == null) this.messages.limit = client.options.messageLimit
|
||||
else this.messages.limit = messageLimit
|
||||
|
||||
this.rateLimitPerUser = data.rate_limit_per_user == null ? null : data.rate_limit_per_user
|
||||
|
||||
this.lastMessageID = data.last_message_id ?? null
|
||||
this.lastPinTimestamp = data.last_pin_timestamp ? Date.parse(data.last_pin_timestamp) : null
|
||||
|
||||
this.update(data)
|
||||
}
|
||||
|
||||
update(data: DiscordChannel): void {
|
||||
super.update(data)
|
||||
|
||||
if (data.rate_limit_per_user !== undefined) this.rateLimitPerUser = data.rate_limit_per_user
|
||||
if (data.topic !== undefined) this.topic = data.topic
|
||||
if (data.default_auto_archive_duration !== undefined) this.defaultAutoArchiveDuration = data.default_auto_archive_duration
|
||||
}
|
||||
|
||||
/** Add a reaction to a message */
|
||||
async addMessageReaction(messageID: BigString, reaction: string): Promise<void> {
|
||||
return this.client.addMessageReaction.call(this.client, this.id, messageID, reaction)
|
||||
}
|
||||
|
||||
/** Create an invite for the channel */
|
||||
async createInvite(options?: CreateChannelInviteOptions, reason?: string): Promise<Invite> {
|
||||
return await this.client.createChannelInvite.call(this.client, this.id, options, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a message in the channel
|
||||
* @arg {String | Object} content A string or object. If an object is passed:
|
||||
* @arg {Object} [content.allowedMentions] A list of mentions to allow (overrides default)
|
||||
* @arg {Boolean} [content.allowedMentions.everyone] Whether or not to allow @everyone/@here.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.roles] Whether or not to allow all role mentions, or an array of specific role mentions to allow.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.users] Whether or not to allow all user mentions, or an array of specific user mentions to allow.
|
||||
* @arg {Boolean} [content.allowedMentions.repliedUser] Whether or not to mention the author of the message being replied to.
|
||||
* @arg {Array<Object>} [content.components] An array of component objects
|
||||
* @arg {String} [content.components[].custom_id] The ID of the component (type 2 style 0-4 and type 3 only)
|
||||
* @arg {Boolean} [content.components[].disabled] Whether the component is disabled (type 2 and 3 only)
|
||||
* @arg {Object} [content.components[].emoji] The emoji to be displayed in the component (type 2)
|
||||
* @arg {String} [content.components[].label] The label to be displayed in the component (type 2)
|
||||
* @arg {Number} [content.components[].max_values] The maximum number of items that can be chosen (1-25, default 1)
|
||||
* @arg {Number} [content.components[].min_values] The minimum number of items that must be chosen (0-25, default 1)
|
||||
* @arg {Array<Object>} [content.components[].options] The options for this component (type 3 only)
|
||||
* @arg {Boolean} [content.components[].options[].default] Whether this option should be the default value selected
|
||||
* @arg {String} [content.components[].options[].description] The description for this option
|
||||
* @arg {Object} [content.components[].options[].emoji] The emoji to be displayed in this option
|
||||
* @arg {String} content.components[].options[].label The label for this option
|
||||
* @arg {Number | String} content.components[].options[].value The value for this option
|
||||
* @arg {String} [content.components[].placeholder] The placeholder text for the component when no option is selected (type 3 only)
|
||||
* @arg {Number} [content.components[].style] The style of the component (type 2 only) - If 0-4, `custom_id` is required; if 5, `url` is required
|
||||
* @arg {Number} content.components[].type The type of component - If 1, it is a collection and a `components` array (nested) is required; if 2, it is a button; if 3, it is a select menu
|
||||
* @arg {String} [content.components[].url] The URL that the component should open for users (type 2 style 5 only)
|
||||
* @arg {String} [content.content] A content string
|
||||
* @arg {Object} [content.embed] An embed object. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
|
||||
* @arg {Array<Object>} [content.embeds] An array of embed objects. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
|
||||
* @arg {Object} [content.messageReference] The message reference, used when replying to messages
|
||||
* @arg {String} [content.messageReference.channelID] The channel ID of the referenced message
|
||||
* @arg {Boolean} [content.messageReference.failIfNotExists=true] Whether to throw an error if the message reference doesn't exist. If false, and the referenced message doesn't exist, the message is created without a referenced message
|
||||
* @arg {String} [content.messageReference.guildID] The guild ID of the referenced message
|
||||
* @arg {String} content.messageReference.messageID The message ID of the referenced message. This cannot reference a system message
|
||||
* @arg {String} [content.messageReferenceID] [DEPRECATED] The ID of the message should be replied to. Use `messageReference` instead
|
||||
* @arg {Array<String>} [content.stickerIDs] An array of IDs corresponding to stickers to send
|
||||
* @arg {Boolean} [content.tts] Set the message TTS flag
|
||||
* @arg {Object | Array<Object>} [file] A file object (or an Array of them)
|
||||
* @arg {Buffer} file.file A buffer containing file data
|
||||
* @arg {String} file.name What to name the file
|
||||
* @returns {Promise<Message>}
|
||||
*/
|
||||
async createMessage(content: MessageContent, file: FileContent | FileContent[]) {
|
||||
return this.client.createMessage.call(this.client, this.id, content, file)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a thread with an existing message
|
||||
* @arg {String} messageID The ID of the message to create the thread from
|
||||
* @arg {Object} options The thread options
|
||||
* @arg {Number} options.autoArchiveDuration Duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080
|
||||
* @arg {String} options.name The thread channel name
|
||||
* @returns {Promise<NewsThreadChannel | PublicThreadChannel>}
|
||||
*/
|
||||
async createThreadWithMessage(messageID: string, options: CreateThreadOptions) {
|
||||
return this.client.createThreadWithMessage.call(this.client, this.id, messageID, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a thread without an existing message
|
||||
* @arg {Object} options The thread options
|
||||
* @arg {Number} options.autoArchiveDuration Duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080
|
||||
* @arg {boolean} [options.invitable] Whether non-moderators can add other non-moderators to the thread (private threads only)
|
||||
* @arg {String} options.name The thread channel name
|
||||
* @arg {Number} [options.type] The channel type of the thread to create. It is recommended to explicitly set this property as this will be a required property in API v10
|
||||
* @returns {Promise<PrivateThreadChannel>}
|
||||
*/
|
||||
async createThreadWithoutMessage(options: CreateThreadWithoutMessageOptions) {
|
||||
return this.client.createThreadWithoutMessage.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a channel webhook
|
||||
* @arg {Object} options Webhook options
|
||||
* @arg {String} [options.avatar] The default avatar as a base64 data URI. Note: base64 strings alone are not base64 data URI strings
|
||||
* @arg {String} options.name The default name
|
||||
* @arg {String} [reason] The reason to be displayed in audit logs
|
||||
* @returns {Promise<Object>} Resolves with a webhook object
|
||||
*/
|
||||
async createWebhook(options: { name: string; avatar?: string | null }, reason: string) {
|
||||
return this.client.createChannelWebhook.call(this.client, this.id, options, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a message
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @arg {String} [reason] The reason to be displayed in audit logs
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async deleteMessage(messageID: string, reason: string) {
|
||||
return this.client.deleteMessage.call(this.client, this.id, messageID, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk delete messages (bot accounts only)
|
||||
* @arg {Array<String>} messageIDs Array of message IDs to delete
|
||||
* @arg {String} [reason] The reason to be displayed in audit logs
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async deleteMessages(messageIDs: string[], reason: string) {
|
||||
return this.client.deleteMessages.call(this.client, this.id, messageIDs, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a message
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @arg {String | Array | Object} content A string, array of strings, or object. If an object is passed:
|
||||
* @arg {Object} [content.allowedMentions] A list of mentions to allow (overrides default)
|
||||
* @arg {Boolean} [content.allowedMentions.everyone] Whether or not to allow @everyone/@here.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.roles] Whether or not to allow all role mentions, or an array of specific role mentions to allow.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.users] Whether or not to allow all user mentions, or an array of specific user mentions to allow.
|
||||
* @arg {Array<Object>} [content.components] An array of component objects
|
||||
* @arg {String} [content.components[].custom_id] The ID of the component (type 2 style 0-4 and type 3 only)
|
||||
* @arg {Boolean} [content.components[].disabled] Whether the component is disabled (type 2 and 3 only)
|
||||
* @arg {Object} [content.components[].emoji] The emoji to be displayed in the component (type 2)
|
||||
* @arg {String} [content.components[].label] The label to be displayed in the component (type 2)
|
||||
* @arg {Number} [content.components[].max_values] The maximum number of items that can be chosen (1-25, default 1)
|
||||
* @arg {Number} [content.components[].min_values] The minimum number of items that must be chosen (0-25, default 1)
|
||||
* @arg {Array<Object>} [content.components[].options] The options for this component (type 3 only)
|
||||
* @arg {Boolean} [content.components[].options[].default] Whether this option should be the default value selected
|
||||
* @arg {String} [content.components[].options[].description] The description for this option
|
||||
* @arg {Object} [content.components[].options[].emoji] The emoji to be displayed in this option
|
||||
* @arg {String} content.components[].options[].label The label for this option
|
||||
* @arg {Number | String} content.components[].options[].value The value for this option
|
||||
* @arg {String} [content.components[].placeholder] The placeholder text for the component when no option is selected (type 3 only)
|
||||
* @arg {Number} [content.components[].style] The style of the component (type 2 only) - If 0-4, `custom_id` is required; if 5, `url` is required
|
||||
* @arg {Number} content.components[].type The type of component - If 1, it is a collection and a `components` array (nested) is required; if 2, it is a button; if 3, it is a select menu
|
||||
* @arg {String} [content.components[].url] The URL that the component should open for users (type 2 style 5 only)
|
||||
* @arg {String} [content.content] A content string
|
||||
* @arg {Object} [content.embed] An embed object. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
|
||||
* @arg {Array<Object>} [content.embeds] An array of embed objects. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
|
||||
* @arg {Object | Array<Object>} [content.file] A file object (or an Array of them)
|
||||
* @arg {Buffer} content.file[].file A buffer containing file data
|
||||
* @arg {String} content.file[].name What to name the file
|
||||
* @arg {Number} [content.flags] A number representing the flags to apply to the message. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#message-object-message-flags) for flags reference
|
||||
* @returns {Promise<Message>}
|
||||
*/
|
||||
async editMessage(messageID: string, content: string) {
|
||||
return this.client.editMessage.call(this.client, this.id, messageID, content)
|
||||
}
|
||||
|
||||
/** Get all archived threads in this channel */
|
||||
async getArchivedThreads(type: 'private', options?: GetArchivedThreadsOptions): Promise<ListedChannelThreads<PrivateThreadChannel>>
|
||||
async getArchivedThreads(type: 'public', options?: GetArchivedThreadsOptions): Promise<ListedChannelThreads<PublicThreadChannel>>
|
||||
async getArchivedThreads(
|
||||
type: 'public' | 'private',
|
||||
options?: GetArchivedThreadsOptions,
|
||||
): Promise<ListedChannelThreads<PrivateThreadChannel | PublicThreadChannel>> {
|
||||
return await this.client.getArchivedThreads.call(this.client, this.id, type as 'public', options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all invites in the channel
|
||||
* @returns {Promise<Array<Invite>>}
|
||||
*/
|
||||
async getInvites() {
|
||||
return this.client.getChannelInvites.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get joined private archived threads in this channel
|
||||
* @arg {Object} [options] Additional options when requesting archived threads
|
||||
* @arg {Date} [options.before] List of threads to return before the timestamp
|
||||
* @arg {Number} [options.limit] Maximum number of threads to return
|
||||
* @returns {Promise<Object>} An object containing an array of `threads`, an array of `members` and whether the response `hasMore` threads that could be returned in a subsequent call
|
||||
*/
|
||||
async getJoinedPrivateArchivedThreads(options: GetArchivedThreadsOptions) {
|
||||
return this.client.getJoinedPrivateArchivedThreads.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a previous message in the channel
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @returns {Promise<Message>}
|
||||
*/
|
||||
async getMessage(messageID: string) {
|
||||
return this.client.getMessage.call(this.client, this.id, messageID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of users who reacted with a specific reaction
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @arg {String} reaction The reaction (Unicode string if Unicode emoji, `emojiName:emojiID` if custom emoji)
|
||||
* @arg {Object} [options] Options for the request. If this is a number, it is treated as `options.limit` ([DEPRECATED] behavior)
|
||||
* @arg {Number} [options.limit=100] The maximum number of users to get
|
||||
* @arg {String} [options.after] Get users after this user ID
|
||||
* @returns {Promise<Array<User>>}
|
||||
*/
|
||||
async getMessageReaction(messageID: string, reaction: string, options: GetMessageReactionOptions) {
|
||||
return this.client.getMessageReaction.call(this.client, this.id, messageID, reaction, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get previous messages in the channel
|
||||
* @arg {Object} [options] Options for the request. If this is a number ([DEPRECATED] behavior), it is treated as `options.limit`
|
||||
* @arg {String} [options.after] Get messages after this message ID
|
||||
* @arg {String} [options.around] Get messages around this message ID (does not work with limit > 100)
|
||||
* @arg {String} [options.before] Get messages before this message ID
|
||||
* @arg {Number} [options.limit=50] The max number of messages to get
|
||||
* @returns {Promise<Array<Message>>}
|
||||
*/
|
||||
async getMessages(options: GetMessagesOptions) {
|
||||
return this.client.getMessages.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the pins in the channel
|
||||
* @returns {Promise<Array<Message>>}
|
||||
*/
|
||||
async getPins() {
|
||||
return this.client.getPins.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the webhooks in the channel
|
||||
* @returns {Promise<Array<Object>>} Resolves with an array of webhook objects
|
||||
*/
|
||||
async getWebhooks() {
|
||||
return this.client.getChannelWebhooks.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Pin a message
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async pinMessage(messageID: string) {
|
||||
return this.client.pinMessage.call(this.client, this.id, messageID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge previous messages in the channel with an optional filter (bot accounts only)
|
||||
* @arg {Object} options Options for the request. If this is a number ([DEPRECATED] behavior), it is treated as `options.limit`
|
||||
* @arg {String} [options.after] Get messages after this message ID
|
||||
* @arg {String} [options.before] Get messages before this message ID
|
||||
* @arg {Function} [options.filter] Optional filter function that returns a boolean when passed a Message object
|
||||
* @arg {Number} options.limit The max number of messages to search through, -1 for no limit
|
||||
* @arg {String} [options.reason] The reason to be displayed in audit logs
|
||||
* @returns {Promise<Number>} Resolves with the number of messages deleted
|
||||
*/
|
||||
async purge(limit: PurgeChannelOptions) {
|
||||
return this.client.purgeChannel.call(this.client, this.id, limit)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a reaction from a message
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @arg {String} reaction The reaction (Unicode string if Unicode emoji, `emojiName:emojiID` if custom emoji)
|
||||
* @arg {String} [userID="@me"] The ID of the user to remove the reaction for
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async removeMessageReaction(messageID: string, reaction: string, userID: string) {
|
||||
return this.client.removeMessageReaction.call(this.client, this.id, messageID, reaction, userID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all reactions from a message for a single emoji
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @arg {String} reaction The reaction (Unicode string if Unicode emoji, `emojiName:emojiID` if custom emoji)
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async removeMessageReactionEmoji(messageID: string, reaction: string) {
|
||||
return this.client.removeMessageReactionEmoji.call(this.client, this.id, messageID, reaction)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all reactions from a message
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async removeMessageReactions(messageID: string) {
|
||||
return this.client.removeMessageReactions.call(this.client, this.id, messageID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Send typing status in the channel
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async sendTyping() {
|
||||
return this.client.sendChannelTyping.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpin a message
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async unpinMessage(messageID: string) {
|
||||
return this.client.unpinMessage.call(this.client, this.id, messageID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Un-send a message. You're welcome Programmix
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async unsendMessage(messageID: string) {
|
||||
return this.client.deleteMessage.call(this.client, this.id, messageID)
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON(['lastMessageID', 'lastPinTimestamp', 'messages', 'rateLimitPerUser', 'topic', ...props])
|
||||
}
|
||||
}
|
||||
|
||||
export default TextChannel
|
||||
271
packages/client/src/Structures/channels/TextVoice.ts
Normal file
271
packages/client/src/Structures/channels/TextVoice.ts
Normal file
@@ -0,0 +1,271 @@
|
||||
/* eslint-disable no-useless-call */
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import type { DiscordChannel, GetMessagesOptions } from '@discordeno/types'
|
||||
import type Client from '../../Client.js'
|
||||
import Collection from '../../Collection.js'
|
||||
import type {
|
||||
CreateInviteOptions,
|
||||
FileContent,
|
||||
GetMessageReactionOptions,
|
||||
MessageContent,
|
||||
MessageContentEdit,
|
||||
PurgeChannelOptions,
|
||||
} from '../../typings.js'
|
||||
import type Message from '../Message.js'
|
||||
import VoiceChannel from './Voice.js'
|
||||
|
||||
/**
|
||||
* Represents a Text-in-Voice channel. See VoiceChannel for more properties and methods.
|
||||
* @extends VoiceChannel
|
||||
* @prop {String} lastMessageID The ID of the last message in this channel
|
||||
* @prop {Collection<Message>} messages Collection of Messages in this channel
|
||||
* @prop {Number} rateLimitPerUser The ratelimit of the channel, in seconds. 0 means no ratelimit is enabled
|
||||
*/
|
||||
export class TextVoiceChannel extends VoiceChannel {
|
||||
lastMessageID: string | null
|
||||
messages: Collection<string, Message>
|
||||
rateLimitPerUser: number | null
|
||||
|
||||
constructor(data: DiscordChannel, client: Client, messageLimit?: number) {
|
||||
super(data, client)
|
||||
|
||||
this.messages = new Collection()
|
||||
if (messageLimit == null) this.messages.limit = client.options.messageLimit
|
||||
else this.messages.limit = messageLimit
|
||||
|
||||
this.lastMessageID = data.last_message_id ?? null
|
||||
this.rateLimitPerUser = data.rate_limit_per_user == null ? null : data.rate_limit_per_user
|
||||
}
|
||||
|
||||
update(data: DiscordChannel) {
|
||||
super.update(data)
|
||||
// "not yet, possibly TBD"
|
||||
if (data.rate_limit_per_user !== undefined) {
|
||||
this.rateLimitPerUser = data.rate_limit_per_user
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a reaction to a message
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @arg {String} reaction The reaction (Unicode string if Unicode emoji, `emojiName:emojiID` if custom emoji)
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async addMessageReaction(messageID: string, reaction: string) {
|
||||
return await this.client.addMessageReaction.call(this.client, this.id, messageID, reaction)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an invite for the channel
|
||||
* @arg {Object} [options] Invite generation options
|
||||
* @arg {Number} [options.maxAge] How long the invite should last in seconds
|
||||
* @arg {Number} [options.maxUses] How many uses the invite should last for
|
||||
* @arg {Boolean} [options.temporary] Whether the invite grants temporary membership or not
|
||||
* @arg {Boolean} [options.unique] Whether the invite is unique or not
|
||||
* @arg {String} [reason] The reason to be displayed in audit logs
|
||||
* @returns {Promise<Invite>}
|
||||
*/
|
||||
async createInvite(options: CreateInviteOptions, reason: string) {
|
||||
return await this.client.createChannelInvite.call(this.client, this.id, options, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a message in the channel
|
||||
* Note: If you want to DM someone, the user ID is **not** the DM channel ID. use Client.getDMChannel() to get the DM channel ID for a user
|
||||
* @arg {String | Object} content A string or object. If an object is passed:
|
||||
* @arg {Object} [content.allowedMentions] A list of mentions to allow (overrides default)
|
||||
* @arg {Boolean} [content.allowedMentions.everyone] Whether or not to allow @everyone/@here.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.roles] Whether or not to allow all role mentions, or an array of specific role mentions to allow.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.users] Whether or not to allow all user mentions, or an array of specific user mentions to allow.
|
||||
* @arg {Boolean} [options.allowedMentions.repliedUser] Whether or not to mention the author of the message being replied to
|
||||
* @arg {Array<Object>} [content.components] An array of component objects
|
||||
* @arg {String} [content.components[].custom_id] The ID of the component (type 2 style 0-4 and type 3 only)
|
||||
* @arg {Boolean} [content.components[].disabled] Whether the component is disabled (type 2 and 3 only)
|
||||
* @arg {Object} [content.components[].emoji] The emoji to be displayed in the component (type 2)
|
||||
* @arg {String} [content.components[].label] The label to be displayed in the component (type 2)
|
||||
* @arg {Number} [content.components[].max_values] The maximum number of items that can be chosen (1-25, default 1)
|
||||
* @arg {Number} [content.components[].min_values] The minimum number of items that must be chosen (0-25, default 1)
|
||||
* @arg {Array<Object>} [content.components[].options] The options for this component (type 3 only)
|
||||
* @arg {Boolean} [content.components[].options[].default] Whether this option should be the default value selected
|
||||
* @arg {String} [content.components[].options[].description] The description for this option
|
||||
* @arg {Object} [content.components[].options[].emoji] The emoji to be displayed in this option
|
||||
* @arg {String} content.components[].options[].label The label for this option
|
||||
* @arg {Number | String} content.components[].options[].value The value for this option
|
||||
* @arg {String} [content.components[].placeholder] The placeholder text for the component when no option is selected (type 3 only)
|
||||
* @arg {Number} [content.components[].style] The style of the component (type 2 only) - If 0-4, `custom_id` is required; if 5, `url` is required
|
||||
* @arg {Number} content.components[].type The type of component - If 1, it is a collection and a `components` array (nested) is required; if 2, it is a button; if 3, it is a select menu
|
||||
* @arg {String} [content.components[].url] The URL that the component should open for users (type 2 style 5 only)
|
||||
* @arg {String} content.content A content string
|
||||
* @arg {Array<Object>} [content.embeds] An array of embed objects. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
|
||||
* @arg {Object} [content.messageReference] The message reference, used when replying to messages
|
||||
* @arg {String} [content.messageReference.channelID] The channel ID of the referenced message
|
||||
* @arg {Boolean} [content.messageReference.failIfNotExists=true] Whether to throw an error if the message reference doesn't exist. If false, and the referenced message doesn't exist, the message is created without a referenced message
|
||||
* @arg {String} [content.messageReference.guildID] The guild ID of the referenced message
|
||||
* @arg {String} content.messageReference.messageID The message ID of the referenced message. This cannot reference a system message
|
||||
* @arg {Array<String>} [content.stickerIDs] An array of IDs corresponding to the stickers to send
|
||||
* @arg {Boolean} [content.tts] Set the message TTS flag
|
||||
* @arg {Object} [file] A file object
|
||||
* @arg {Buffer} file.file A buffer containing file data
|
||||
* @arg {String} file.name What to name the file
|
||||
* @returns {Promise<Message>}
|
||||
*/
|
||||
async createMessage(content: MessageContent, file?: FileContent | FileContent[]) {
|
||||
return await this.client.createMessage.call(this.client, this.id, content, file)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a message
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @arg {String} [reason] The reason to be displayed in audit logs
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async deleteMessage(messageID: string, reason: string) {
|
||||
return await this.client.deleteMessage.call(this.client, this.id, messageID, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk delete messages (bot accounts only)
|
||||
* @arg {Array<String>} messageIDs Array of message IDs to delete
|
||||
* @arg {String} [reason] The reason to be displayed in audit logs
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async deleteMessages(messageIDs: string[], reason: string) {
|
||||
return await this.client.deleteMessages.call(this.client, this.id, messageIDs, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a message
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @arg {String | Array | Object} content A string, array of strings, or object. If an object is passed:
|
||||
* @arg {Object} [content.allowedMentions] A list of mentions to allow (overrides default)
|
||||
* @arg {Boolean} [content.allowedMentions.everyone] Whether or not to allow @everyone/@here.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.roles] Whether or not to allow all role mentions, or an array of specific role mentions to allow.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.users] Whether or not to allow all user mentions, or an array of specific user mentions to allow.
|
||||
* @arg {Array<Object>} [content.components] An array of component objects
|
||||
* @arg {String} [content.components[].custom_id] The ID of the component (type 2 style 0-4 and type 3 only)
|
||||
* @arg {Boolean} [content.components[].disabled] Whether the component is disabled (type 2 and 3 only)
|
||||
* @arg {Object} [content.components[].emoji] The emoji to be displayed in the component (type 2)
|
||||
* @arg {String} [content.components[].label] The label to be displayed in the component (type 2)
|
||||
* @arg {Number} [content.components[].max_values] The maximum number of items that can be chosen (1-25, default 1)
|
||||
* @arg {Number} [content.components[].min_values] The minimum number of items that must be chosen (0-25, default 1)
|
||||
* @arg {Array<Object>} [content.components[].options] The options for this component (type 3 only)
|
||||
* @arg {Boolean} [content.components[].options[].default] Whether this option should be the default value selected
|
||||
* @arg {String} [content.components[].options[].description] The description for this option
|
||||
* @arg {Object} [content.components[].options[].emoji] The emoji to be displayed in this option
|
||||
* @arg {String} content.components[].options[].label The label for this option
|
||||
* @arg {Number | String} content.components[].options[].value The value for this option
|
||||
* @arg {String} [content.components[].placeholder] The placeholder text for the component when no option is selected (type 3 only)
|
||||
* @arg {Number} [content.components[].style] The style of the component (type 2 only) - If 0-4, `custom_id` is required; if 5, `url` is required
|
||||
* @arg {Number} content.components[].type The type of component - If 1, it is a collection and a `components` array (nested) is required; if 2, it is a button; if 3, it is a select menu
|
||||
* @arg {String} [content.components[].url] The URL that the component should open for users (type 2 style 5 only)
|
||||
* @arg {String} content.content A content string
|
||||
* @arg {Boolean} [content.disableEveryone] Whether to filter @everyone/@here or not (overrides default)
|
||||
* @arg {Array<Object>} [content.embeds] An array of embed objects. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
|
||||
* @arg {Number} [content.flags] A number representing the flags to apply to the message. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#message-object-message-flags) for flags reference
|
||||
* @returns {Promise<Message>}
|
||||
*/
|
||||
async editMessage(messageID: string, content: MessageContentEdit) {
|
||||
return await this.client.editMessage.call(this.client, this.id, messageID, content)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all invites in the channel
|
||||
* @returns {Promise<Array<Invite>>}
|
||||
*/
|
||||
async getInvites() {
|
||||
return await this.client.getChannelInvites.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a previous message in the channel
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @returns {Promise<Message>}
|
||||
*/
|
||||
async getMessage(messageID: string) {
|
||||
return await this.client.getMessage.call(this.client, this.id, messageID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of users who reacted with a specific reaction
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @arg {String} reaction The reaction (Unicode string if Unicode emoji, `emojiName:emojiID` if custom emoji)
|
||||
* @arg {Object} [options] Options for the request. If this is a number, it is treated as `options.limit` ([DEPRECATED] behavior)
|
||||
* @arg {Number} [options.limit=100] The maximum number of users to get
|
||||
* @arg {String} [options.after] Get users after this user ID
|
||||
* @returns {Promise<Array<User>>}
|
||||
*/
|
||||
async getMessageReaction(messageID: string, reaction: string, options: GetMessageReactionOptions) {
|
||||
return await this.client.getMessageReaction.call(this.client, this.id, messageID, reaction, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get previous messages in the channel
|
||||
* @arg {Object} [options] Options for the request. If this is a number ([DEPRECATED] behavior), it is treated as `options.limit`
|
||||
* @arg {String} [options.after] Get messages after this message ID
|
||||
* @arg {String} [options.around] Get messages around this message ID (does not work with limit > 100)
|
||||
* @arg {String} [options.before] Get messages before this message ID
|
||||
* @arg {Number} [options.limit=50] The max number of messages to get
|
||||
* @returns {Promise<Array<Message>>}
|
||||
*/
|
||||
async getMessages(options: GetMessagesOptions) {
|
||||
return await this.client.getMessages.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge previous messages in the channel with an optional filter (bot accounts only)
|
||||
* @arg {Object} options Options for the request. If this is a number ([DEPRECATED] behavior), it is treated as `options.limit`
|
||||
* @arg {String} [options.after] Get messages after this message ID
|
||||
* @arg {String} [options.before] Get messages before this message ID
|
||||
* @arg {Function} [options.filter] Optional filter function that returns a boolean when passed a Message object
|
||||
* @arg {Number} options.limit The max number of messages to search through, -1 for no limit
|
||||
* @arg {String} [options.reason] The reason to be displayed in audit logs
|
||||
* @returns {Promise<Number>} Resolves with the number of messages deleted
|
||||
*/
|
||||
async purge(options: PurgeChannelOptions) {
|
||||
return await this.client.purgeChannel.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a reaction from a message
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @arg {String} reaction The reaction (Unicode string if Unicode emoji, `emojiName:emojiID` if custom emoji)
|
||||
* @arg {String} [userID="@me"] The ID of the user to remove the reaction for
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async removeMessageReaction(messageID: string, reaction: string, userID: string) {
|
||||
return await this.client.removeMessageReaction.call(this.client, this.id, messageID, reaction, userID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all reactions from a message for a single emoji
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @arg {String} reaction The reaction (Unicode string if Unicode emoji, `emojiName:emojiID` if custom emoji)
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async removeMessageReactionEmoji(messageID: string, reaction: string) {
|
||||
return await this.client.removeMessageReactionEmoji.call(this.client, this.id, messageID, reaction)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all reactions from a message
|
||||
* @arg {String} messageID The ID of the message
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async removeMessageReactions(messageID: string) {
|
||||
return await this.client.removeMessageReactions.call(this.client, this.id, messageID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Send typing status in the channel
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async sendTyping() {
|
||||
return await this.client.sendChannelTyping.call(this.client, this.id)
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON(['lastMessageID', 'messages', 'rateLimitPerUser', ...props])
|
||||
}
|
||||
}
|
||||
|
||||
export default TextVoiceChannel
|
||||
91
packages/client/src/Structures/channels/Voice.ts
Normal file
91
packages/client/src/Structures/channels/Voice.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
/* eslint-disable no-useless-call */
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import type { DiscordChannel } from '@discordeno/types'
|
||||
import type Client from '../../Client.js'
|
||||
import Collection from '../../Collection.js'
|
||||
import type { CreateInviteOptions, TextVoiceChannelTypes, VideoQualityMode } from '../../typings.js'
|
||||
import type Member from '../guilds/Member.js'
|
||||
import GuildChannel from './Guild.js'
|
||||
|
||||
export class VoiceChannel extends GuildChannel {
|
||||
bitrate: number = 0
|
||||
rtcRegion: string | null = null
|
||||
type: TextVoiceChannelTypes = 0
|
||||
userLimit: number = 0
|
||||
videoQualityMode: VideoQualityMode = 0
|
||||
voiceMembers: Collection<string, Member>
|
||||
|
||||
constructor(data: DiscordChannel, client: Client) {
|
||||
super(data, client)
|
||||
|
||||
this.voiceMembers = new Collection()
|
||||
this.update(data)
|
||||
}
|
||||
|
||||
update(data: DiscordChannel): void {
|
||||
super.update(data)
|
||||
|
||||
if (data.bitrate !== undefined) {
|
||||
this.bitrate = data.bitrate
|
||||
}
|
||||
if (data.rtc_region !== undefined) {
|
||||
this.rtcRegion = data.rtc_region
|
||||
}
|
||||
if (data.user_limit !== undefined) {
|
||||
this.userLimit = data.user_limit
|
||||
}
|
||||
if (data.video_quality_mode !== undefined) {
|
||||
this.videoQualityMode = data.video_quality_mode
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an invite for the channel
|
||||
* @arg {Object} [options] Invite generation options
|
||||
* @arg {Number} [options.maxAge] How long the invite should last in seconds
|
||||
* @arg {Number} [options.maxUses] How many uses the invite should last for
|
||||
* @arg {Boolean} [options.temporary] Whether the invite grants temporary membership or not
|
||||
* @arg {Boolean} [options.unique] Whether the invite is unique or not
|
||||
* @arg {String} [reason] The reason to be displayed in audit logs
|
||||
* @returns {Promise<Invite>}
|
||||
*/
|
||||
async createInvite(options: CreateInviteOptions, reason: string) {
|
||||
return await this.client.createChannelInvite.call(this.client, this.id, options, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all invites in the channel
|
||||
* @returns {Promise<Array<Invite>>}
|
||||
*/
|
||||
async getInvites() {
|
||||
return await this.client.getChannelInvites.call(this.client, this.id)
|
||||
}
|
||||
|
||||
// TODO: gateway
|
||||
// /**
|
||||
// * Joins the channel.
|
||||
// * @arg {Object} [options] VoiceConnection constructor options
|
||||
// * @arg {Object} [options.opusOnly] Skip opus encoder initialization. You should not enable this unless you know what you are doing
|
||||
// * @arg {Object} [options.shared] Whether the VoiceConnection will be part of a SharedStream or not
|
||||
// * @arg {Boolean} [options.selfMute] Whether the bot joins the channel muted or not
|
||||
// * @arg {Boolean} [options.selfDeaf] Whether the bot joins the channel deafened or not
|
||||
// * @returns {Promise<VoiceConnection>} Resolves with a VoiceConnection
|
||||
// */
|
||||
// join(options: JoinVoiceChannelOptions) {
|
||||
// return this.client.joinVoiceChannel.call(this.client, this.id, options);
|
||||
// }
|
||||
|
||||
// TODO: gateway
|
||||
// /**
|
||||
// * Leaves the channel.
|
||||
// */
|
||||
// leave() {
|
||||
// return this.client.leaveVoiceChannel.call(this.client, this.id);
|
||||
// }
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON(['bitrate', 'rtcRegion', 'userLimit', 'videoQualityMode', 'voiceMembers', ...props])
|
||||
}
|
||||
}
|
||||
|
||||
export default VoiceChannel
|
||||
42
packages/client/src/Structures/channels/threads/Member.ts
Normal file
42
packages/client/src/Structures/channels/threads/Member.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/* eslint-disable no-useless-call */
|
||||
import type { BigString, DiscordThreadMember } from "@discordeno/types"
|
||||
import Base from "../../../Base.js"
|
||||
import type Client from "../../../Client.js"
|
||||
import type Member from "../../guilds/Member.js"
|
||||
|
||||
|
||||
export class ThreadMember extends Base {
|
||||
client: Client
|
||||
/** The user-thread settings of this member */
|
||||
flags: number
|
||||
/** The ID of the thread this member is a part of */
|
||||
threadID: BigString
|
||||
/** Timestamp of when the member joined the thread */
|
||||
joinTimestamp: number
|
||||
/** The guild member that this thread member belongs to. This will never be present when fetching over REST */
|
||||
guildMember?: Member
|
||||
|
||||
constructor(data: DiscordThreadMember, client: Client) {
|
||||
super(data.user_id)
|
||||
|
||||
this.client = client
|
||||
this.flags = data.flags
|
||||
this.threadID = data.id
|
||||
this.joinTimestamp = Date.parse(data.join_timestamp)
|
||||
}
|
||||
|
||||
get _client(): Client {
|
||||
return this.client
|
||||
}
|
||||
|
||||
/** Remove the member from the thread */
|
||||
async leave(): Promise<void> {
|
||||
return await this._client.leaveThread.call(this._client, this.threadID, this.id)
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON(['threadID', 'joinTimestamp', ...props])
|
||||
}
|
||||
}
|
||||
|
||||
export default ThreadMember
|
||||
@@ -0,0 +1,5 @@
|
||||
import ThreadChannel from './Thread.js'
|
||||
|
||||
export class NewsThreadChannel extends ThreadChannel {}
|
||||
|
||||
export default NewsThreadChannel
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { DiscordChannel } from "@discordeno/types";
|
||||
import type Client from "../../../Client.js";
|
||||
import ThreadChannel from "./Thread.js";
|
||||
|
||||
|
||||
export class PrivateThreadChannel extends ThreadChannel {
|
||||
constructor(data: DiscordChannel, client: Client, messageLimit?: number) {
|
||||
super(data, client, messageLimit);
|
||||
|
||||
this.update(data);
|
||||
}
|
||||
|
||||
update(data: DiscordChannel): void {
|
||||
if(data.thread_metadata !== undefined) {
|
||||
this.threadMetadata = {
|
||||
archiveTimestamp: Date.parse(data.thread_metadata.archive_timestamp),
|
||||
archived: data.thread_metadata.archived,
|
||||
autoArchiveDuration: data.thread_metadata.auto_archive_duration,
|
||||
invitable: data.thread_metadata.invitable,
|
||||
locked: data.thread_metadata.locked
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default PrivateThreadChannel;
|
||||
@@ -0,0 +1,5 @@
|
||||
import ThreadChannel from './Thread.js'
|
||||
|
||||
export class PublicThreadChannel extends ThreadChannel {}
|
||||
|
||||
export default PublicThreadChannel
|
||||
180
packages/client/src/Structures/channels/threads/Thread.ts
Normal file
180
packages/client/src/Structures/channels/threads/Thread.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable no-useless-call */
|
||||
import type { BigString, DiscordChannel, GetMessagesOptions } from '@discordeno/types'
|
||||
import type Client from '../../../Client.js'
|
||||
import Collection from '../../../Collection.js'
|
||||
import type { FileContent, GetMessageReactionOptions, MessageContent, MessageContentEdit, PurgeChannelOptions } from '../../../typings.js'
|
||||
import type Message from '../../Message.js'
|
||||
import type User from '../../users/User.js'
|
||||
import GuildChannel from '../Guild.js'
|
||||
import ThreadMember from './Member.js'
|
||||
|
||||
export class ThreadChannel extends GuildChannel {
|
||||
/** The cached messages that were sent in this channel. */
|
||||
messages: Collection<BigString, Message>
|
||||
/** The cached thread members that are in this channel. */
|
||||
members: Collection<BigString, ThreadMember>
|
||||
/** The id of the last message in this channel. */
|
||||
lastMessageID: BigString | null
|
||||
/** The id of the user who created this thread. */
|
||||
ownerID: BigString
|
||||
/** The approximate amount of members that have joined this thread. */
|
||||
memberCount?: number
|
||||
/** The approximate amount of messages in this channel. */
|
||||
messageCount?: number
|
||||
/** The rate limit that users can send messages in this channel. 0 means no rate limit has been enabled. */
|
||||
rateLimitPerUser?: number
|
||||
/** The data relevant to this thread. */
|
||||
threadMetadata?: {
|
||||
/** Timestamp when the thread's archive status was last changed, used for calculating recent activity */
|
||||
archiveTimestamp: number
|
||||
/** Whether the thread is archived. */
|
||||
archived: boolean
|
||||
/** Duration in minutes to automatically archive the thread after recent activity, either 60, 1440, 4320 or 10080 */
|
||||
autoArchiveDuration: number
|
||||
/** Whether the thread is locked. */
|
||||
locked: boolean
|
||||
/** Whether or not the thread is inviteable. */
|
||||
invitable?: boolean
|
||||
}
|
||||
|
||||
/** The bot's thread member object if it has joined the thread. */
|
||||
member?: ThreadMember
|
||||
|
||||
constructor(data: DiscordChannel, client: Client, messageLimit?: number) {
|
||||
super(data, client)
|
||||
|
||||
this.members = new Collection()
|
||||
this.messages = new Collection()
|
||||
|
||||
this.messages.limit = messageLimit ?? client.options.messageLimit
|
||||
this.lastMessageID = data.last_message_id ?? null
|
||||
this.ownerID = data.owner_id!
|
||||
|
||||
this.update(data)
|
||||
}
|
||||
|
||||
update(data: DiscordChannel): void {
|
||||
super.update(data)
|
||||
|
||||
if (data.member_count !== undefined) {
|
||||
this.memberCount = data.member_count
|
||||
}
|
||||
if (data.message_count !== undefined) {
|
||||
this.messageCount = data.message_count
|
||||
}
|
||||
if (data.rate_limit_per_user !== undefined) {
|
||||
this.rateLimitPerUser = data.rate_limit_per_user
|
||||
}
|
||||
if (data.thread_metadata !== undefined) {
|
||||
this.threadMetadata = {
|
||||
archiveTimestamp: Date.parse(data.thread_metadata.archive_timestamp),
|
||||
archived: data.thread_metadata.archived,
|
||||
autoArchiveDuration: data.thread_metadata.auto_archive_duration,
|
||||
locked: data.thread_metadata.locked,
|
||||
}
|
||||
}
|
||||
if (data.member !== undefined) {
|
||||
this.member = new ThreadMember(data.member, this.client)
|
||||
}
|
||||
}
|
||||
|
||||
async addMessageReaction(messageID: BigString, reaction: string): Promise<void> {
|
||||
return await this.client.addMessageReaction.call(this.client, this.id, messageID, reaction)
|
||||
}
|
||||
|
||||
async createMessage(content: MessageContent, file?: FileContent | FileContent[]) {
|
||||
return await this.client.createMessage.call(this.client, this.id, content, file)
|
||||
}
|
||||
|
||||
async deleteMessage(messageID: BigString, reason?: string): Promise<void> {
|
||||
return await this.client.deleteMessage.call(this.client, this.id, messageID, reason)
|
||||
}
|
||||
|
||||
async deleteMessages(messageIDs: BigString[], reason?: string): Promise<void> {
|
||||
return await this.client.deleteMessages.call(this.client, this.id, messageIDs, reason)
|
||||
}
|
||||
|
||||
async editMessage(messageID: BigString, content: MessageContentEdit) {
|
||||
return await this.client.editMessage.call(this.client, this.id, messageID, content)
|
||||
}
|
||||
|
||||
async getMembers(): Promise<ThreadMember[]> {
|
||||
return await this.client.getThreadMembers.call(this.client, this.id)
|
||||
}
|
||||
|
||||
async getMessage(messageID: BigString): Promise<Message> {
|
||||
return await this.client.getMessage.call(this.client, this.id, messageID)
|
||||
}
|
||||
|
||||
async getMessageReaction(messageID: BigString, reaction: string, options?: GetMessageReactionOptions): Promise<User[]> {
|
||||
return await this.client.getMessageReaction.call(this.client, this.id, messageID, reaction, options)
|
||||
}
|
||||
|
||||
async getMessages(options: GetMessagesOptions) {
|
||||
return await this.client.getMessages.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
async getPins(): Promise<Message[]> {
|
||||
return await this.client.getPins.call(this.client, this.id)
|
||||
}
|
||||
|
||||
async join(userID: BigString = '@me'): Promise<void> {
|
||||
return await this.client.joinThread.call(this.client, this.id, userID)
|
||||
}
|
||||
|
||||
async leave(userID: BigString): Promise<void> {
|
||||
return await this.client.leaveThread.call(this.client, this.id, userID)
|
||||
}
|
||||
|
||||
async pinMessage(messageID: BigString): Promise<void> {
|
||||
return await this.client.pinMessage.call(this.client, this.id, messageID)
|
||||
}
|
||||
|
||||
async purge(options: PurgeChannelOptions): Promise<number> {
|
||||
return await this.client.purgeChannel.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
async removeMessageReaction(messageID: BigString, reaction: string, userID: BigString = '@me') {
|
||||
return await this.client.removeMessageReaction.call(this.client, this.id, messageID, reaction, userID)
|
||||
}
|
||||
|
||||
async removeMessageReactionEmoji(messageID: BigString, reaction: string): Promise<void> {
|
||||
return await this.client.removeMessageReactionEmoji.call(this.client, this.id, messageID, reaction)
|
||||
}
|
||||
|
||||
async removeMessageReactions(messageID: BigString): Promise<void> {
|
||||
return await this.client.removeMessageReactions.call(this.client, this.id, messageID)
|
||||
}
|
||||
|
||||
async sendTyping(): Promise<void> {
|
||||
return await this.client.sendChannelTyping.call(this.client, this.id)
|
||||
}
|
||||
|
||||
async unpinMessage(messageID: BigString): Promise<void> {
|
||||
return await this.client.unpinMessage.call(this.client, this.id, messageID)
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use deleteMessage instead
|
||||
*/
|
||||
async unsendMessage(messageID: BigString): Promise<void> {
|
||||
return await this.client.deleteMessage.call(this.client, this.id, messageID)
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON([
|
||||
'lastMessageID',
|
||||
'memberCount',
|
||||
'messageCount',
|
||||
'messages',
|
||||
'ownerID',
|
||||
'rateLimitPerUser',
|
||||
'threadMetadata',
|
||||
'member',
|
||||
...props,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export default ThreadChannel
|
||||
181
packages/client/src/Structures/guilds/AuditLogEntry.ts
Normal file
181
packages/client/src/Structures/guilds/AuditLogEntry.ts
Normal file
@@ -0,0 +1,181 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import type { DiscordAuditLogChange, DiscordAuditLogEntry } from '@discordeno/types'
|
||||
import { AuditLogEvents } from '@discordeno/types'
|
||||
import Base from '../../Base.js'
|
||||
import type GuildChannel from '../channels/Guild.js'
|
||||
import type TextChannel from '../channels/Text.js'
|
||||
import Invite from '../Invite.js'
|
||||
import type Message from '../Message.js'
|
||||
import type User from '../users/User.js'
|
||||
import type Guild from './Guild.js'
|
||||
import type Member from './Member.js'
|
||||
import type Role from './Role.js'
|
||||
|
||||
export class GuildAuditLogEntry extends Base {
|
||||
/** The guild to which this entry belongs. */
|
||||
guild: Guild
|
||||
/** The action type of the entry. */
|
||||
actionType: AuditLogEvents
|
||||
/** The reason for the action. */
|
||||
reason: string | null
|
||||
/** The user that performed the action. */
|
||||
user?: User
|
||||
/** The properties of the targeted object before the action was taken. For example, if a channel was renamed from #general to #potato, this would be `{name: "general"}`` */
|
||||
before: Record<DiscordAuditLogChange['key'], DiscordAuditLogChange['old_value']>
|
||||
|
||||
/** The properties of the targeted object after the action was taken. For example, if a channel was renamed from #general to #potato, this would be `{name: "potato"}`` */
|
||||
after: Record<DiscordAuditLogChange['key'], DiscordAuditLogChange['new_value']>
|
||||
|
||||
/** The ID of the action target */
|
||||
targetID?: string
|
||||
/** The number of entities targeted. For example, for action type 26 (MEMBER_MOVE), this is the number of members that were moved/disconnected from the voice channel */
|
||||
count?: number
|
||||
/** The channel targeted in the entry, action types 26 (MEMBER_MOVE), 72/74/75 (MESSAGE_DELETE/PIN/UNPIN) and 83/84/85 (STAGE_INSTANCE_CREATE/UPDATE/DELETE) only */
|
||||
channel?: GuildChannel
|
||||
/** The message that was (un)pinned, action types 74/75 (MESSAGE_PIN/UNPIN) only. If the message is not cached, this will be an object with an `id` key. No other property is guaranteed. */
|
||||
message?: Message | { id: string }
|
||||
/** The number of days of inactivity to prune for, action type 21 (MEMBER_PRUNE) only */
|
||||
deleteMemberDays?: number
|
||||
/** The number of members pruned from the server, action type 21 (MEMBER_PRUNE) only */
|
||||
membersRemoved?: number
|
||||
/** The member described by the permission overwrite, action types 13-15 (CHANNEL\_OVERWRITE\_CREATE/UPDATE/DELETE) only. If the member is not cached, this could be {id: String} */
|
||||
member?: Member | { id: string }
|
||||
/** The role described by the permission overwrite, action types 13-15 (CHANNEL\_OVERWRITE\_CREATE/UPDATE/DELETE) only. If the role is not cached, this could be {id: String, name: String} */
|
||||
role?: Role | { id: string; name: string }
|
||||
|
||||
constructor(data: DiscordAuditLogEntry, guild: Guild) {
|
||||
super(data.id)
|
||||
|
||||
this.guild = guild
|
||||
this.actionType = data.action_type
|
||||
this.reason = data.reason ?? null
|
||||
this.user = data.user_id ? guild.client.users.get(data.user_id) : undefined
|
||||
this.before = {} as any
|
||||
this.after = {} as any
|
||||
if (data.changes) {
|
||||
data.changes.forEach((change) => {
|
||||
if (change.old_value !== undefined) {
|
||||
this.before[change.key] = change.old_value
|
||||
}
|
||||
if (change.new_value !== undefined) {
|
||||
this.after[change.key] = change.new_value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (data.target_id) {
|
||||
this.targetID = data.target_id
|
||||
}
|
||||
if (data.options) {
|
||||
if (data.options.count) {
|
||||
this.count = +data.options.count
|
||||
}
|
||||
if (data.options.channel_id) {
|
||||
if (this.actionType >= 83) {
|
||||
this.channel = guild.threads.get(data.options.channel_id)
|
||||
} else {
|
||||
this.channel = guild.channels.get(data.options.channel_id)
|
||||
}
|
||||
if (data.options.message_id) {
|
||||
this.message = (this.channel && (this.channel as TextChannel).messages.get(data.options.message_id)) ?? {
|
||||
id: data.options.message_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data.options.delete_member_days) {
|
||||
this.deleteMemberDays = +data.options.delete_member_days
|
||||
this.membersRemoved = +data.options.members_removed
|
||||
}
|
||||
if (data.options.type) {
|
||||
if (data.options.type === '1') {
|
||||
this.member = guild.members.get(data.options.id) ?? {
|
||||
id: data.options.id,
|
||||
}
|
||||
} else if (data.options.type === '0') {
|
||||
this.role = guild.roles.get(data.options.id) ?? {
|
||||
id: data.options.id,
|
||||
name: data.options.role_name,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get target() {
|
||||
// pay more, get less
|
||||
if (this.actionType < 10) {
|
||||
// Guild
|
||||
return this.guild
|
||||
} else if (this.actionType < 20) {
|
||||
// Channel
|
||||
return this.guild?.channels.get(this.targetID!)
|
||||
} else if (this.actionType < 30) {
|
||||
// Member
|
||||
if (this.actionType === AuditLogEvents.MemberMove || this.actionType === AuditLogEvents.MemberDisconnect) {
|
||||
// MEMBER_MOVE / MEMBER_DISCONNECT
|
||||
return null
|
||||
}
|
||||
return this.guild?.members.get(this.targetID!)
|
||||
} else if (this.actionType < 40) {
|
||||
// Role
|
||||
return this.guild?.roles.get(this.targetID!)
|
||||
} else if (this.actionType < 50) {
|
||||
// Invite
|
||||
const changes = this.actionType === 42 ? this.before : this.after // Apparently the meaning of life is a deleted invite
|
||||
return new Invite(
|
||||
{
|
||||
code: changes.code as string,
|
||||
// @ts-expect-error idk why this is happening
|
||||
channel: changes.channel,
|
||||
guild: this.guild.toJSON(),
|
||||
uses: changes.uses as number,
|
||||
max_uses: changes.max_uses as number,
|
||||
max_age: changes.max_age as number,
|
||||
temporary: changes.temporary as boolean,
|
||||
},
|
||||
this.guild?.client,
|
||||
)
|
||||
} else if (this.actionType < 60) {
|
||||
// Webhook
|
||||
return null // Go get the webhook yourself
|
||||
} else if (this.actionType < 70) {
|
||||
// Emoji
|
||||
return this.guild?.emojis?.find((emoji) => emoji.id === this.targetID)
|
||||
} else if (this.actionType < 80) {
|
||||
// Message
|
||||
return this.guild?.client.users.get(this.targetID!)
|
||||
} else if (this.actionType < 83) {
|
||||
// Integrations
|
||||
return null
|
||||
} else if (this.actionType < 90) {
|
||||
// Stage Instances
|
||||
return this.guild?.threads.get(this.targetID!)
|
||||
} else if (this.actionType < 100) {
|
||||
// Sticker
|
||||
return this.guild?.stickers?.find((sticker) => sticker.id === this.targetID)
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
throw new Error('Unrecognized action type: ' + this.actionType)
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON([
|
||||
'actionType',
|
||||
'after',
|
||||
'before',
|
||||
'channel',
|
||||
'count',
|
||||
'deleteMemberDays',
|
||||
'member',
|
||||
'membersRemoved',
|
||||
'reason',
|
||||
'role',
|
||||
'targetID',
|
||||
'user',
|
||||
...props,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export default GuildAuditLogEntry
|
||||
946
packages/client/src/Structures/guilds/Guild.ts
Normal file
946
packages/client/src/Structures/guilds/Guild.ts
Normal file
@@ -0,0 +1,946 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable no-useless-call */
|
||||
import {
|
||||
BitwisePermissionFlags,
|
||||
ChannelTypes,
|
||||
type ApplicationCommandTypes,
|
||||
type BigString,
|
||||
type DefaultMessageNotificationLevels,
|
||||
type DiscordEmoji,
|
||||
type DiscordGuild,
|
||||
type DiscordMemberWithUser,
|
||||
type DiscordSticker,
|
||||
type ExplicitContentFilterLevels,
|
||||
type GuildFeatures,
|
||||
type GuildNsfwLevel,
|
||||
type MfaLevels,
|
||||
type PremiumTiers,
|
||||
type SystemChannelFlags,
|
||||
type VerificationLevels,
|
||||
} from '@discordeno/types'
|
||||
import Base from '../../Base.js'
|
||||
import type Client from '../../Client.js'
|
||||
import type { ImageFormat, ImageSize } from '../../Client.js'
|
||||
import Collection from '../../Collection.js'
|
||||
import { BANNER, GUILD_DISCOVERY_SPLASH, GUILD_ICON, GUILD_SPLASH } from '../../Endpoints.js'
|
||||
import type {
|
||||
AnyGuildChannel,
|
||||
AnyThreadChannel,
|
||||
ApplicationCommand,
|
||||
ApplicationCommandPermissions,
|
||||
ApplicationCommandStructure,
|
||||
ChannelPosition,
|
||||
CreateChannelOptions,
|
||||
CreateStickerOptions,
|
||||
DiscoveryMetadata,
|
||||
DiscoveryOptions,
|
||||
DiscoverySubcategoryResponse,
|
||||
EditStickerOptions,
|
||||
Emoji,
|
||||
EmojiOptions,
|
||||
GetGuildAuditLogOptions,
|
||||
GetGuildBansOptions,
|
||||
GetPruneOptions,
|
||||
GetRESTGuildMembersOptions,
|
||||
GuildApplicationCommandPermissions,
|
||||
GuildAuditLog,
|
||||
GuildBan,
|
||||
GuildOptions,
|
||||
GuildTemplateOptions,
|
||||
GuildVanity,
|
||||
IntegrationOptions,
|
||||
ListedGuildThreads,
|
||||
MemberOptions,
|
||||
PruneMemberOptions,
|
||||
RoleOptions,
|
||||
Sticker,
|
||||
VoiceRegion,
|
||||
VoiceStateOptions,
|
||||
Webhook,
|
||||
WelcomeScreen,
|
||||
WelcomeScreenOptions,
|
||||
Widget,
|
||||
WidgetData,
|
||||
} from '../../typings.js'
|
||||
import type CategoryChannel from '../channels/Category.js'
|
||||
import Channel from '../channels/Channel.js'
|
||||
import type GuildChannel from '../channels/Guild.js'
|
||||
import type StageChannel from '../channels/Stage.js'
|
||||
import type TextChannel from '../channels/Text.js'
|
||||
import type TextVoiceChannel from '../channels/TextVoice.js'
|
||||
import type ThreadChannel from '../channels/threads/Thread.js'
|
||||
import type VoiceChannel from '../channels/Voice.js'
|
||||
import type Invite from '../Invite.js'
|
||||
import Permission from '../Permission.js'
|
||||
import User from '../users/User.js'
|
||||
import type GuildIntegration from './Integration.js'
|
||||
import Member from './Member.js'
|
||||
import Role from './Role.js'
|
||||
import StageInstance from './StageInstance.js'
|
||||
import type GuildTemplate from './Template.js'
|
||||
import type { VoiceState } from './VoiceState.js'
|
||||
|
||||
export class Guild extends Base {
|
||||
/** The client object */
|
||||
client: Client
|
||||
/** The id of the guild owner. */
|
||||
ownerID: BigString
|
||||
/** The id of the application. */
|
||||
applicationID?: BigString | null
|
||||
/** The id of the widget channel. */
|
||||
widgetChannelID?: BigString | null
|
||||
/** The afk channel id if one is set. */
|
||||
afkChannelID?: BigString | null
|
||||
/** The system channel id if one is set. */
|
||||
systemChannelID?: BigString | null
|
||||
/** The public updates channel id if one is set. */
|
||||
publicUpdatesChannelID?: BigString | null
|
||||
/** The rules channel id if one is set. */
|
||||
rulesChannelID?: BigString | null
|
||||
/** The name of the guild. */
|
||||
name?: string
|
||||
/** The description of the guild. */
|
||||
description?: string | null
|
||||
/** The vanity url if one is set. */
|
||||
vanityURL?: string | null
|
||||
/** The preferred locale of the server. */
|
||||
preferredLocale?: string
|
||||
/** The system channel flags. */
|
||||
systemChannelFlags?: SystemChannelFlags
|
||||
/** The verification level of the guild. */
|
||||
verificationLevel?: VerificationLevels
|
||||
/** The default notification level. */
|
||||
defaultNotifications?: DefaultMessageNotificationLevels
|
||||
/** The explicit content filter setting for this guild. */
|
||||
explicitContentFilter?: ExplicitContentFilterLevels
|
||||
/** Array of guild features */
|
||||
features: GuildFeatures[] = []
|
||||
/** The premium tier of the guild. */
|
||||
premiumTier?: PremiumTiers
|
||||
/** The MFA level of the guild. */
|
||||
mfaLevel?: MfaLevels
|
||||
/** The NSFW level of the guild. */
|
||||
nsfwLevel?: GuildNsfwLevel
|
||||
/** The compressed form of the guild splash image. */
|
||||
_splash?: bigint
|
||||
/** The compressed form of the guild's discovery splash image. */
|
||||
_discoverySplash?: bigint
|
||||
/** The compressed form of the guild's banner image. */
|
||||
_banner?: bigint
|
||||
/** The compressed form of the guild's icon image. */
|
||||
_icon?: bigint
|
||||
/** The cached emojis in the guild. */
|
||||
emojis?: DiscordEmoji[]
|
||||
/** The cached stickers in the guild. */
|
||||
stickers?: DiscordSticker[]
|
||||
/** The afk timeout in seconds. */
|
||||
afkTimeout?: number
|
||||
/** When this guild was joined at. */
|
||||
joinedAt: number
|
||||
/** The amount of members in the guild. */
|
||||
memberCount?: number
|
||||
/** The approximate member count in the guild. */
|
||||
approximateMemberCount?: number
|
||||
/** The approximate presence count in the guild. */
|
||||
approximatePresenceCount?: number
|
||||
/** The amount of subscribers to the server. */
|
||||
premiumSubscriptionCount?: number
|
||||
/** The maximum amount of presences that can be in a guild. */
|
||||
maxPresences?: number | null
|
||||
/** The maximum amount of members that can be in the guild. */
|
||||
maxMembers?: number
|
||||
/** The maximum amount of members that can be in a video channel. */
|
||||
maxVideoChannelUsers?: number | null
|
||||
/** Whether or not this guild is unavailable. */
|
||||
unavailable: boolean
|
||||
/** Whether or not the widget is enabled in this guild. */
|
||||
widgetEnabled: boolean
|
||||
/** Whether or not this guild is considered large. */
|
||||
large?: boolean
|
||||
/** Whether or not the premium progress bar is enabled. */
|
||||
premiumProgressBarEnabled?: boolean
|
||||
/** Whether or not this server is nsfw. */
|
||||
nsfw?: boolean
|
||||
/** The welcome screen settings. */
|
||||
welcomeScreen?: {
|
||||
description: string | null
|
||||
welcomeChannels?: Array<{
|
||||
channelID: string
|
||||
description: string
|
||||
emojiID: string | null
|
||||
emojiName: string | null
|
||||
}>
|
||||
}
|
||||
|
||||
/** The cached members in this guild. */
|
||||
members = new Collection<BigString, Member>()
|
||||
/** The cached roles in this guild. */
|
||||
roles = new Collection<BigString, Role>()
|
||||
/** The cached channels in this guild. */
|
||||
channels = new Collection<BigString, GuildChannel>()
|
||||
/** The cached threads in this guild. */
|
||||
threads = new Collection<BigString, ThreadChannel>()
|
||||
/** The cached voice states in this guild. */
|
||||
voiceStates = new Collection<BigString, VoiceState>()
|
||||
/** The cached stage instances in this guild. */
|
||||
stageInstances = new Collection<BigString, StageInstance>()
|
||||
|
||||
constructor(data: DiscordGuild, client: Client) {
|
||||
super(data.id)
|
||||
this.client = client
|
||||
this.ownerID = data.owner_id
|
||||
|
||||
this.unavailable = !!data.unavailable
|
||||
this.joinedAt = Date.parse(data.joined_at!)
|
||||
this.memberCount = data.member_count
|
||||
this.applicationID = data.application_id
|
||||
this.widgetEnabled = !!data.widget_enabled
|
||||
|
||||
if (data.widget_channel_id !== undefined) {
|
||||
this.widgetChannelID = data.widget_channel_id
|
||||
}
|
||||
|
||||
if (data.approximate_member_count !== undefined) {
|
||||
this.approximateMemberCount = data.approximate_member_count
|
||||
}
|
||||
if (data.approximate_presence_count !== undefined) {
|
||||
this.approximatePresenceCount = data.approximate_presence_count
|
||||
}
|
||||
|
||||
if (data.roles) {
|
||||
for (const r of data.roles) {
|
||||
const role = new Role(r, this)
|
||||
this.roles.set(role.id, role)
|
||||
}
|
||||
}
|
||||
|
||||
if (data.channels) {
|
||||
for (const channelData of data.channels) {
|
||||
channelData.guild_id = this.id.toString()
|
||||
const channel = Channel.from(channelData, client) as GuildChannel
|
||||
this.channels.set(channel.id, channel)
|
||||
client._channelGuildMap.set(channel.id, this.id)
|
||||
}
|
||||
}
|
||||
|
||||
if (data.threads) {
|
||||
for (const threadData of data.threads) {
|
||||
threadData.guild_id = this.id.toString()
|
||||
const thread = Channel.from(threadData, client) as ThreadChannel
|
||||
this.threads.set(thread.id, thread)
|
||||
client._threadGuildMap.set(thread.id, this.id)
|
||||
}
|
||||
}
|
||||
|
||||
if (data.members) {
|
||||
for (const m of data.members) {
|
||||
const member = new Member(m as DiscordMemberWithUser, this, client)
|
||||
this.members.set(member.id, member)
|
||||
}
|
||||
}
|
||||
|
||||
if (data.stage_instances) {
|
||||
for (const stageInstance of data.stage_instances) {
|
||||
stageInstance.guild_id = this.id
|
||||
|
||||
const instance = new StageInstance(stageInstance, client)
|
||||
this.stageInstances.set(instance.id, instance)
|
||||
}
|
||||
}
|
||||
|
||||
if (data.presences) {
|
||||
for (const presence of data.presences) {
|
||||
if (presence.user?.id) {
|
||||
const cached = this.client.users.get(presence.user.id)
|
||||
if (cached) cached.update(presence.user)
|
||||
else {
|
||||
const user = new User(presence.user, this.client)
|
||||
this.client.users.set(user.id, user)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.voice_states) {
|
||||
for (const voiceState of data.voice_states) {
|
||||
if (!this.members.get(voiceState.user_id)) continue
|
||||
|
||||
if (voiceState.member) {
|
||||
const member = new Member(voiceState.member, this, client)
|
||||
this.members.set(member.id, member)
|
||||
const user = new User(voiceState.member.user, client)
|
||||
this.client.users.set(user.id, user)
|
||||
|
||||
// TODO: check channel type maybe voice channel?
|
||||
;(this.channels.get(voiceState.channel_id!) as VoiceChannel)?.voiceMembers.set(member.id, member)
|
||||
}
|
||||
|
||||
// TODO: voice support
|
||||
// if (
|
||||
// client.options.seedVoiceConnections &&
|
||||
// voiceState.user_id === client.id &&
|
||||
// !client.voiceConnections.get(this.id)
|
||||
// ) {
|
||||
// process.nextTick(() =>
|
||||
// this.client.joinVoiceChannel(voiceState.channel_id)
|
||||
// );
|
||||
// }
|
||||
}
|
||||
}
|
||||
this.update(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated - please use .client
|
||||
*/
|
||||
get _client() {
|
||||
return this.client
|
||||
}
|
||||
|
||||
update(data: DiscordGuild) {
|
||||
if (data.name !== undefined) {
|
||||
this.name = data.name
|
||||
}
|
||||
if (data.verification_level !== undefined) {
|
||||
this.verificationLevel = data.verification_level
|
||||
}
|
||||
if (data.splash !== undefined) {
|
||||
this._splash = data.splash ? this.client.iconHashToBigInt(data.splash) : undefined
|
||||
}
|
||||
|
||||
if (data.discovery_splash !== undefined) {
|
||||
this._discoverySplash = data.discovery_splash ? this.client.iconHashToBigInt(data.discovery_splash) : undefined
|
||||
}
|
||||
|
||||
if (data.banner !== undefined) {
|
||||
this._banner = data.banner ? this.client.iconHashToBigInt(data.banner) : undefined
|
||||
}
|
||||
|
||||
if (data.owner_id !== undefined) {
|
||||
this.ownerID = data.owner_id
|
||||
}
|
||||
|
||||
if (data.icon !== undefined) {
|
||||
this._icon = data.icon ? this.client.iconHashToBigInt(data.icon) : undefined
|
||||
}
|
||||
|
||||
// TODO: compress features.
|
||||
if (data.features !== undefined) {
|
||||
this.features = data.features
|
||||
}
|
||||
|
||||
if (data.emojis !== undefined) {
|
||||
this.emojis = data.emojis
|
||||
}
|
||||
|
||||
if (data.stickers !== undefined) {
|
||||
this.stickers = data.stickers
|
||||
}
|
||||
|
||||
if (data.afk_channel_id !== undefined) {
|
||||
this.afkChannelID = data.afk_channel_id
|
||||
}
|
||||
|
||||
if (data.afk_timeout !== undefined) {
|
||||
this.afkTimeout = data.afk_timeout
|
||||
}
|
||||
|
||||
if (data.default_message_notifications !== undefined) {
|
||||
this.defaultNotifications = data.default_message_notifications
|
||||
}
|
||||
|
||||
if (data.mfa_level !== undefined) {
|
||||
this.mfaLevel = data.mfa_level
|
||||
}
|
||||
|
||||
if (data.large !== undefined) {
|
||||
this.large = data.large
|
||||
}
|
||||
|
||||
if (data.max_presences !== undefined) {
|
||||
this.maxPresences = data.max_presences
|
||||
}
|
||||
|
||||
if (data.explicit_content_filter !== undefined) {
|
||||
this.explicitContentFilter = data.explicit_content_filter
|
||||
}
|
||||
|
||||
if (data.system_channel_id !== undefined) {
|
||||
this.systemChannelID = data.system_channel_id
|
||||
}
|
||||
|
||||
if (data.system_channel_flags !== undefined) {
|
||||
this.systemChannelFlags = data.system_channel_flags
|
||||
}
|
||||
if (data.premium_progress_bar_enabled !== undefined) {
|
||||
this.premiumProgressBarEnabled = data.premium_progress_bar_enabled
|
||||
}
|
||||
if (data.premium_tier !== undefined) {
|
||||
this.premiumTier = data.premium_tier
|
||||
}
|
||||
if (data.premium_subscription_count !== undefined) {
|
||||
this.premiumSubscriptionCount = data.premium_subscription_count
|
||||
}
|
||||
if (data.vanity_url_code !== undefined) {
|
||||
this.vanityURL = data.vanity_url_code
|
||||
}
|
||||
if (data.preferred_locale !== undefined) {
|
||||
this.preferredLocale = data.preferred_locale
|
||||
}
|
||||
if (data.description !== undefined) {
|
||||
this.description = data.description
|
||||
}
|
||||
if (data.max_members !== undefined) {
|
||||
this.maxMembers = data.max_members
|
||||
}
|
||||
if (data.public_updates_channel_id !== undefined) {
|
||||
this.publicUpdatesChannelID = data.public_updates_channel_id
|
||||
}
|
||||
if (data.rules_channel_id !== undefined) {
|
||||
this.rulesChannelID = data.rules_channel_id
|
||||
}
|
||||
if (data.max_video_channel_users !== undefined) {
|
||||
this.maxVideoChannelUsers = data.max_video_channel_users
|
||||
}
|
||||
if (data.welcome_screen !== undefined) {
|
||||
this.welcomeScreen = {
|
||||
description: data.welcome_screen.description,
|
||||
welcomeChannels: data.welcome_screen.welcome_channels?.map((c) => {
|
||||
return {
|
||||
channelID: c.channel_id,
|
||||
description: c.description,
|
||||
emojiID: c.emoji_id,
|
||||
emojiName: c.emoji_name,
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
// if (data.nsfw !== undefined) {
|
||||
// this.nsfw = data.nsfw;
|
||||
// }
|
||||
if (data.nsfw_level !== undefined) {
|
||||
this.nsfwLevel = data.nsfw_level
|
||||
}
|
||||
}
|
||||
|
||||
get banner(): string | undefined {
|
||||
return this._banner ? this.client.iconBigintToHash(this._banner) : undefined
|
||||
}
|
||||
|
||||
get bannerURL(): string | null {
|
||||
return this.banner ? this.client._formatImage(BANNER(this.id, this.banner)) : null
|
||||
}
|
||||
|
||||
get icon(): string | undefined {
|
||||
return this._icon ? this.client.iconBigintToHash(this._icon) : undefined
|
||||
}
|
||||
|
||||
get iconURL(): string | null {
|
||||
return this.icon ? this.client._formatImage(GUILD_ICON(this.id, this.icon)) : null
|
||||
}
|
||||
|
||||
get splash(): string | undefined {
|
||||
return this._splash ? this.client.iconBigintToHash(this._splash) : undefined
|
||||
}
|
||||
|
||||
get splashURL(): string | null {
|
||||
return this.splash ? this.client._formatImage(GUILD_SPLASH(this.id, this.splash)) : null
|
||||
}
|
||||
|
||||
get discoverySplash(): string | undefined {
|
||||
return this._discoverySplash ? this.client.iconBigintToHash(this._discoverySplash) : undefined
|
||||
}
|
||||
|
||||
get discoverySplashURL(): string | null {
|
||||
return this.discoverySplash ? this.client._formatImage(GUILD_DISCOVERY_SPLASH(this.id, this.discoverySplash)) : null
|
||||
}
|
||||
|
||||
/** Add a discovery subcategory */
|
||||
async addDiscoverySubcategory(categoryID: BigString, reason?: string): Promise<DiscoverySubcategoryResponse> {
|
||||
return await this.client.addGuildDiscoverySubcategory.call(this.client, this.id, categoryID, reason)
|
||||
}
|
||||
|
||||
/** Add a role to a guild member */
|
||||
async addMemberRole(memberID: BigString, roleID: BigString, reason?: string): Promise<void> {
|
||||
return await this.client.addGuildMemberRole.call(this.client, this.id, memberID, roleID, reason)
|
||||
}
|
||||
|
||||
/** Ban a user from the guild */
|
||||
async banMember(userID: BigString, deleteMessageDays = 0, reason?: string): Promise<void> {
|
||||
return await this.client.banGuildMember.call(this.client, this.id, userID, deleteMessageDays, reason)
|
||||
}
|
||||
|
||||
/** Bulk create/edit guild application commands */
|
||||
async bulkEditCommands(commands: Array<ApplicationCommand<ApplicationCommandTypes>>): Promise<Array<ApplicationCommand<ApplicationCommandTypes>>> {
|
||||
return await this.client.bulkEditGuildCommands.call(this.client, this.id, commands)
|
||||
}
|
||||
|
||||
/** Create a channel in the guild */
|
||||
async createChannel(
|
||||
name: string,
|
||||
type = ChannelTypes.GuildText,
|
||||
options: CreateChannelOptions,
|
||||
): Promise<CategoryChannel | TextChannel | TextVoiceChannel | StageChannel> {
|
||||
return await this.client.createChannel.call(this.client, this.id, name, type as number, options)
|
||||
}
|
||||
|
||||
/** Create a guild application command */
|
||||
async createCommand(command: ApplicationCommandStructure): Promise<ApplicationCommand<ApplicationCommandTypes>> {
|
||||
return await this.client.createGuildCommand.call(this.client, this.id, command)
|
||||
}
|
||||
|
||||
/** Create a emoji in the guild */
|
||||
async createEmoji(options: EmojiOptions, reason?: string): Promise<Emoji> {
|
||||
return await this.client.createGuildEmoji.call(this.client, this.id, options, reason)
|
||||
}
|
||||
|
||||
/** Create a guild role */
|
||||
async createRole(options: Role | RoleOptions, reason?: string): Promise<Role> {
|
||||
return await this.client.createRole.call(this.client, this.id, options, reason)
|
||||
}
|
||||
|
||||
/** Create a guild sticker */
|
||||
async createSticker(options: CreateStickerOptions, reason?: string): Promise<Sticker> {
|
||||
return await this.client.createGuildSticker.call(this.client, this.id, options, reason)
|
||||
}
|
||||
|
||||
/** Create a template for this guild */
|
||||
async createTemplate(name: string, description?: string): Promise<GuildTemplate> {
|
||||
return await this.client.createGuildTemplate.call(this.client, this.id, name, description)
|
||||
}
|
||||
|
||||
/** Delete the guild (bot user must be owner) */
|
||||
async delete(): Promise<void> {
|
||||
if (this.ownerID !== this.client.id) throw new Error('To delete a guild, the bot must be the owner of the guild.')
|
||||
|
||||
return await this.client.deleteGuild.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Delete a guild application command */
|
||||
async deleteCommand(commandID: BigString): Promise<void> {
|
||||
return await this.client.deleteGuildCommand.call(this.client, this.id, commandID)
|
||||
}
|
||||
|
||||
/** Delete a discovery subcategory */
|
||||
async deleteDiscoverySubcategory(categoryID: BigString, reason?: string): Promise<void> {
|
||||
return await this.client.deleteGuildDiscoverySubcategory.call(this.client, this.id, categoryID, reason)
|
||||
}
|
||||
|
||||
/** Delete a emoji in the guild */
|
||||
async deleteEmoji(emojiID: BigString, reason?: string): Promise<void> {
|
||||
return await this.client.deleteGuildEmoji.call(this.client, this.id, emojiID, reason)
|
||||
}
|
||||
|
||||
/** Delete a guild integration */
|
||||
async deleteIntegration(integrationID: BigString): Promise<void> {
|
||||
return await this.client.deleteGuildIntegration.call(this.client, this.id, integrationID)
|
||||
}
|
||||
|
||||
/** Delete a role */
|
||||
async deleteRole(roleID: BigString, reason?: string): Promise<void> {
|
||||
return await this.client.deleteRole.call(this.client, this.id, roleID, reason)
|
||||
}
|
||||
|
||||
/** Delete a guild sticker */
|
||||
async deleteSticker(stickerID: BigString, reason?: string): Promise<void> {
|
||||
return await this.client.deleteGuildSticker.call(this.client, this.id, stickerID, reason)
|
||||
}
|
||||
|
||||
/** Delete a guild template */
|
||||
async deleteTemplate(code: string): Promise<void> {
|
||||
return await this.client.deleteGuildTemplate.call(this.client, this.id, code)
|
||||
}
|
||||
|
||||
/** Get the guild's banner with the given format and size */
|
||||
dynamicBannerURL(format?: ImageFormat, size?: ImageSize): string | null {
|
||||
return this.banner ? this.client._formatImage(BANNER(this.id, this.banner), format, size) : null
|
||||
}
|
||||
|
||||
/** Get the guild's discovery splash with the given format and size */
|
||||
dynamicDiscoverySplashURL(format?: ImageFormat, size?: ImageSize): string | null {
|
||||
return this.discoverySplash ? this.client._formatImage(GUILD_DISCOVERY_SPLASH(this.id, this.discoverySplash), format, size) : null
|
||||
}
|
||||
|
||||
/** Get the guild's icon with the given format and size */
|
||||
dynamicIconURL(format?: ImageFormat, size?: ImageSize): string | null {
|
||||
return this.icon ? this.client._formatImage(GUILD_ICON(this.id, this.icon), format, size) : null
|
||||
}
|
||||
|
||||
/** Get the guild's splash with the given format and size */
|
||||
dynamicSplashURL(format?: ImageFormat, size?: ImageSize): string | null {
|
||||
return this.splash ? this.client._formatImage(GUILD_SPLASH(this.id, this.splash), format, size) : null
|
||||
}
|
||||
|
||||
/** Edit the guild */
|
||||
async edit(options: GuildOptions, reason?: string): Promise<Guild> {
|
||||
return await this.client.editGuild.call(this.client, this.id, options, reason)
|
||||
}
|
||||
|
||||
/** Edit multiple channels' positions. Note that channel position numbers are grouped by type (category, text, voice), then sorted in ascending order (lowest number is on top). */
|
||||
async editChannelPositions(channelPositions: ChannelPosition[]): Promise<void> {
|
||||
return await this.client.editChannelPositions.call(this.client, this.id, channelPositions)
|
||||
}
|
||||
|
||||
/** Edit a guild application command */
|
||||
async editCommand(commandID: BigString, commands: ApplicationCommandStructure): Promise<ApplicationCommand<ApplicationCommandTypes>> {
|
||||
return await this.client.editGuildCommand.call(this.client, this.id, commandID, commands)
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits command permissions for a specific command in a guild.
|
||||
* Note: You can only add up to 10 permission overwrites for a command.
|
||||
*/
|
||||
async editCommandPermissions(commandID: BigString, permissions: ApplicationCommandPermissions[]): Promise<GuildApplicationCommandPermissions> {
|
||||
return await this.client.editCommandPermissions.call(this.client, this.id, commandID, permissions)
|
||||
}
|
||||
|
||||
/** Edit the guild's discovery data */
|
||||
async editDiscovery(options: DiscoveryOptions): Promise<DiscoveryMetadata> {
|
||||
return await this.client.editGuildDiscovery.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a emoji in the guild
|
||||
* @arg {String} emojiID The ID of the emoji you want to modify
|
||||
* @arg {Object} options Emoji options
|
||||
* @arg {String} [options.name] The name of emoji
|
||||
* @arg {Array} [options.roles] An array containing authorized role IDs
|
||||
* @arg {String} [reason] The reason to be displayed in audit logs
|
||||
* @returns {Promise<Object>} A guild emoji object
|
||||
*/
|
||||
async editEmoji(
|
||||
emojiID: BigString,
|
||||
options: {
|
||||
name?: string | undefined
|
||||
roles?: string[] | undefined
|
||||
},
|
||||
reason?: string,
|
||||
): Promise<Emoji> {
|
||||
return await this.client.editGuildEmoji.call(this.client, this.id, emojiID, options, reason)
|
||||
}
|
||||
|
||||
/** Edit a guild integration */
|
||||
async editIntegration(integrationID: BigString, options: IntegrationOptions): Promise<void> {
|
||||
return await this.client.editGuildIntegration.call(this.client, this.id, integrationID, options)
|
||||
}
|
||||
|
||||
/** Edit a guild member */
|
||||
async editMember(memberID: BigString, options: MemberOptions, reason?: string): Promise<Member> {
|
||||
return await this.client.editGuildMember.call(this.client, this.id, memberID, options, reason)
|
||||
}
|
||||
|
||||
/** Edit the guild role */
|
||||
async editRole(roleID: BigString, options: RoleOptions, reason?: string): Promise<Role> {
|
||||
return await this.client.editRole.call(this.client, this.id, roleID, options, reason)
|
||||
}
|
||||
|
||||
/** Edit a guild sticker */
|
||||
async editSticker(stickerID: BigString, options: EditStickerOptions, reason?: string): Promise<Sticker> {
|
||||
return await this.client.editGuildSticker.call(this.client, this.id, stickerID, options, reason)
|
||||
}
|
||||
|
||||
/** Edit a guild template */
|
||||
async editTemplate(code: string, options: GuildTemplateOptions): Promise<GuildTemplate> {
|
||||
return await this.client.editGuildTemplate.call(this.client, this.id, code, options)
|
||||
}
|
||||
|
||||
/** Modify the guild's vanity code */
|
||||
async editVanity(code: string | null): Promise<unknown> {
|
||||
return await this.client.editGuildVanity.call(this.client, this.id, code)
|
||||
}
|
||||
|
||||
/** Update a user's voice state - See [caveats](https://discord.com/developers/docs/resources/guild#modify-user-voice-state-caveats) */
|
||||
async editVoiceState(options: VoiceStateOptions, userID: BigString = '@me'): Promise<void> {
|
||||
return await this.client.editGuildVoiceState.call(this.client, this.id, options, userID)
|
||||
}
|
||||
|
||||
/** Edit the guild welcome screen */
|
||||
async editWelcomeScreen(options: WelcomeScreenOptions): Promise<WelcomeScreen> {
|
||||
return await this.client.editGuildWelcomeScreen.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
/** Modify a guild's widget */
|
||||
async editWidget(options: Widget): Promise<Widget> {
|
||||
return await this.client.editGuildWidget.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
/** Request all guild members from Discord */
|
||||
async fetchAllMembers(timeout?: number): Promise<number> {
|
||||
return await this.fetchMembers({
|
||||
guildId: this.id,
|
||||
limit: 0,
|
||||
}).then((m: any[]) => m.length)
|
||||
}
|
||||
|
||||
/** Request specific guild members through the gateway connection */
|
||||
async fetchMembers(options: RequestGuildMembers): Promise<Member[]> {
|
||||
// TODO: Use gateway fetch
|
||||
return await this.client.getRESTGuildMembers(this.id, options)
|
||||
}
|
||||
|
||||
/** Get all active threads in this guild */
|
||||
async getActiveThreads(): Promise<ListedGuildThreads<AnyThreadChannel>> {
|
||||
return await this.client.getActiveGuildThreads.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Get the audit log for the guild */
|
||||
async getAuditLog(options: GetGuildAuditLogOptions): Promise<GuildAuditLog> {
|
||||
return await this.client.getGuildAuditLog.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
/** Get a ban from the ban list of a guild */
|
||||
async getBan(userID: BigString): Promise<GuildBan> {
|
||||
return await this.client.getGuildBan.call(this.client, this.id, userID)
|
||||
}
|
||||
|
||||
/** Get the ban list of the guild */
|
||||
async getBans(options: GetGuildBansOptions): Promise<GuildBan[]> {
|
||||
return await this.client.getGuildBans.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
/** Get a guild application command */
|
||||
async getCommand(commandID: BigString): Promise<ApplicationCommand<ApplicationCommandTypes>> {
|
||||
return await this.client.getGuildCommand.call(this.client, this.id, commandID)
|
||||
}
|
||||
|
||||
/** Get the a guild's application command permissions */
|
||||
async getCommandPermissions(commandID: BigString): Promise<GuildApplicationCommandPermissions> {
|
||||
return await this.client.getCommandPermissions.call(this.client, this.id, commandID)
|
||||
}
|
||||
|
||||
/** Get the guild's application commands */
|
||||
async getCommands(): Promise<ApplicationCommand<ApplicationCommandTypes>> {
|
||||
return await this.client.getGuildCommands.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Get the guild's discovery object */
|
||||
async getDiscovery(): Promise<DiscoveryMetadata> {
|
||||
return await this.client.getGuildDiscovery.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Get the all of a guild's application command permissions */
|
||||
async getGuildCommandPermissions(): Promise<GuildApplicationCommandPermissions[]> {
|
||||
return await this.client.getGuildCommandPermissions.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Get a list of integrations for the guild */
|
||||
async getIntegrations(): Promise<GuildIntegration[]> {
|
||||
return await this.client.getGuildIntegrations.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Get all invites in the guild */
|
||||
async getInvites(): Promise<Invite[]> {
|
||||
return await this.client.getGuildInvites.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Get the prune count for the guild */
|
||||
async getPruneCount(options: GetPruneOptions): Promise<number> {
|
||||
return await this.client.getPruneCount.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
/** Get a guild's channels via the REST API. REST mode is required to use this endpoint. */
|
||||
async getRESTChannels(): Promise<AnyGuildChannel[]> {
|
||||
return await this.client.getRESTGuildChannels.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Get a guild emoji via the REST API. REST mode is required to use this endpoint. */
|
||||
async getRESTEmoji(emojiID: BigString): Promise<Emoji> {
|
||||
return await this.client.getRESTGuildEmoji.call(this.client, this.id, emojiID)
|
||||
}
|
||||
|
||||
/** Get a guild's emojis via the REST API. REST mode is required to use this endpoint. */
|
||||
async getRESTEmojis(): Promise<Emoji[]> {
|
||||
return await this.client.getRESTGuildEmojis.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Get a guild's members via the REST API. REST mode is required to use this endpoint. */
|
||||
async getRESTMember(memberID: BigString): Promise<Member> {
|
||||
return await this.client.getRESTGuildMember.call(this.client, this.id, memberID)
|
||||
}
|
||||
|
||||
/** Get a guild's members via the REST API. REST mode is required to use this endpoint. */
|
||||
async getRESTMembers(options?: GetRESTGuildMembersOptions): Promise<Member[]> {
|
||||
return await this.client.getRESTGuildMembers.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
/** Get a guild's roles via the REST API. REST mode is required to use this endpoint. */
|
||||
async getRESTRoles(): Promise<Role[]> {
|
||||
return await this.client.getRESTGuildRoles.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Get a guild sticker via the REST API. REST mode is required to use this endpoint. */
|
||||
async getRESTSticker(stickerID: BigString): Promise<Sticker> {
|
||||
return await this.client.getRESTGuildSticker.call(this.client, this.id, stickerID)
|
||||
}
|
||||
|
||||
/** Get a guild's stickers via the REST API. REST mode is required to use this endpoint. */
|
||||
async getRESTStickers(): Promise<Sticker[]> {
|
||||
return await this.client.getRESTGuildStickers.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Get the guild's templates */
|
||||
async getTemplates(): Promise<GuildTemplate[]> {
|
||||
return await this.client.getGuildTemplates.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Returns the vanity url of the guild */
|
||||
async getVanity(): Promise<GuildVanity> {
|
||||
return await this.client.getGuildVanity.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Get possible voice regions for a guild */
|
||||
async getVoiceRegions(): Promise<VoiceRegion[]> {
|
||||
return await this.client.getVoiceRegions.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Get all the webhooks in the guild */
|
||||
async getWebhooks(): Promise<Webhook[]> {
|
||||
return await this.client.getGuildWebhooks.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Get the welcome screen of the Community guild, shown to new members */
|
||||
async getWelcomeScreen(): Promise<WelcomeScreen> {
|
||||
return await this.client.getGuildWelcomeScreen.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Get a guild's widget object */
|
||||
async getWidget(): Promise<WidgetData> {
|
||||
return await this.client.getGuildWidget.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Get a guild's widget settings object */
|
||||
async getWidgetSettings(): Promise<Widget> {
|
||||
return await this.client.getGuildWidgetSettings.call(this.client, this.id)
|
||||
}
|
||||
|
||||
/** Kick a member from the guild */
|
||||
async kickMember(userID: BigString, reason?: string): Promise<void> {
|
||||
return await this.client.kickGuildMember.call(this.client, this.id, userID, reason)
|
||||
}
|
||||
|
||||
/** Leave the guild */
|
||||
async leave(): Promise<void> {
|
||||
return await this.client.leaveGuild.call(this.client, this.id)
|
||||
}
|
||||
|
||||
// TODO: gateway voice
|
||||
// /** Leaves the voice channel in this guild */
|
||||
// async leaveVoiceChannel(): Promise<void> {
|
||||
// return await this.client.closeVoiceConnection.call(this.client, this.id);
|
||||
// }
|
||||
|
||||
/** Get the guild permissions of a member */
|
||||
permissionsOf(memberID: BigString | Member): Permission {
|
||||
const member = ['string', 'bigint'].includes(typeof memberID) ? this.members.get(memberID as BigString)! : (memberID as Member)
|
||||
if (member.id === this.ownerID) {
|
||||
return new Permission(BitwisePermissionFlags.ADMINISTRATOR)
|
||||
} else {
|
||||
let permissions = this.roles.get(this.id)!.permissions.allow
|
||||
if (permissions & BigInt(BitwisePermissionFlags.ADMINISTRATOR)) {
|
||||
return new Permission(BitwisePermissionFlags.ADMINISTRATOR)
|
||||
}
|
||||
for (const id of member.roles) {
|
||||
const role = this.roles.get(id)
|
||||
if (!role) {
|
||||
continue
|
||||
}
|
||||
|
||||
const { allow: perm } = role.permissions
|
||||
if (perm & BigInt(BitwisePermissionFlags.ADMINISTRATOR)) {
|
||||
permissions = BigInt(BitwisePermissionFlags.ADMINISTRATOR)
|
||||
break
|
||||
} else {
|
||||
permissions |= perm
|
||||
}
|
||||
}
|
||||
return new Permission(permissions)
|
||||
}
|
||||
}
|
||||
|
||||
/** Begin pruning the guild */
|
||||
async pruneMembers(options?: PruneMemberOptions): Promise<number> {
|
||||
return await this.client.pruneMembers.call(this.client, this.id, options)
|
||||
}
|
||||
|
||||
/** Remove a role from a guild member */
|
||||
async removeMemberRole(memberID: BigString, roleID: BigString, reason?: string): Promise<void> {
|
||||
return await this.client.removeGuildMemberRole.call(this.client, this.id, memberID, roleID, reason)
|
||||
}
|
||||
|
||||
/** Search for guild members by partial nickname/username */
|
||||
async searchMembers(query: string, limit = 1): Promise<Member[]> {
|
||||
return await this.client.searchGuildMembers.call(this.client, this.id, query, limit)
|
||||
}
|
||||
|
||||
/** Force a guild integration to sync */
|
||||
async syncIntegration(integrationID: BigString): Promise<void> {
|
||||
return await this.client.syncGuildIntegration.call(this.client, this.id, integrationID)
|
||||
}
|
||||
|
||||
/** Force a guild template to sync */
|
||||
async syncTemplate(code: string): Promise<GuildTemplate> {
|
||||
return await this.client.syncGuildTemplate.call(this.client, this.id, code)
|
||||
}
|
||||
|
||||
/** Unban a user from the guild */
|
||||
async unbanMember(userID: BigString, reason?: string): Promise<void> {
|
||||
return await this.client.unbanGuildMember.call(this.client, this.id, userID, reason)
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON([
|
||||
'afkChannelID',
|
||||
'afkTimeout',
|
||||
'applicationID',
|
||||
'approximateMemberCount',
|
||||
'approximatePresenceCount',
|
||||
'autoRemoved',
|
||||
'banner',
|
||||
'categories',
|
||||
'channels',
|
||||
'defaultNotifications',
|
||||
'description',
|
||||
'discoverySplash',
|
||||
'emojiCount',
|
||||
'emojis',
|
||||
'explicitContentFilter',
|
||||
'features',
|
||||
'icon',
|
||||
'joinedAt',
|
||||
'keywords',
|
||||
'large',
|
||||
'maxMembers',
|
||||
'maxPresences',
|
||||
'maxVideoChannelUsers',
|
||||
'memberCount',
|
||||
'members',
|
||||
'mfaLevel',
|
||||
'name',
|
||||
'ownerID',
|
||||
'pendingVoiceStates',
|
||||
'preferredLocale',
|
||||
'premiumProgressBarEnabled',
|
||||
'premiumSubscriptionCount',
|
||||
'premiumTier',
|
||||
'primaryCategory',
|
||||
'primaryCategoryID',
|
||||
'publicUpdatesChannelID',
|
||||
'roles',
|
||||
'rulesChannelID',
|
||||
'splash',
|
||||
'stickers',
|
||||
'systemChannelFlags',
|
||||
'systemChannelID',
|
||||
'unavailable',
|
||||
'vanityURL',
|
||||
'verificationLevel',
|
||||
'voiceStates',
|
||||
'welcomeScreen',
|
||||
'widgetChannelID',
|
||||
'widgetEnabled',
|
||||
...props,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export default Guild
|
||||
131
packages/client/src/Structures/guilds/Integration.ts
Normal file
131
packages/client/src/Structures/guilds/Integration.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
/* eslint-disable no-useless-call */
|
||||
import type { DiscordIntegration, DiscordIntegrationApplication } from '@discordeno/types'
|
||||
import Base from '../../Base.js'
|
||||
import type { IntegrationOptions } from '../../typings.js'
|
||||
import User from '../users/User.js'
|
||||
import type Guild from './Guild.js'
|
||||
|
||||
export class GuildIntegration extends Base {
|
||||
/** The guild where this integration exists. */
|
||||
guild: Guild
|
||||
/** The name of the integration. */
|
||||
name: string
|
||||
/** The type of integration. */
|
||||
type: string
|
||||
/** The user connected to the integration. */
|
||||
user?: User
|
||||
/** Whether the integration is syncing or not. */
|
||||
syncing?: boolean
|
||||
/** THe Unix timestamp of last integration sync. */
|
||||
syncedAt?: number
|
||||
/** The number of subscribers. */
|
||||
subscriberCount?: number
|
||||
/** The role id of the role connected to the integration. */
|
||||
roleID?: string
|
||||
/** WHether or not the application was revoked. */
|
||||
revoked?: boolean
|
||||
/** Whether integration emoticons are enabled or not. */
|
||||
enableEmoticons?: boolean
|
||||
/** Behavior of expired subscriptions */
|
||||
expireBehavior?: number
|
||||
/** Grace period for expired subscriptions. */
|
||||
expireGracePeriod?: number
|
||||
/** Whether the integration is enabled or not. */
|
||||
enabled?: boolean
|
||||
/** The bot/oauth2 application for integration. */
|
||||
application?: DiscordIntegrationApplication
|
||||
|
||||
/** Info on the integration account */
|
||||
account: {
|
||||
/** The id of the integration account. */
|
||||
id: string
|
||||
/** The name of the integration account. */
|
||||
name: string
|
||||
}
|
||||
|
||||
constructor(data: DiscordIntegration, guild: Guild) {
|
||||
super(data.id)
|
||||
|
||||
this.guild = guild
|
||||
this.name = data.name
|
||||
this.type = data.type
|
||||
|
||||
if (data.role_id !== undefined) {
|
||||
this.roleID = data.role_id
|
||||
}
|
||||
|
||||
if (data.user) {
|
||||
this.user = new User(data.user, guild.client)
|
||||
guild.client.users.set(this.user.id, this.user)
|
||||
}
|
||||
|
||||
this.account = data.account // not worth making a class for
|
||||
|
||||
this.update(data)
|
||||
}
|
||||
|
||||
update(data: DiscordIntegration): void {
|
||||
this.enabled = data.enabled
|
||||
if (data.syncing !== undefined) {
|
||||
this.syncing = data.syncing
|
||||
}
|
||||
if (data.expire_behavior !== undefined) {
|
||||
this.expireBehavior = data.expire_behavior
|
||||
}
|
||||
if (data.expire_behavior !== undefined) {
|
||||
this.expireGracePeriod = data.expire_grace_period
|
||||
}
|
||||
if (data.enable_emoticons) {
|
||||
this.enableEmoticons = data.enable_emoticons
|
||||
}
|
||||
if (data.subscriber_count !== undefined) {
|
||||
this.subscriberCount = data.subscriber_count
|
||||
}
|
||||
if (data.synced_at !== undefined) {
|
||||
this.syncedAt = Date.parse(data.synced_at)
|
||||
}
|
||||
if (data.revoked !== undefined) {
|
||||
this.revoked = data.revoked
|
||||
}
|
||||
if (data.application !== undefined) {
|
||||
this.application = data.application
|
||||
}
|
||||
}
|
||||
|
||||
/** Delete the guild integration */
|
||||
async delete(): Promise<void> {
|
||||
return await this.guild.client.deleteGuildIntegration.call(this.guild.client, this.guild.id, this.id)
|
||||
}
|
||||
|
||||
/** Edit the guild integration */
|
||||
async edit(options: IntegrationOptions): Promise<void> {
|
||||
return await this.guild.client.editGuildIntegration.call(this.guild.client, this.guild.id, this.id, options)
|
||||
}
|
||||
|
||||
/** Force the guild integration to sync */
|
||||
async sync(): Promise<void> {
|
||||
return await this.guild.client.syncGuildIntegration.call(this.guild.client, this.guild.id, this.id)
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON([
|
||||
'account',
|
||||
'application',
|
||||
'enabled',
|
||||
'enableEmoticons',
|
||||
'expireBehavior',
|
||||
'expireGracePeriod',
|
||||
'name',
|
||||
'revoked',
|
||||
'roleID',
|
||||
'subscriberCount',
|
||||
'syncedAt',
|
||||
'syncing',
|
||||
'type',
|
||||
'user',
|
||||
...props,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export default GuildIntegration
|
||||
220
packages/client/src/Structures/guilds/Member.ts
Normal file
220
packages/client/src/Structures/guilds/Member.ts
Normal file
@@ -0,0 +1,220 @@
|
||||
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable no-useless-call */
|
||||
/* eslint-disable @typescript-eslint/return-await */
|
||||
|
||||
import type { BigString, DiscordMember, DiscordMemberWithUser } from '@discordeno/types'
|
||||
import Base from '../../Base.js'
|
||||
import type Client from '../../Client.js'
|
||||
import type { ImageFormat, ImageSize } from '../../Client.js'
|
||||
import { GUILD_AVATAR } from '../../Endpoints.js'
|
||||
import type { MemberOptions } from '../../typings.js'
|
||||
import User from '../users/User.js'
|
||||
import type Guild from './Guild.js'
|
||||
|
||||
export class Member extends Base {
|
||||
/** The client manager */
|
||||
client: Client
|
||||
/** An array of role IDs this member is a part of */
|
||||
roles: BigString[]
|
||||
/** The guild the member is in */
|
||||
guild: Guild
|
||||
/** The user object of the member */
|
||||
user: User
|
||||
/** The server nickname of the member. */
|
||||
nick: string | null
|
||||
/** The timestamp when this member joined the server. */
|
||||
joinedAt?: number
|
||||
/** Timestamp of when the member boosted the guild */
|
||||
premiumSince?: number
|
||||
/** Whether the user has not yet passed the guild's Membership Screening requirements */
|
||||
pending?: boolean
|
||||
/** Timestamp of timeout expiry. If `null`, the member is not timed out */
|
||||
communicationDisabledUntil?: number | null
|
||||
|
||||
/** The compressed form of the members avatar. */
|
||||
_avatar?: bigint
|
||||
|
||||
constructor(data: (DiscordMember & { id: BigString }) | DiscordMemberWithUser, guild: Guild, client: Client) {
|
||||
super(client.isDiscordMemberWithUser(data) ? data.user.id : data.id)
|
||||
this.client = client
|
||||
this.guild = guild
|
||||
this.nick = null
|
||||
this.roles = data.roles ?? []
|
||||
|
||||
const userID = client.isDiscordMemberWithUser(data) ? data.user.id : data.id
|
||||
|
||||
this.user = client.users.get(userID)!
|
||||
if (data.user) {
|
||||
this.user = new User(data.user, client)
|
||||
client.users.set(this.user.id, this.user)
|
||||
}
|
||||
|
||||
if (!this.user) {
|
||||
throw new Error('User associated with Member not found: ' + userID)
|
||||
}
|
||||
|
||||
this.update(data)
|
||||
}
|
||||
|
||||
update(data: (DiscordMember & { id: BigString }) | DiscordMemberWithUser) {
|
||||
if (data.joined_at !== undefined) {
|
||||
this.joinedAt = data.joined_at ? Date.parse(data.joined_at) : undefined
|
||||
}
|
||||
|
||||
if (data.premium_since !== undefined) {
|
||||
this.premiumSince = data.premium_since === null ? undefined : Date.parse(data.premium_since)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (data.hasOwnProperty('mute') && this.guild) {
|
||||
// TODO: voice stuff
|
||||
// const state = this.guild.voiceStates.get(this.id);
|
||||
// if (
|
||||
// data.channel_id === null &&
|
||||
// !data.mute &&
|
||||
// !data.deaf &&
|
||||
// !data.suppress
|
||||
// ) {
|
||||
// this.guild.voiceStates.delete(this.id);
|
||||
// } else if (state) {
|
||||
// state.update(data);
|
||||
// } else if (data.channel_id || data.mute || data.deaf || data.suppress) {
|
||||
// this.guild.voiceStates.update(data);
|
||||
// }
|
||||
}
|
||||
|
||||
if (data.nick !== undefined) this.nick = data.nick
|
||||
if (data.roles !== undefined) this.roles = data.roles
|
||||
if (data.pending !== undefined) this.pending = data.pending
|
||||
if (data.avatar !== undefined) this._avatar = this.client.iconHashToBigInt(data.avatar)
|
||||
|
||||
if (data.communication_disabled_until !== undefined) {
|
||||
if (data.communication_disabled_until !== null) {
|
||||
this.communicationDisabledUntil = Date.parse(data.communication_disabled_until)
|
||||
} else {
|
||||
this.communicationDisabledUntil = data.communication_disabled_until
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get avatar(): string | undefined {
|
||||
return this._avatar ? this.client.iconBigintToHash(this._avatar) : undefined
|
||||
}
|
||||
|
||||
get accentColor() {
|
||||
return this.user.accentColor
|
||||
}
|
||||
|
||||
get avatarURL() {
|
||||
return this.avatar ? this.client._formatImage(GUILD_AVATAR(this.guild.id, this.id, this.avatar)) : this.user.avatarURL
|
||||
}
|
||||
|
||||
get banner() {
|
||||
return this.user.banner
|
||||
}
|
||||
|
||||
get bannerURL() {
|
||||
return this.user.bannerURL
|
||||
}
|
||||
|
||||
get bot() {
|
||||
return this.user.bot
|
||||
}
|
||||
|
||||
get createdAt() {
|
||||
return this.user.createdAt
|
||||
}
|
||||
|
||||
get defaultAvatar() {
|
||||
return this.user.defaultAvatar
|
||||
}
|
||||
|
||||
get defaultAvatarURL() {
|
||||
return this.user.defaultAvatarURL
|
||||
}
|
||||
|
||||
get discriminator() {
|
||||
return this.user.discriminator
|
||||
}
|
||||
|
||||
get mention() {
|
||||
return `<@!${this.id}>`
|
||||
}
|
||||
|
||||
get permissions() {
|
||||
return this.guild.permissionsOf(this)
|
||||
}
|
||||
|
||||
get staticAvatarURL() {
|
||||
return this.user.staticAvatarURL
|
||||
}
|
||||
|
||||
get username() {
|
||||
return this.user.username
|
||||
}
|
||||
|
||||
get voiceState() {
|
||||
if (this.guild?.voiceStates.has(this.id)) {
|
||||
return this.guild.voiceStates.get(this.id)
|
||||
} else {
|
||||
// @ts-expect-error some eris magic at play here
|
||||
return new VoiceState({ id: this.id })
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a role to the guild member */
|
||||
async addRole(roleID: BigString, reason?: string): Promise<void> {
|
||||
return await this.client.addGuildMemberRole.call(this.client, this.guild.id, this.id, roleID, reason)
|
||||
}
|
||||
|
||||
/** Ban the user from the guild */
|
||||
async ban(deleteMessageDays = 0, reason?: string): Promise<void> {
|
||||
return await this.client.banGuildMember.call(this.client, this.guild.id, this.id, deleteMessageDays, reason)
|
||||
}
|
||||
|
||||
/** Edit the guild member */
|
||||
async edit(options: MemberOptions, reason?: string): Promise<Member> {
|
||||
return await this.client.editGuildMember.call(this.client, this.guild.id, this.id, options, reason)
|
||||
}
|
||||
|
||||
/** Get the member's avatar with the given format and size */
|
||||
dynamicAvatarURL(format?: ImageFormat, size?: ImageSize): string {
|
||||
return this.avatar
|
||||
? this.client._formatImage(GUILD_AVATAR(this.guild.id, this.id, this.avatar), format, size)
|
||||
: this.user.dynamicAvatarURL(format, size)
|
||||
}
|
||||
|
||||
/** Kick the member from the guild */
|
||||
async kick(reason?: string): Promise<void> {
|
||||
return await this.client.kickGuildMember.call(this.client, this.guild.id, this.id, reason)
|
||||
}
|
||||
|
||||
/** Remove a role from the guild member */
|
||||
async removeRole(roleID: BigString, reason?: string): Promise<void> {
|
||||
return await this.client.removeGuildMemberRole.call(this.client, this.guild.id, this.id, roleID, reason)
|
||||
}
|
||||
|
||||
/** Unban the user from the guild */
|
||||
async unban(reason?: string): Promise<void> {
|
||||
return await this.client.unbanGuildMember.call(this.client, this.guild.id, this.id, reason)
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON([
|
||||
'activities',
|
||||
'communicationDisabledUntil',
|
||||
'joinedAt',
|
||||
'nick',
|
||||
'pending',
|
||||
'premiumSince',
|
||||
'roles',
|
||||
'status',
|
||||
'user',
|
||||
'voiceState',
|
||||
...props,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export default Member
|
||||
113
packages/client/src/Structures/guilds/Preview.ts
Normal file
113
packages/client/src/Structures/guilds/Preview.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import type { DiscordEmoji, DiscordGuildPreview } from '@discordeno/types'
|
||||
import Base from '../../Base.js'
|
||||
import type Client from '../../Client.js'
|
||||
import type { ImageFormat, ImageSize } from '../../Client.js'
|
||||
import { GUILD_DISCOVERY_SPLASH, GUILD_ICON, GUILD_SPLASH } from '../../Endpoints.js'
|
||||
|
||||
export class GuildPreview extends Base {
|
||||
/** The client object */
|
||||
client: Client
|
||||
/** The name of the guild. */
|
||||
name: string
|
||||
/** The description of the guild. */
|
||||
description: string | null
|
||||
/** An array of guild emojis. */
|
||||
emojis: DiscordEmoji[]
|
||||
/** The approximate number of members in the guild. */
|
||||
approximateMemberCount: number
|
||||
/** The approximate number of presences in the guild. */
|
||||
approximatePresenceCount: number
|
||||
|
||||
/** The guild's icon image url. */
|
||||
_icon: bigint | null
|
||||
/** The guild's splash image url. */
|
||||
_splash: bigint | null
|
||||
/** The guild's discovery splash image url. */
|
||||
_discoverySplash: bigint | null
|
||||
/** The guild's features. */
|
||||
_features: GuildToggles
|
||||
|
||||
constructor(data: DiscordGuildPreview, client: Client) {
|
||||
super(data.id)
|
||||
|
||||
this.client = client
|
||||
this.name = data.name
|
||||
this.description = data.description
|
||||
this._icon = data.icon ? client.iconHashToBigInt(data.icon) : null
|
||||
this._splash = data.splash ? client.iconHashToBigInt(data.splash) : null
|
||||
this._discoverySplash = data.discovery_splash ? client.iconHashToBigInt(data.discovery_splash) : null
|
||||
this.approximateMemberCount = data.approximate_member_count
|
||||
this.approximatePresenceCount = data.approximate_presence_count
|
||||
this.emojis = data.emojis
|
||||
// TODO: make dd version accept a specific subset of discord guild here
|
||||
// @ts-expect-error this should not cause an issue
|
||||
this._features = new GuildToggles(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use .client
|
||||
*/
|
||||
get _client(): Client {
|
||||
return this.client
|
||||
}
|
||||
|
||||
get icon(): string | undefined {
|
||||
return this._icon ? this.client.iconBigintToHash(this._icon) : undefined
|
||||
}
|
||||
|
||||
get iconURL(): string | null {
|
||||
return this.icon ? this.client._formatImage(GUILD_ICON(this.id, this.icon)) : null
|
||||
}
|
||||
|
||||
get splash(): string | undefined {
|
||||
return this._splash ? this.client.iconBigintToHash(this._splash) : undefined
|
||||
}
|
||||
|
||||
get splashURL(): string | null {
|
||||
return this.splash ? this.client._formatImage(GUILD_SPLASH(this.id, this.splash)) : null
|
||||
}
|
||||
|
||||
get discoverySplash(): string | undefined {
|
||||
return this._discoverySplash ? this.client.iconBigintToHash(this._discoverySplash) : undefined
|
||||
}
|
||||
|
||||
get discoverySplashURL(): string | null {
|
||||
return this.discoverySplash ? this.client._formatImage(GUILD_DISCOVERY_SPLASH(this.id, this.discoverySplash)) : null
|
||||
}
|
||||
|
||||
get features(): string[] {
|
||||
return this._features.features.map((feature) => feature.replace(/([a-z])([A-Z])/, '$1_$2').toUpperCase())
|
||||
}
|
||||
|
||||
/** Get the guild's splash with the given format and size */
|
||||
dynamicDiscoverySplashURL(format?: ImageFormat, size?: ImageSize): string | null {
|
||||
return this.discoverySplash ? this.client._formatImage(GUILD_DISCOVERY_SPLASH(this.id, this.discoverySplash), format, size) : null
|
||||
}
|
||||
|
||||
/** Get the guild's icon with the given format and size */
|
||||
dynamicIconURL(format?: ImageFormat, size?: ImageSize): string | null {
|
||||
return this.icon ? this.client._formatImage(GUILD_ICON(this.id, this.icon), format, size) : null
|
||||
}
|
||||
|
||||
/** Get the guild's splash with the given format and size */
|
||||
dynamicSplashURL(format?: ImageFormat, size?: ImageSize): string | null {
|
||||
return this.splash ? this.client._formatImage(GUILD_SPLASH(this.id, this.splash), format, size) : null
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON([
|
||||
'approximateMemberCount',
|
||||
'approximatePresenceCount',
|
||||
'description',
|
||||
'discoverySplash',
|
||||
'emojis',
|
||||
'features',
|
||||
'icon',
|
||||
'name',
|
||||
'splash',
|
||||
...props,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export default GuildPreview
|
||||
116
packages/client/src/Structures/guilds/Role.ts
Normal file
116
packages/client/src/Structures/guilds/Role.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable no-useless-call */
|
||||
/* eslint-disable @typescript-eslint/return-await */
|
||||
|
||||
import type { DiscordRole, DiscordRoleTags } from '@discordeno/types'
|
||||
import Base from '../../Base.js'
|
||||
import { ROLE_ICON } from '../../Endpoints.js'
|
||||
import type { RoleOptions } from '../../typings.js'
|
||||
import Permission from '../Permission.js'
|
||||
import type Guild from './Guild.js'
|
||||
|
||||
export class Role extends Base {
|
||||
permissions: Permission
|
||||
name: string
|
||||
color: number
|
||||
hoist: boolean
|
||||
mentionable: boolean
|
||||
managed: boolean
|
||||
icon?: string
|
||||
unicodeEmoji?: string
|
||||
position: number
|
||||
guild: Guild
|
||||
tags?: Omit<DiscordRoleTags, 'premium_subscriber'> & { premium_subscriber?: boolean }
|
||||
|
||||
constructor(data: DiscordRole, guild: Guild) {
|
||||
super(data.id)
|
||||
this.guild = guild
|
||||
|
||||
this.name = data.name
|
||||
this.permissions = new Permission(data.permissions)
|
||||
this.color = data.color
|
||||
this.hoist = !!data.hoist
|
||||
this.mentionable = !!data.mentionable
|
||||
this.managed = !!data.managed
|
||||
this.icon = data.icon
|
||||
this.unicodeEmoji = data.unicode_emoji
|
||||
this.position = data.position
|
||||
this.tags = data.tags
|
||||
? {
|
||||
bot_id: data.tags.bot_id,
|
||||
integration_id: data.tags.integration_id,
|
||||
premium_subscriber: data.tags.premium_subscriber === null,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
|
||||
update(data: DiscordRole) {
|
||||
if (data.name !== undefined) {
|
||||
this.name = data.name
|
||||
}
|
||||
if (data.mentionable !== undefined) {
|
||||
this.mentionable = data.mentionable
|
||||
}
|
||||
if (data.managed !== undefined) {
|
||||
this.managed = data.managed
|
||||
}
|
||||
if (data.hoist !== undefined) {
|
||||
this.hoist = data.hoist
|
||||
}
|
||||
if (data.color !== undefined) {
|
||||
this.color = data.color
|
||||
}
|
||||
if (data.position !== undefined) {
|
||||
this.position = data.position
|
||||
}
|
||||
if (data.permissions !== undefined) {
|
||||
this.permissions = new Permission(data.permissions)
|
||||
}
|
||||
if (data.tags !== undefined) {
|
||||
this.tags = {
|
||||
bot_id: data.tags.bot_id,
|
||||
integration_id: data.tags.integration_id,
|
||||
premium_subscriber: data.tags.premium_subscriber === null,
|
||||
}
|
||||
}
|
||||
if (data.icon !== undefined) {
|
||||
this.icon = data.icon
|
||||
}
|
||||
if (data.unicode_emoji !== undefined) {
|
||||
this.unicodeEmoji = data.unicode_emoji
|
||||
}
|
||||
}
|
||||
|
||||
get iconURL() {
|
||||
return this.icon ? this.guild.client._formatImage(ROLE_ICON(this.id, this.icon)) : null
|
||||
}
|
||||
|
||||
get json() {
|
||||
return this.permissions.json
|
||||
}
|
||||
|
||||
get mention() {
|
||||
return `<@&${this.id}>`
|
||||
}
|
||||
|
||||
/** Delete the role */
|
||||
async delete(reason: string): Promise<void> {
|
||||
return await this.guild.client.deleteRole.call(this.guild.client, this.guild.id, this.id, reason)
|
||||
}
|
||||
|
||||
/** Edit the guild role */
|
||||
async edit(options: RoleOptions, reason?: string): Promise<Role> {
|
||||
return await this.guild.client.editRole.call(this.guild.client, this.guild.id, this.id, options, reason)
|
||||
}
|
||||
|
||||
/** Edit the role's position. Note that role position numbers are highest on top and lowest at the bottom. */
|
||||
async editPosition(position: number): Promise<void> {
|
||||
return await this.guild.client.editRolePosition.call(this.guild.client, this.guild.id, this.id, position)
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON(['color', 'hoist', 'icon', 'managed', 'mentionable', 'name', 'permissions', 'position', 'tags', 'unicodeEmoji', ...props])
|
||||
}
|
||||
}
|
||||
|
||||
export default Role
|
||||
63
packages/client/src/Structures/guilds/StageInstance.ts
Normal file
63
packages/client/src/Structures/guilds/StageInstance.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable no-useless-call */
|
||||
import type { DiscordStageInstance } from "@discordeno/types";
|
||||
import Base from "../../Base.js";
|
||||
import type Client from "../../Client.js";
|
||||
import type { StageInstanceOptions } from "../../typings.js";
|
||||
import type StageChannel from "../channels/Stage.js";
|
||||
import type Guild from "./Guild.js";
|
||||
|
||||
|
||||
export class StageInstance extends Base {
|
||||
/** The client manager. */
|
||||
client: Client;
|
||||
/** The associated stage channel */
|
||||
channel: StageChannel | { id: string };
|
||||
/** The guild of the associated stage channel */
|
||||
guild: Guild | { id: string };
|
||||
/** The stage channel topic */
|
||||
topic?: string | null;
|
||||
|
||||
constructor(data: DiscordStageInstance, client: Client) {
|
||||
super(data.id);
|
||||
|
||||
this.client = client;
|
||||
this.channel = client.getChannel(data.channel_id) || {
|
||||
id: data.channel_id,
|
||||
};
|
||||
this.guild = client.guilds.get(data.guild_id) ?? { id: data.guild_id };
|
||||
this.update(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `.client` instead.
|
||||
*/
|
||||
get _client(): Client {
|
||||
return this.client;
|
||||
}
|
||||
|
||||
update(data: DiscordStageInstance) {
|
||||
if (data.topic !== undefined) {
|
||||
this.topic = data.topic;
|
||||
}
|
||||
}
|
||||
|
||||
/** Delete this stage instance */
|
||||
async delete(): Promise<void> {
|
||||
return await this.client.deleteStageInstance.call(
|
||||
this.client,
|
||||
this.channel.id
|
||||
);
|
||||
}
|
||||
|
||||
/** Update this stage instance */
|
||||
async edit(options: StageInstanceOptions): Promise<StageInstance> {
|
||||
return await this.client.editStageInstance.call(
|
||||
this.client,
|
||||
this.channel.id,
|
||||
options
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default StageInstance;
|
||||
95
packages/client/src/Structures/guilds/Template.ts
Normal file
95
packages/client/src/Structures/guilds/Template.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/* eslint-disable no-useless-call */
|
||||
import type { DiscordTemplate } from '@discordeno/types'
|
||||
import Base from '../../Base.js'
|
||||
import type Client from '../../Client.js'
|
||||
import type { GuildTemplateOptions } from '../../typings.js'
|
||||
import User from '../users/User.js'
|
||||
import Guild from './Guild.js'
|
||||
|
||||
export class GuildTemplate {
|
||||
/** The client class itself. */
|
||||
client: Client
|
||||
/** The template code (unique Id) */
|
||||
code: string
|
||||
/** Template name */
|
||||
name: string
|
||||
/** The description for the template */
|
||||
description: string | null
|
||||
/** Number of times this template has been used */
|
||||
usageCount: number
|
||||
/** The user who created the template */
|
||||
creator: User
|
||||
/** When this template was created */
|
||||
createdAt: number
|
||||
/** When this template was last synced to the source guild */
|
||||
updatedAt: number
|
||||
/** The guild snapshot this template contains */
|
||||
serializedSourceGuild: Guild
|
||||
/** The guild this template is based on. If the guild is not cached, this will be an object with `id` key. No other property is guaranteed */
|
||||
sourceGuild: Guild | { id: string }
|
||||
/** Whether the template has un-synced changes */
|
||||
isDirty: boolean | null
|
||||
|
||||
constructor(data: DiscordTemplate, client: Client) {
|
||||
this.client = client
|
||||
this.code = data.code
|
||||
this.createdAt = Date.parse(data.created_at)
|
||||
this.creator = new User(data.creator, client)
|
||||
this.client.users.set(this.creator.id, this.creator)
|
||||
this.description = data.description
|
||||
this.isDirty = data.is_dirty
|
||||
this.name = data.name
|
||||
this.sourceGuild = client.guilds.get(data.source_guild_id) ?? { id: data.source_guild_id }
|
||||
this.updatedAt = Date.parse(data.updated_at)
|
||||
this.usageCount = data.usage_count
|
||||
|
||||
data.serialized_source_guild.features = []
|
||||
// @ts-expect-error Hacks to get this to not error
|
||||
this.serializedSourceGuild = new Guild(data.serialized_source_guild, client)
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use .client instead.
|
||||
*/
|
||||
get _client(): Client {
|
||||
return this.client
|
||||
}
|
||||
|
||||
/** Create a guild based on this template. Only for bots in less than 10 guilds */
|
||||
async createGuild(name: string, icon?: string): Promise<Guild> {
|
||||
return await this.client.createGuildFromTemplate.call(this.client, this.code, name, icon)
|
||||
}
|
||||
|
||||
/** Delete this template */
|
||||
async delete(): Promise<void> {
|
||||
return await this.client.deleteGuildTemplate.call(this.client, this.sourceGuild.id, this.code)
|
||||
}
|
||||
|
||||
/** Edit this template */
|
||||
async edit(options: GuildTemplateOptions): Promise<GuildTemplate> {
|
||||
return await this.client.editGuildTemplate.call(this.client, this.sourceGuild.id, this.code, options)
|
||||
}
|
||||
|
||||
/** Force this template to sync to the guild's current state */
|
||||
async sync(): Promise<GuildTemplate> {
|
||||
return await this.client.syncGuildTemplate.call(this.client, this.sourceGuild.id, this.code)
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return Base.prototype.toJSON.call(this, [
|
||||
'code',
|
||||
'createdAt',
|
||||
'creator',
|
||||
'description',
|
||||
'isDirty',
|
||||
'name',
|
||||
'serializedSourceGuild',
|
||||
'sourceGuild',
|
||||
'updatedAt',
|
||||
'usageCount',
|
||||
...props,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export default GuildTemplate
|
||||
23
packages/client/src/Structures/guilds/Unavailable.ts
Normal file
23
packages/client/src/Structures/guilds/Unavailable.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { DiscordUnavailableGuild } from "@discordeno/types"
|
||||
import Base from "../../Base.js"
|
||||
import type Client from "../../Client.js"
|
||||
|
||||
|
||||
export class UnavailableGuild extends Base {
|
||||
/** Whether or not the guild is unavailable. */
|
||||
unavailable: boolean
|
||||
|
||||
constructor(data: DiscordUnavailableGuild, client: Client) {
|
||||
super(data.id)
|
||||
|
||||
// TODO: gateway
|
||||
// this.shard = client.shards.get(client.guildShardMap[this.id]);
|
||||
this.unavailable = !!data.unavailable
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON(['unavailable', ...props])
|
||||
}
|
||||
}
|
||||
|
||||
export default UnavailableGuild
|
||||
132
packages/client/src/Structures/guilds/VoiceState.ts
Normal file
132
packages/client/src/Structures/guilds/VoiceState.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import type { DiscordVoiceState } from "@discordeno/types"
|
||||
import Base from "../../Base.js"
|
||||
|
||||
|
||||
export class VoiceState extends Base {
|
||||
channelID: string | null = null
|
||||
requestToSpeakTimestamp: number | null
|
||||
sessionID!: string | null
|
||||
|
||||
bitfield: VoiceStateToggles
|
||||
|
||||
constructor(data: DiscordVoiceState & { id: string }) {
|
||||
super(data.id)
|
||||
|
||||
this.requestToSpeakTimestamp = null
|
||||
this.bitfield = new VoiceStateToggles(data)
|
||||
|
||||
this.update(data)
|
||||
}
|
||||
|
||||
/** Whether or not the user is deafened by the server. */
|
||||
get deaf(): boolean {
|
||||
return this.bitfield.deaf
|
||||
}
|
||||
|
||||
/** Set whether or not the user is deafened by the server. */
|
||||
set deaf(value: boolean) {
|
||||
if (value) this.bitfield.add(VoiceStateToggle.deaf)
|
||||
else this.bitfield.remove(VoiceStateToggle.deaf)
|
||||
}
|
||||
|
||||
/** Whether or not the user is muted by the server. */
|
||||
get mute(): boolean {
|
||||
return this.bitfield.mute
|
||||
}
|
||||
|
||||
/** Set whether or not the user is muted by the server. */
|
||||
set mute(value: boolean) {
|
||||
if (value) this.bitfield.add(VoiceStateToggle.mute)
|
||||
else this.bitfield.remove(VoiceStateToggle.mute)
|
||||
}
|
||||
|
||||
/** Whether or not the user has deafened themself. */
|
||||
get selfDeaf(): boolean {
|
||||
return this.bitfield.selfDeaf
|
||||
}
|
||||
|
||||
/** Set whether or not the user has deafened themself. */
|
||||
set selfDeaf(value: boolean) {
|
||||
if (value) this.bitfield.add(VoiceStateToggle.selfDeaf)
|
||||
else this.bitfield.remove(VoiceStateToggle.selfDeaf)
|
||||
}
|
||||
|
||||
/** Whether or not the user has muted themself. */
|
||||
get selfMute(): boolean {
|
||||
return this.bitfield.selfMute
|
||||
}
|
||||
|
||||
/** Set whether or not the user has muted themself. */
|
||||
set selfMute(value: boolean) {
|
||||
if (value) this.bitfield.add(VoiceStateToggle.selfMute)
|
||||
else this.bitfield.remove(VoiceStateToggle.selfMute)
|
||||
}
|
||||
|
||||
/** Whether or not the user is streaming. */
|
||||
get selfStream(): boolean {
|
||||
return this.bitfield.selfStream
|
||||
}
|
||||
|
||||
/** Set whether or not the user is streaming. */
|
||||
set selfStream(value: boolean) {
|
||||
if (value) this.bitfield.add(VoiceStateToggle.selfStream)
|
||||
else this.bitfield.remove(VoiceStateToggle.selfStream)
|
||||
}
|
||||
|
||||
/** Whether or not the user is video calling. */
|
||||
get selfVideo(): boolean {
|
||||
return this.bitfield.selfVideo
|
||||
}
|
||||
|
||||
/** Set whether or not the user is video calling. */
|
||||
set selfVideo(value: boolean) {
|
||||
if (value) this.bitfield.add(VoiceStateToggle.selfVideo)
|
||||
else this.bitfield.remove(VoiceStateToggle.selfVideo)
|
||||
}
|
||||
|
||||
/** Whether or not the user is suppressed from speaking. */
|
||||
get suppress(): boolean {
|
||||
return this.bitfield.suppress
|
||||
}
|
||||
|
||||
/** Set whether or not the user is suppressed from speaking. */
|
||||
set suppress(value: boolean) {
|
||||
if (value) this.bitfield.add(VoiceStateToggle.suppress)
|
||||
else this.bitfield.remove(VoiceStateToggle.suppress)
|
||||
}
|
||||
|
||||
update(data: DiscordVoiceState) {
|
||||
if (data.channel_id !== undefined) {
|
||||
this.channelID = data.channel_id
|
||||
this.sessionID = data.channel_id === null ? null : data.session_id
|
||||
} else if (this.channelID === undefined) {
|
||||
this.channelID = this.sessionID = null
|
||||
}
|
||||
|
||||
if (data.mute !== undefined) this.mute = data.mute
|
||||
if (data.deaf !== undefined) this.deaf = data.deaf
|
||||
if (data.request_to_speak_timestamp) this.requestToSpeakTimestamp = Date.parse(data.request_to_speak_timestamp)
|
||||
if (data.self_mute !== undefined) this.selfMute = data.self_mute
|
||||
if (data.self_deaf !== undefined) this.selfDeaf = data.self_deaf
|
||||
if (data.self_video !== undefined) this.selfVideo = data.self_video
|
||||
if (data.self_stream !== undefined) this.selfStream = data.self_stream
|
||||
if (data.suppress !== undefined) this.suppress = data.suppress
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON([
|
||||
'channelID',
|
||||
'deaf',
|
||||
'mute',
|
||||
'requestToSpeakTimestamp',
|
||||
'selfDeaf',
|
||||
'selfMute',
|
||||
'selfStream',
|
||||
'selfVideo',
|
||||
'sessionID',
|
||||
'suppress',
|
||||
...props,
|
||||
])
|
||||
}
|
||||
}
|
||||
80
packages/client/src/Structures/interactions/Autocomplete.ts
Normal file
80
packages/client/src/Structures/interactions/Autocomplete.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
/* eslint-disable @typescript-eslint/return-await */
|
||||
/* eslint-disable no-useless-call */
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import type { BigString, DiscordInteraction, DiscordInteractionData } from '@discordeno/types'
|
||||
import { InteractionResponseTypes } from '@discordeno/types'
|
||||
import type Client from '../../Client.js'
|
||||
import type { ApplicationCommandOptionChoice } from '../../typings.js'
|
||||
import type NewsChannel from '../channels/News.js'
|
||||
import type PrivateChannel from '../channels/Private.js'
|
||||
import type TextChannel from '../channels/Text.js'
|
||||
import type Guild from '../guilds/Guild.js'
|
||||
import Member from '../guilds/Member.js'
|
||||
import Permission from '../Permission.js'
|
||||
import User from '../users/User.js'
|
||||
import Interaction from './Interaction.js'
|
||||
|
||||
export class AutocompleteInteraction extends Interaction {
|
||||
/** The guild id if this interaction occurred in a guild. */
|
||||
guildID?: BigString
|
||||
/** The permissions the app or bot has within the channel, the interaction was sent from. */
|
||||
appPermissions?: Permission
|
||||
/** The channel id where this interaction was created in. */
|
||||
channelID: BigString
|
||||
/** The user who triggered the interaction. */
|
||||
user: User
|
||||
/** The data attached to this interaction. */
|
||||
data?: DiscordInteractionData
|
||||
/** The member who triggered the interaction. Sent when used in a guild. */
|
||||
member?: Member
|
||||
|
||||
constructor(data: DiscordInteraction, client: Client) {
|
||||
super(data, client)
|
||||
|
||||
this.channelID = data.channel_id!
|
||||
this.data = data.data
|
||||
|
||||
if (data.guild_id !== undefined) {
|
||||
this.guildID = data.guild_id
|
||||
}
|
||||
|
||||
if (data.member !== undefined && this.guild) {
|
||||
this.member = new Member(data.member, this.guild, this.client)
|
||||
this.guild.members.set(this.member.id, this.member)
|
||||
}
|
||||
|
||||
this.user = new User(data.user ?? data.member!.user, this.client)
|
||||
this.client.users.set(this.user.id, this.user)
|
||||
|
||||
if (data.app_permissions !== undefined) {
|
||||
this.appPermissions = new Permission(data.app_permissions)
|
||||
}
|
||||
}
|
||||
|
||||
/** The channel the interaction was created in. */
|
||||
get channel(): PrivateChannel | TextChannel | NewsChannel {
|
||||
return this.client.getChannel(this.channelID) as PrivateChannel | TextChannel | NewsChannel
|
||||
}
|
||||
|
||||
/** The guild the interaction was created in. */
|
||||
get guild(): Guild | undefined {
|
||||
return this.guildID ? this.client.guilds.get(this.guildID) : undefined
|
||||
}
|
||||
|
||||
async acknowledge(choices: ApplicationCommandOptionChoice[]) {
|
||||
return await this.result(choices)
|
||||
}
|
||||
|
||||
async result(choices: ApplicationCommandOptionChoice[]) {
|
||||
if (this.acknowledged) throw new Error('You have already acknowledged this interaction.')
|
||||
|
||||
return this.client.createInteractionResponse
|
||||
.call(this.client, this.id, this.token, {
|
||||
type: InteractionResponseTypes.ApplicationCommandAutocompleteResult,
|
||||
data: { choices },
|
||||
})
|
||||
.then(() => this.update())
|
||||
}
|
||||
}
|
||||
|
||||
export default AutocompleteInteraction
|
||||
318
packages/client/src/Structures/interactions/Command.ts
Normal file
318
packages/client/src/Structures/interactions/Command.ts
Normal file
@@ -0,0 +1,318 @@
|
||||
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable no-useless-call */
|
||||
/* eslint-disable @typescript-eslint/return-await */
|
||||
import {
|
||||
ApplicationCommandTypes,
|
||||
InteractionResponseTypes,
|
||||
type BigString,
|
||||
type DiscordAttachment,
|
||||
type DiscordInteraction,
|
||||
type DiscordInteractionDataOption,
|
||||
type DiscordMessageComponents,
|
||||
type MessageComponentTypes,
|
||||
} from '@discordeno/types'
|
||||
import type Client from '../../Client.js'
|
||||
import Collection from '../../Collection.js'
|
||||
import type { AnyChannel, FileContent, InteractionContent, InteractionContentEdit } from '../../typings.js'
|
||||
import Channel from '../channels/Channel.js'
|
||||
import Member from '../guilds/Member.js'
|
||||
import Role from '../guilds/Role.js'
|
||||
import Message from '../Message.js'
|
||||
import User from '../users/User.js'
|
||||
import Interaction from './Interaction.js'
|
||||
|
||||
export class CommandInteraction extends Interaction {
|
||||
channel: AnyChannel
|
||||
/** The type of component */
|
||||
componentType?: MessageComponentTypes
|
||||
/** The custom id provided for this component. */
|
||||
customId?: string
|
||||
/** The components if its a Modal Submit interaction. */
|
||||
components?: DiscordMessageComponents
|
||||
/** The values chosen by the user. */
|
||||
values?: string[]
|
||||
/** the type of the invoked command */
|
||||
commandType: ApplicationCommandTypes = ApplicationCommandTypes.ChatInput
|
||||
/** The Ids and Message objects */
|
||||
messages = new Collection<BigString, Message>()
|
||||
/** The Ids and User objects */
|
||||
users = new Collection<BigString, User>()
|
||||
/** The Ids and partial Member objects */
|
||||
members = new Collection<BigString, Member>()
|
||||
/** The Ids and Role objects */
|
||||
roles = new Collection<BigString, Role>()
|
||||
/** The Ids and partial Channel objects */
|
||||
channels = new Collection<BigString, Channel>()
|
||||
/** The ids and attachment objects */
|
||||
attachments = new Collection<BigString, DiscordAttachment>()
|
||||
/** The params + values from the user */
|
||||
options?: DiscordInteractionDataOption[]
|
||||
/** The target id if this is a context menu command. */
|
||||
targetID?: string
|
||||
/** the id of the guild the command is registered to */
|
||||
guildID?: string
|
||||
|
||||
member?: Member
|
||||
user: User
|
||||
|
||||
constructor(info: DiscordInteraction, client: Client) {
|
||||
super(info, client)
|
||||
|
||||
this.channel = this.client.getChannel(info.channel_id!) || {
|
||||
id: info.channel_id,
|
||||
}
|
||||
|
||||
// this.data = info.data!;
|
||||
const guild = this.client.guilds.get(info.guild_id!)
|
||||
|
||||
if (info.data?.resolved !== undefined) {
|
||||
// Users
|
||||
if (info.data.resolved.users !== undefined) {
|
||||
for (const u of Object.values(info.data.resolved.users)) {
|
||||
const user = new User(u, this.client)
|
||||
this.users.set(user.id, user)
|
||||
}
|
||||
}
|
||||
|
||||
// Members
|
||||
if (info.data.resolved.members !== undefined) {
|
||||
for (const [, m] of Object.entries(info.data.resolved.members)) {
|
||||
// @ts-expect-error some eris magic at play here
|
||||
m.id = m
|
||||
// @ts-expect-error some eris magic at play here
|
||||
const member = new Member(m, guild, this.client)
|
||||
this.members.set(member.id, member)
|
||||
}
|
||||
}
|
||||
|
||||
// Roles
|
||||
if (info.data.resolved.roles !== undefined) {
|
||||
for (const r of Object.values(info.data.resolved.roles)) {
|
||||
// @ts-expect-error some eris magic at play here
|
||||
const role = new Role(r, guild)
|
||||
this.roles.set(role.id, role)
|
||||
}
|
||||
}
|
||||
|
||||
// Channels
|
||||
if (info.data.resolved.channels !== undefined) {
|
||||
for (const c of Object.values(info.data.resolved.channels)) {
|
||||
const channel = new Channel(c, this.client)
|
||||
this.channels.set(channel.id, channel)
|
||||
}
|
||||
}
|
||||
|
||||
// Messages
|
||||
if (info.data.resolved.messages !== undefined) {
|
||||
for (const m of Object.values(info.data.resolved.messages)) {
|
||||
const message = new Message(m, this.client)
|
||||
this.messages.set(message.id, message)
|
||||
}
|
||||
}
|
||||
|
||||
// Attachments
|
||||
if (info.data.resolved.attachments !== undefined) {
|
||||
for (const attachment of Object.values(info.data.resolved.attachments)) {
|
||||
this.attachments.set(attachment.id, attachment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.guildID = info.guild_id
|
||||
|
||||
if (info.member !== undefined) {
|
||||
// @ts-expect-error some eris magic at play here
|
||||
this.member = new Member(info.member, guild, this.client)
|
||||
guild?.members.set(this.member.id, this.member)
|
||||
}
|
||||
|
||||
this.user = new User(info.user ?? info.member!.user, this.client)
|
||||
this.client.users.set(this.user.id, this.user)
|
||||
|
||||
if (info.data) {
|
||||
this.componentType = info.data.component_type
|
||||
this.customId = info.data.custom_id
|
||||
this.components = info.data.components
|
||||
this.values = info.data.values
|
||||
this.commandType = info.data.type
|
||||
this.options = info.data.options
|
||||
this.targetID = info.data.target_id
|
||||
}
|
||||
}
|
||||
|
||||
get data() {
|
||||
return {
|
||||
component_type: this.componentType,
|
||||
custom_id: this.customId,
|
||||
components: this.components,
|
||||
values: this.values,
|
||||
type: this.commandType,
|
||||
resolved: {
|
||||
messages: this.messages.toRecord(),
|
||||
users: this.users.toRecord(),
|
||||
members: this.members.toRecord(),
|
||||
roles: this.roles.toRecord(),
|
||||
channels: this.channels.toRecord(),
|
||||
attachments: this.attachments.toRecord(),
|
||||
},
|
||||
options: this.options,
|
||||
target_id: this.targetID,
|
||||
guild_id: this.guildID,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledges the interaction with a defer response
|
||||
* Note: You can **not** use more than 1 initial interaction response per interaction.
|
||||
*/
|
||||
async acknowledge(flags?: number): Promise<void> {
|
||||
return this.defer(flags)
|
||||
}
|
||||
|
||||
/** Respond to the interaction with a followup message */
|
||||
async createFollowup(content: string | InteractionContent, file?: FileContent | FileContent[]): Promise<Message> {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error('createFollowup cannot be used to acknowledge an interaction, please use acknowledge, createMessage, or defer first.')
|
||||
}
|
||||
if (content !== undefined) {
|
||||
if (typeof content !== 'object' || content === null) {
|
||||
content = {
|
||||
content: '' + content,
|
||||
}
|
||||
} else if (content.content !== undefined && typeof content.content !== 'string') {
|
||||
content.content = '' + content.content
|
||||
}
|
||||
}
|
||||
|
||||
return await this.client.executeWebhook.call(this.client, this.applicationID, this.token, {
|
||||
...content,
|
||||
wait: true,
|
||||
file,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledges the interaction with a message. If already acknowledged runs createFollowup
|
||||
* Note: You can **not** use more than 1 initial interaction response per interaction, use createFollowup if you have already responded with a different interaction response.
|
||||
*/
|
||||
async createMessage(content: string | InteractionContent, file?: FileContent | FileContent[]): Promise<void> {
|
||||
if (this.acknowledged) {
|
||||
// @ts-expect-error some eris magic at play here
|
||||
return await this.createFollowup(content, file)
|
||||
}
|
||||
|
||||
if (typeof content !== 'object' || content === null) {
|
||||
content = {
|
||||
content: '' + content,
|
||||
}
|
||||
} else if (content.content !== undefined && typeof content.content !== 'string') {
|
||||
content.content = '' + content.content
|
||||
}
|
||||
|
||||
return this.client.createInteractionResponse
|
||||
.call(
|
||||
this.client,
|
||||
this.id,
|
||||
this.token,
|
||||
{
|
||||
type: InteractionResponseTypes.ChannelMessageWithSource,
|
||||
data: content,
|
||||
},
|
||||
file,
|
||||
)
|
||||
.then(() => this.update())
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledges the interaction with a defer response
|
||||
* Note: You can **not** use more than 1 initial interaction response per interaction.
|
||||
*/
|
||||
async defer(flags?: number): Promise<void> {
|
||||
if (this.acknowledged) {
|
||||
throw new Error('You have already acknowledged this interaction.')
|
||||
}
|
||||
return this.client.createInteractionResponse
|
||||
.call(this.client, this.id, this.token, {
|
||||
type: InteractionResponseTypes.DeferredChannelMessageWithSource,
|
||||
data: {
|
||||
flags: flags ?? 0,
|
||||
},
|
||||
})
|
||||
.then(() => this.update())
|
||||
}
|
||||
|
||||
/** Delete a message */
|
||||
async deleteMessage(messageID: BigString): Promise<void> {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error('deleteMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, or defer first.')
|
||||
}
|
||||
return this.client.deleteWebhookMessage.call(this.client, this.applicationID, this.token, messageID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the Original message
|
||||
* Warning: Will error with ephemeral messages.
|
||||
*/
|
||||
async deleteOriginalMessage(): Promise<void> {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error('deleteOriginalMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, or defer first.')
|
||||
}
|
||||
return this.client.deleteWebhookMessage.call(this.client, this.applicationID, this.token, '@original')
|
||||
}
|
||||
|
||||
/** Edit a message */
|
||||
async editMessage(messageID: BigString, content: string | InteractionContentEdit, file?: FileContent | FileContent[]): Promise<Message> {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error('editMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, or defer first.')
|
||||
}
|
||||
if (content !== undefined) {
|
||||
if (typeof content !== 'object' || content === null) {
|
||||
content = {
|
||||
content: '' + content,
|
||||
}
|
||||
} else if (content.content !== undefined && typeof content.content !== 'string') {
|
||||
content.content = '' + content.content
|
||||
}
|
||||
}
|
||||
|
||||
return await this.client.editWebhookMessage.call(this.client, this.applicationID, this.token, messageID, {
|
||||
...content,
|
||||
file,
|
||||
})
|
||||
}
|
||||
|
||||
/** Edit the Original response message */
|
||||
async editOriginalMessage(content: string | InteractionContentEdit, file?: FileContent | FileContent[]): Promise<Message> {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error('editOriginalMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, or defer first.')
|
||||
}
|
||||
if (content !== undefined) {
|
||||
if (typeof content !== 'object' || content === null) {
|
||||
content = {
|
||||
content: '' + content,
|
||||
}
|
||||
} else if (content.content !== undefined && typeof content.content !== 'string') {
|
||||
content.content = '' + content.content
|
||||
}
|
||||
}
|
||||
|
||||
return this.client.editWebhookMessage.call(this.client, this.applicationID, this.token, '@original', {
|
||||
...content,
|
||||
file,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Original response message
|
||||
* Warning: Will error with ephemeral messages.
|
||||
*/
|
||||
async getOriginalMessage(): Promise<Message> {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error('getOriginalMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, or defer first.')
|
||||
}
|
||||
return this.client.getWebhookMessage.call(this.client, this.applicationID, this.token, '@original')
|
||||
}
|
||||
}
|
||||
|
||||
export default CommandInteraction
|
||||
296
packages/client/src/Structures/interactions/Component.ts
Normal file
296
packages/client/src/Structures/interactions/Component.ts
Normal file
@@ -0,0 +1,296 @@
|
||||
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
||||
/* eslint-disable no-useless-call */
|
||||
/* eslint-disable @typescript-eslint/return-await */
|
||||
|
||||
import type { BigString, DiscordInteraction, MessageComponentTypes } from '@discordeno/types'
|
||||
import { InteractionResponseTypes } from '@discordeno/types'
|
||||
import type Client from '../../Client.js'
|
||||
import type { AnyChannel, FileContent, InteractionApplicationCommandCallbackData, MessageWebhookContent, WebhookPayload } from '../../typings.js'
|
||||
import type Guild from '../guilds/Guild.js'
|
||||
import Member from '../guilds/Member.js'
|
||||
import Message from '../Message.js'
|
||||
import Permission from '../Permission.js'
|
||||
import User from '../users/User.js'
|
||||
import Interaction from './Interaction.js'
|
||||
|
||||
export class ComponentInteraction extends Interaction {
|
||||
/** The channel id where this interaction occurred in. */
|
||||
channelID: BigString
|
||||
/** The guild id where this interaction occurred in. */
|
||||
guildID?: BigString
|
||||
/** The member object if this interaction occurred in a guild. */
|
||||
member?: Member
|
||||
/** The user object for the user that created this interaction. */
|
||||
user: User
|
||||
/** The permissions the app or bot has within the channel, the interaction was sent from. */
|
||||
appPermissions?: Permission
|
||||
/** The message object, if this interaction occurred on a message. */
|
||||
message?: Message
|
||||
/** The custom id of the component. */
|
||||
customID: string
|
||||
/** The type of component. */
|
||||
componentType: MessageComponentTypes
|
||||
/** The values from a selector component. */
|
||||
values?: string[]
|
||||
|
||||
constructor(data: DiscordInteraction, client: Client) {
|
||||
super(data, client)
|
||||
|
||||
this.channelID = data.channel_id!
|
||||
// this.data = data.data;
|
||||
this.guildID = data.guild_id
|
||||
// Required to make a component
|
||||
this.customID = data.data!.custom_id!
|
||||
this.componentType = data.data!.component_type!
|
||||
this.values = data.data!.values
|
||||
|
||||
if (data.member !== undefined && this.guild) {
|
||||
this.member = new Member(data.member, this.guild, this.client)
|
||||
this.guild.members.set(this.member.id, this.member)
|
||||
}
|
||||
|
||||
if (data.message !== undefined) {
|
||||
this.message = new Message(data.message, this.client)
|
||||
}
|
||||
|
||||
this.user = new User(data.user ?? data.member!.user, this.client)
|
||||
this.client.users.set(this.user.id, this.user)
|
||||
|
||||
if (data.app_permissions !== undefined) {
|
||||
this.appPermissions = new Permission(data.app_permissions)
|
||||
}
|
||||
}
|
||||
|
||||
/** The channel if cached, where this interaction occurred. */
|
||||
get channel(): AnyChannel | undefined {
|
||||
return this.channelID ? this.client.getChannel(this.channelID) : undefined
|
||||
}
|
||||
|
||||
/** The guild if cached, where this interaction occurred. */
|
||||
get guild(): Guild | undefined {
|
||||
return this.guildID ? this.client.guilds.get(this.guildID) : undefined
|
||||
}
|
||||
|
||||
/** Acknowledges the interaction with a defer message update response */
|
||||
async acknowledge(): Promise<void> {
|
||||
return await this.deferUpdate()
|
||||
}
|
||||
|
||||
/** Respond to the interaction with a followup message. */
|
||||
async createFollowup(content: WebhookPayload, file?: FileContent | FileContent[]): Promise<Message> {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error(
|
||||
'createFollowup cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, or editParent first.',
|
||||
)
|
||||
}
|
||||
|
||||
if (content !== undefined) {
|
||||
if (typeof content !== 'object' || content === null) {
|
||||
content = {
|
||||
content: '' + content,
|
||||
}
|
||||
} else if (content.content !== undefined && typeof content.content !== 'string') {
|
||||
content.content = '' + content.content
|
||||
}
|
||||
}
|
||||
|
||||
if (file) {
|
||||
content.file = file
|
||||
}
|
||||
|
||||
return await this.client.executeWebhook.call(this.client, this.applicationID, this.token, { ...content, wait: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledges the interaction with a message. If already acknowledged runs createFollowup
|
||||
* Note: You can **not** use more than 1 initial interaction response per interaction, use createFollowup if you have already responded with a different interaction response.
|
||||
*/
|
||||
async createMessage(content: InteractionApplicationCommandCallbackData, file?: FileContent | FileContent[]): Promise<Message | undefined> {
|
||||
if (this.acknowledged) {
|
||||
return await this.createFollowup(content, file)
|
||||
}
|
||||
|
||||
if (content !== undefined) {
|
||||
if (typeof content !== 'object' || content === null) {
|
||||
content = {
|
||||
content: '' + content,
|
||||
}
|
||||
} else if (content.content !== undefined && typeof content.content !== 'string') {
|
||||
content.content = '' + content.content
|
||||
}
|
||||
}
|
||||
|
||||
await this.client.createInteractionResponse
|
||||
.call(
|
||||
this.client,
|
||||
this.id,
|
||||
this.token,
|
||||
{
|
||||
type: InteractionResponseTypes.ChannelMessageWithSource,
|
||||
data: content,
|
||||
},
|
||||
file,
|
||||
)
|
||||
.then(() => this.update())
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledges the interaction with a defer response
|
||||
* Note: You can **not** use more than 1 initial interaction response per interaction.
|
||||
*/
|
||||
async defer(flags: number): Promise<void> {
|
||||
if (this.acknowledged) {
|
||||
throw new Error('You have already acknowledged this interaction.')
|
||||
}
|
||||
|
||||
return await this.client.createInteractionResponse
|
||||
.call(this.client, this.id, this.token, {
|
||||
type: InteractionResponseTypes.DeferredChannelMessageWithSource,
|
||||
data: {
|
||||
flags: flags || 0,
|
||||
},
|
||||
})
|
||||
.then(() => this.update())
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledges the interaction with a defer message update response
|
||||
* Note: You can **not** use more than 1 initial interaction response per interaction.
|
||||
*/
|
||||
async deferUpdate(): Promise<void> {
|
||||
if (this.acknowledged) {
|
||||
throw new Error('You have already acknowledged this interaction.')
|
||||
}
|
||||
|
||||
return await this.client.createInteractionResponse
|
||||
.call(this.client, this.id, this.token, {
|
||||
type: InteractionResponseTypes.DeferredUpdateMessage,
|
||||
})
|
||||
.then(() => this.update())
|
||||
}
|
||||
|
||||
/** Delete a message */
|
||||
async deleteMessage(messageID: BigString): Promise<void> {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error(
|
||||
'deleteMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, or editParent first.',
|
||||
)
|
||||
}
|
||||
|
||||
return await this.client.deleteWebhookMessage.call(this.client, this.applicationID, this.token, messageID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the parent message
|
||||
* Warning: Will error with ephemeral messages.
|
||||
*/
|
||||
async deleteOriginalMessage(): Promise<void> {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error(
|
||||
'deleteOriginalMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, or editParent first.',
|
||||
)
|
||||
}
|
||||
|
||||
return await this.client.deleteWebhookMessage.call(this.client, this.applicationID, this.token, '@original')
|
||||
}
|
||||
|
||||
/** Edit a message */
|
||||
async editMessage(messageID: BigString, content: MessageWebhookContent, file?: FileContent | FileContent[]): Promise<Message> {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error(
|
||||
'editMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, or editParent first.',
|
||||
)
|
||||
}
|
||||
|
||||
if (content !== undefined) {
|
||||
if (typeof content !== 'object' || content === null) {
|
||||
content = {
|
||||
content: '' + content,
|
||||
}
|
||||
} else if (content.content !== undefined && typeof content.content !== 'string') {
|
||||
content.content = '' + content.content
|
||||
}
|
||||
}
|
||||
|
||||
if (file) {
|
||||
content.file = file
|
||||
}
|
||||
|
||||
return await this.client.editWebhookMessage.call(this.client, this.applicationID, this.token, messageID, content)
|
||||
}
|
||||
|
||||
/** Edit the parent message */
|
||||
async editOriginalMessage(content: MessageWebhookContent, file?: FileContent | FileContent[]): Promise<Message> {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error(
|
||||
'editOriginalMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, or editParent first.',
|
||||
)
|
||||
}
|
||||
|
||||
if (content !== undefined) {
|
||||
if (typeof content !== 'object' || content === null) {
|
||||
content = {
|
||||
content: '' + content,
|
||||
}
|
||||
} else if (content.content !== undefined && typeof content.content !== 'string') {
|
||||
content.content = '' + content.content
|
||||
}
|
||||
}
|
||||
|
||||
if (file) {
|
||||
content.file = file
|
||||
}
|
||||
|
||||
return await this.client.editWebhookMessage.call(this.client, this.applicationID, this.token, '@original', content)
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledges the interaction by editing the parent message. If already acknowledged runs editOriginalMessage
|
||||
* Note: You can **not** use more than 1 initial interaction response per interaction, use edit if you have already responded with a different interaction response.
|
||||
* Warning: Will error with ephemeral messages.
|
||||
*/
|
||||
async editParent(content: InteractionApplicationCommandCallbackData, file?: FileContent | FileContent[]): Promise<Message | undefined> {
|
||||
if (this.acknowledged) {
|
||||
return this.editOriginalMessage(content)
|
||||
}
|
||||
|
||||
if (content !== undefined) {
|
||||
if (typeof content !== 'object' || content === null) {
|
||||
content = {
|
||||
content: '' + content,
|
||||
}
|
||||
} else if (content.content !== undefined && typeof content.content !== 'string') {
|
||||
content.content = '' + content.content
|
||||
}
|
||||
}
|
||||
|
||||
await this.client.createInteractionResponse
|
||||
.call(
|
||||
this.client,
|
||||
this.id,
|
||||
this.token,
|
||||
{
|
||||
type: InteractionResponseTypes.UpdateMessage,
|
||||
data: content,
|
||||
},
|
||||
file,
|
||||
)
|
||||
.then(() => this.update())
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent message
|
||||
* Warning: Will error with ephemeral messages.
|
||||
*/
|
||||
async getOriginalMessage(): Promise<Message> {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error(
|
||||
'getOriginalMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, or editParent first.',
|
||||
)
|
||||
}
|
||||
|
||||
return this.client.getWebhookMessage.call(this.client, this.applicationID, this.token, '@original')
|
||||
}
|
||||
}
|
||||
|
||||
export default ComponentInteraction
|
||||
62
packages/client/src/Structures/interactions/Interaction.ts
Normal file
62
packages/client/src/Structures/interactions/Interaction.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import { InteractionTypes, type BigString, type DiscordInteraction } from '@discordeno/types'
|
||||
import Base from '../../Base.js'
|
||||
import type Client from '../../Client.js'
|
||||
import AutocompleteInteraction from './Autocomplete.js'
|
||||
import CommandInteraction from './Command.js'
|
||||
import ComponentInteraction from './Component.js'
|
||||
import PingInteraction from './Ping.js'
|
||||
import UnknownInteraction from './Unknown.js'
|
||||
|
||||
export class Interaction extends Base {
|
||||
client: Client
|
||||
applicationID: BigString
|
||||
token: string
|
||||
type: InteractionTypes
|
||||
version: 1
|
||||
acknowledged: boolean
|
||||
|
||||
constructor(data: DiscordInteraction, client: Client) {
|
||||
super(data.id)
|
||||
this.client = client
|
||||
|
||||
this.applicationID = data.application_id
|
||||
this.token = data.token
|
||||
this.type = data.type
|
||||
this.version = data.version
|
||||
this.acknowledged = false
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `.client`
|
||||
*/
|
||||
get _client(): Client {
|
||||
return this.client
|
||||
}
|
||||
|
||||
update() {
|
||||
this.acknowledged = true
|
||||
}
|
||||
|
||||
static from(data: DiscordInteraction, client: Client) {
|
||||
switch (data.type) {
|
||||
case InteractionTypes.Ping: {
|
||||
return new PingInteraction(data, client)
|
||||
}
|
||||
case InteractionTypes.ApplicationCommand: {
|
||||
return new CommandInteraction(data, client)
|
||||
}
|
||||
case InteractionTypes.MessageComponent: {
|
||||
return new ComponentInteraction(data, client)
|
||||
}
|
||||
case InteractionTypes.ApplicationCommandAutocomplete: {
|
||||
return new AutocompleteInteraction(data, client)
|
||||
}
|
||||
}
|
||||
|
||||
client.emit('warn', new Error(`Unknown interaction type: ${data.type}\n${JSON.stringify(data)}`))
|
||||
return new UnknownInteraction(data, client)
|
||||
}
|
||||
}
|
||||
|
||||
export default Interaction
|
||||
33
packages/client/src/Structures/interactions/Ping.ts
Normal file
33
packages/client/src/Structures/interactions/Ping.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/* eslint-disable no-useless-call */
|
||||
/* eslint-disable @typescript-eslint/return-await */
|
||||
|
||||
import { InteractionResponseTypes } from '@discordeno/types'
|
||||
import Interaction from './Interaction.js'
|
||||
|
||||
export class PingInteraction extends Interaction {
|
||||
/**
|
||||
* Acknowledges the ping interaction with a pong response.
|
||||
* Note: You can **not** use more than 1 initial interaction response per interaction.
|
||||
*/
|
||||
async acknowledge(): Promise<void> {
|
||||
return this.pong()
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledges the ping interaction with a pong response.
|
||||
* Note: You can **not** use more than 1 initial interaction response per interaction.
|
||||
*/
|
||||
async pong(): Promise<void> {
|
||||
if (this.acknowledged) {
|
||||
throw new Error('You have already acknowledged this interaction.')
|
||||
}
|
||||
|
||||
return await this.client.createInteractionResponse
|
||||
.call(this.client, this.id, this.token, {
|
||||
type: InteractionResponseTypes.Pong,
|
||||
})
|
||||
.then(() => this.update())
|
||||
}
|
||||
}
|
||||
|
||||
export default PingInteraction
|
||||
493
packages/client/src/Structures/interactions/Unknown.ts
Normal file
493
packages/client/src/Structures/interactions/Unknown.ts
Normal file
@@ -0,0 +1,493 @@
|
||||
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable no-useless-call */
|
||||
|
||||
import type { DiscordInteraction } from '@discordeno/types'
|
||||
import { InteractionResponseTypes } from '@discordeno/types'
|
||||
import type Client from '../../Client.js'
|
||||
import type {
|
||||
ApplicationCommandOptionChoice,
|
||||
FileContent,
|
||||
InteractionContent,
|
||||
InteractionContentEdit,
|
||||
InteractionResponse,
|
||||
PossiblyUncachedTextable,
|
||||
TextableChannel,
|
||||
} from '../../typings.js'
|
||||
import Member from '../guilds/Member.js'
|
||||
import Message from '../Message.js'
|
||||
import Permission from '../Permission.js'
|
||||
import User from '../users/User.js'
|
||||
import Interaction from './Interaction.js'
|
||||
|
||||
export class UnknownInteraction<T extends PossiblyUncachedTextable = TextableChannel> extends Interaction {
|
||||
channel?: T
|
||||
data?: unknown
|
||||
guildID?: string
|
||||
member?: Member
|
||||
message?: Message
|
||||
type: number = 0
|
||||
user?: User
|
||||
appPermissions?: Permission
|
||||
|
||||
constructor(data: DiscordInteraction, client: Client) {
|
||||
super(data, client)
|
||||
|
||||
if (data.channel_id !== undefined) {
|
||||
this.channel = (this.client.getChannel(data.channel_id) as unknown as T) || {
|
||||
id: data.channel_id,
|
||||
}
|
||||
}
|
||||
|
||||
if (data.data !== undefined) {
|
||||
this.data = data.data
|
||||
}
|
||||
|
||||
if (data.guild_id !== undefined) {
|
||||
this.guildID = data.guild_id
|
||||
}
|
||||
|
||||
if (data.member !== undefined) {
|
||||
const guild = this.client.guilds.get(data.guild_id ?? '')!
|
||||
this.member = new Member(data.member, guild, this.client)
|
||||
guild.members.set(this.member.id, this.member)
|
||||
}
|
||||
|
||||
if (data.message !== undefined) {
|
||||
this.message = new Message(data.message, this.client)
|
||||
}
|
||||
|
||||
if (data.user !== undefined) {
|
||||
this.user = new User(data.user, this.client)
|
||||
this.client.users.set(this.user.id, this.user)
|
||||
}
|
||||
|
||||
if (data.app_permissions !== undefined) {
|
||||
this.appPermissions = new Permission(data.app_permissions)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledges the autocomplete interaction with a result of choices.
|
||||
* Note: You can **not** use more than 1 initial interaction response per interaction.
|
||||
* @arg {Object} data The data object
|
||||
* @arg {Number} data.type The type of [interaction response](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type) to send
|
||||
* @arg {Object} data.data The data to return to discord
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async acknowledge(data: InteractionResponse) {
|
||||
if (this.acknowledged) {
|
||||
throw new Error('You have already acknowledged this interaction.')
|
||||
}
|
||||
return await this.client.createInteractionResponse.call(this.client, this.id, this.token, data).then(() => this.update())
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to the interaction with a followup message
|
||||
* @arg {String | Object} content A string or object. If an object is passed:
|
||||
* @arg {Object} [content.allowedMentions] A list of mentions to allow (overrides default)
|
||||
* @arg {Boolean} [content.allowedMentions.everyone] Whether or not to allow @everyone/@here.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.roles] Whether or not to allow all role mentions, or an array of specific role mentions to allow.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.users] Whether or not to allow all user mentions, or an array of specific user mentions to allow.
|
||||
* @arg {Array<Object>} [content.components] An array of component objects
|
||||
* @arg {String} [content.components[].custom_id] The ID of the component (type 2 style 0-4 and type 3 only)
|
||||
* @arg {Boolean} [content.components[].disabled] Whether the component is disabled (type 2 and 3 only)
|
||||
* @arg {Object} [content.components[].emoji] The emoji to be displayed in the component (type 2)
|
||||
* @arg {String} [content.components[].label] The label to be displayed in the component (type 2)
|
||||
* @arg {Number} [content.components[].max_values] The maximum number of items that can be chosen (1-25, default 1)
|
||||
* @arg {Number} [content.components[].min_values] The minimum number of items that must be chosen (0-25, default 1)
|
||||
* @arg {Array<Object>} [content.components[].options] The options for this component (type 3 only)
|
||||
* @arg {Boolean} [content.components[].options[].default] Whether this option should be the default value selected
|
||||
* @arg {String} [content.components[].options[].description] The description for this option
|
||||
* @arg {Object} [content.components[].options[].emoji] The emoji to be displayed in this option
|
||||
* @arg {String} content.components[].options[].label The label for this option
|
||||
* @arg {Number | String} content.components[].options[].value The value for this option
|
||||
* @arg {String} [content.components[].placeholder] The placeholder text for the component when no option is selected (type 3 only)
|
||||
* @arg {Number} [content.components[].style] The style of the component (type 2 only) - If 0-4, `custom_id` is required; if 5, `url` is required
|
||||
* @arg {Number} content.components[].type The type of component - If 1, it is a collection and a `components` array (nested) is required; if 2, it is a button; if 3, it is a select menu
|
||||
* @arg {String} [content.components[].url] The URL that the component should open for users (type 2 style 5 only)
|
||||
* @arg {String} [content.content] A content string
|
||||
* @arg {Object} [content.embed] An embed object. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
|
||||
* @arg {Array<Object>} [options.embeds] An array of embed objects. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
|
||||
* @arg {Number} [content.flags] 64 for Ephemeral
|
||||
* @arg {Boolean} [content.tts] Set the message TTS flag
|
||||
* @arg {Object | Array<Object>} [file] A file object (or an Array of them)
|
||||
* @arg {Buffer} file.file A buffer containing file data
|
||||
* @arg {String} file.name What to name the file
|
||||
* @returns {Promise<Message?>}
|
||||
*/
|
||||
async createFollowup(content: string | InteractionContent, file?: FileContent | FileContent[]) {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error(
|
||||
'createFollowup cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, editParent, pong, or result first.',
|
||||
)
|
||||
}
|
||||
if (content !== undefined) {
|
||||
if (typeof content !== 'object' || content === null) {
|
||||
content = {
|
||||
content: '' + content,
|
||||
}
|
||||
} else if (content.content !== undefined && typeof content.content !== 'string') {
|
||||
content.content = '' + content.content
|
||||
}
|
||||
}
|
||||
if (file) {
|
||||
content.file = file
|
||||
}
|
||||
return await this.client.executeWebhook.call(this.client, this.applicationID, this.token, Object.assign({ wait: true as true }, content))
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledges the interaction with a message. If already acknowledged runs createFollowup
|
||||
* Note: You can **not** use more than 1 initial interaction response per interaction, use createFollowup if you have already responded with a different interaction response.
|
||||
* @arg {String | Object} content A string or object. If an object is passed:
|
||||
* @arg {Object} [content.allowedMentions] A list of mentions to allow (overrides default)
|
||||
* @arg {Boolean} [content.allowedMentions.everyone] Whether or not to allow @everyone/@here.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.roles] Whether or not to allow all role mentions, or an array of specific role mentions to allow.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.users] Whether or not to allow all user mentions, or an array of specific user mentions to allow.
|
||||
* @arg {Array<Object>} [content.components] An array of component objects
|
||||
* @arg {String} [content.components[].custom_id] The ID of the component (type 2 style 0-4 and type 3 only)
|
||||
* @arg {Boolean} [content.components[].disabled] Whether the component is disabled (type 2 and 3 only)
|
||||
* @arg {Object} [content.components[].emoji] The emoji to be displayed in the component (type 2)
|
||||
* @arg {String} [content.components[].label] The label to be displayed in the component (type 2)
|
||||
* @arg {Number} [content.components[].max_values] The maximum number of items that can be chosen (1-25, default 1)
|
||||
* @arg {Number} [content.components[].min_values] The minimum number of items that must be chosen (0-25, default 1)
|
||||
* @arg {Array<Object>} [content.components[].options] The options for this component (type 3 only)
|
||||
* @arg {Boolean} [content.components[].options[].default] Whether this option should be the default value selected
|
||||
* @arg {String} [content.components[].options[].description] The description for this option
|
||||
* @arg {Object} [content.components[].options[].emoji] The emoji to be displayed in this option
|
||||
* @arg {String} content.components[].options[].label The label for this option
|
||||
* @arg {Number | String} content.components[].options[].value The value for this option
|
||||
* @arg {String} [content.components[].placeholder] The placeholder text for the component when no option is selected (type 3 only)
|
||||
* @arg {Number} [content.components[].style] The style of the component (type 2 only) - If 0-4, `custom_id` is required; if 5, `url` is required
|
||||
* @arg {Number} content.components[].type The type of component - If 1, it is a collection and a `components` array (nested) is required; if 2, it is a button; if 3, it is a select menu
|
||||
* @arg {String} [content.components[].url] The URL that the component should open for users (type 2 style 5 only)
|
||||
* @arg {String} [content.content] A content string
|
||||
* @arg {Object} [content.embed] An embed object. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
|
||||
* @arg {Array<Object>} [content.embeds] An array of embed objects. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
|
||||
* @arg {Boolean} [content.flags] 64 for Ephemeral
|
||||
* @arg {Boolean} [content.tts] Set the message TTS flag
|
||||
* @arg {Object | Array<Object>} [file] A file object (or an Array of them)
|
||||
* @arg {Buffer} file.file A buffer containing file data
|
||||
* @arg {String} file.name What to name the file
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async createMessage(content: string | InteractionContent, file?: FileContent | FileContent[]) {
|
||||
if (this.acknowledged) {
|
||||
return await this.createFollowup(content, file)
|
||||
}
|
||||
if (content !== undefined) {
|
||||
if (typeof content !== 'object' || content === null) {
|
||||
content = {
|
||||
content: '' + content,
|
||||
}
|
||||
} else if (content.content !== undefined && typeof content.content !== 'string') {
|
||||
content.content = '' + content.content
|
||||
}
|
||||
}
|
||||
|
||||
return await this.client.createInteractionResponse
|
||||
.call(
|
||||
this.client,
|
||||
this.id,
|
||||
this.token,
|
||||
{
|
||||
type: InteractionResponseTypes.ChannelMessageWithSource,
|
||||
data: content,
|
||||
},
|
||||
file,
|
||||
)
|
||||
.then(() => this.update())
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledges the interaction with a defer response
|
||||
* Note: You can **not** use more than 1 initial interaction response per interaction.
|
||||
* @arg {Number} [flags] 64 for Ephemeral
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async defer(flags: number) {
|
||||
if (this.acknowledged) {
|
||||
throw new Error('You have already acknowledged this interaction.')
|
||||
}
|
||||
return await this.client.createInteractionResponse
|
||||
.call(this.client, this.id, this.token, {
|
||||
type: InteractionResponseTypes.DeferredChannelMessageWithSource,
|
||||
data: {
|
||||
flags: flags || 0,
|
||||
},
|
||||
})
|
||||
.then(() => this.update())
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledges the interaction with a defer message update response (Message Component only)
|
||||
* Note: You can **not** use more than 1 initial interaction response per interaction.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async deferUpdate() {
|
||||
if (this.acknowledged) {
|
||||
throw new Error('You have already acknowledged this interaction.')
|
||||
}
|
||||
return await this.client.createInteractionResponse
|
||||
.call(this.client, this.id, this.token, {
|
||||
type: InteractionResponseTypes.DeferredUpdateMessage,
|
||||
})
|
||||
.then(() => this.update())
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a message
|
||||
* @arg {String} messageID the id of the message to delete, or "@original" for the original response.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async deleteMessage(messageID: string) {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error(
|
||||
'deleteMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, editParent, or pong first.',
|
||||
)
|
||||
}
|
||||
return await this.client.deleteWebhookMessage.call(this.client, this.applicationID, this.token, messageID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the Original message (or the parent message for components)
|
||||
* Warning: Will error with ephemeral messages.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async deleteOriginalMessage() {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error(
|
||||
'deleteOriginalMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, editParent, or pong first.',
|
||||
)
|
||||
}
|
||||
return await this.client.deleteWebhookMessage.call(this.client, this.applicationID, this.token, '@original')
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a message
|
||||
* @arg {String} messageID the id of the message to edit, or "@original" for the original response.
|
||||
* @arg {Object} content Interaction message edit options
|
||||
* @arg {Object} [content.allowedMentions] A list of mentions to allow (overrides default)
|
||||
* @arg {Boolean} [content.allowedMentions.everyone] Whether or not to allow @everyone/@here.
|
||||
* @arg {Boolean} [content.allowedMentions.repliedUser] Whether or not to mention the author of the message being replied to.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.roles] Whether or not to allow all role mentions, or an array of specific role mentions to allow.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.users] Whether or not to allow all user mentions, or an array of specific user mentions to allow.
|
||||
* @arg {Array<Object>} [content.components] An array of component objects
|
||||
* @arg {String} [content.components[].custom_id] The ID of the component (type 2 style 0-4 and type 3 only)
|
||||
* @arg {Boolean} [content.components[].disabled] Whether the component is disabled (type 2 and 3 only)
|
||||
* @arg {Object} [content.components[].emoji] The emoji to be displayed in the component (type 2)
|
||||
* @arg {String} [content.components[].label] The label to be displayed in the component (type 2)
|
||||
* @arg {Number} [content.components[].max_values] The maximum number of items that can be chosen (1-25, default 1)
|
||||
* @arg {Number} [content.components[].min_values] The minimum number of items that must be chosen (0-25, default 1)
|
||||
* @arg {Array<Object>} [content.components[].options] The options for this component (type 3 only)
|
||||
* @arg {Boolean} [content.components[].options[].default] Whether this option should be the default value selected
|
||||
* @arg {String} [content.components[].options[].description] The description for this option
|
||||
* @arg {Object} [content.components[].options[].emoji] The emoji to be displayed in this option
|
||||
* @arg {String} content.components[].options[].label The label for this option
|
||||
* @arg {Number | String} content.components[].options[].value The value for this option
|
||||
* @arg {String} [content.components[].placeholder] The placeholder text for the component when no option is selected (type 3 only)
|
||||
* @arg {Number} [content.components[].style] The style of the component (type 2 only) - If 0-4, `custom_id` is required; if 5, `url` is required
|
||||
* @arg {Number} content.components[].type The type of component - If 1, it is a collection and a `components` array (nested) is required; if 2, it is a button; if 3, it is a select menu
|
||||
* @arg {String} [content.components[].url] The URL that the component should open for users (type 2 style 5 only)
|
||||
* @arg {String} [content.content] A content string
|
||||
* @arg {Object} [content.embed] An embed object. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
|
||||
* @arg {Array<Object>} [content.embeds] An array of embed objects. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
|
||||
* @arg {Object | Array<Object>} [file] A file object (or an Array of them)
|
||||
* @arg {Buffer} file.file A buffer containing file data
|
||||
* @arg {String} file.name What to name the file
|
||||
* @returns {Promise<Message>}
|
||||
*/
|
||||
async editMessage(messageID: string, content: string | InteractionContentEdit, file?: FileContent | FileContent[]) {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error(
|
||||
'editMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, editParent, pong, or result first.',
|
||||
)
|
||||
}
|
||||
if (content !== undefined) {
|
||||
if (typeof content !== 'object' || content === null) {
|
||||
content = {
|
||||
content: '' + content,
|
||||
}
|
||||
} else if (content.content !== undefined && typeof content.content !== 'string') {
|
||||
content.content = '' + content.content
|
||||
}
|
||||
}
|
||||
if (file) {
|
||||
content.file = file
|
||||
}
|
||||
return await this.client.editWebhookMessage.call(this.client, this.applicationID, this.token, messageID, content)
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit the Original response message
|
||||
* @arg {Object} content Interaction message edit options (or the parent message for components)
|
||||
* @arg {Object} [content.allowedMentions] A list of mentions to allow (overrides default)
|
||||
* @arg {Boolean} [content.allowedMentions.everyone] Whether or not to allow @everyone/@here.
|
||||
* @arg {Boolean} [content.allowedMentions.repliedUser] Whether or not to mention the author of the message being replied to.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.roles] Whether or not to allow all role mentions, or an array of specific role mentions to allow.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.users] Whether or not to allow all user mentions, or an array of specific user mentions to allow.
|
||||
* @arg {Array<Object>} [content.components] An array of component objects
|
||||
* @arg {String} [content.components[].custom_id] The ID of the component (type 2 style 0-4 and type 3 only)
|
||||
* @arg {Boolean} [content.components[].disabled] Whether the component is disabled (type 2 and 3 only)
|
||||
* @arg {Object} [content.components[].emoji] The emoji to be displayed in the component (type 2)
|
||||
* @arg {String} [content.components[].label] The label to be displayed in the component (type 2)
|
||||
* @arg {Number} [content.components[].max_values] The maximum number of items that can be chosen (1-25, default 1)
|
||||
* @arg {Number} [content.components[].min_values] The minimum number of items that must be chosen (0-25, default 1)
|
||||
* @arg {Array<Object>} [content.components[].options] The options for this component (type 3 only)
|
||||
* @arg {Boolean} [content.components[].options[].default] Whether this option should be the default value selected
|
||||
* @arg {String} [content.components[].options[].description] The description for this option
|
||||
* @arg {Object} [content.components[].options[].emoji] The emoji to be displayed in this option
|
||||
* @arg {String} content.components[].options[].label The label for this option
|
||||
* @arg {Number | String} content.components[].options[].value The value for this option
|
||||
* @arg {String} [content.components[].placeholder] The placeholder text for the component when no option is selected (type 3 only)
|
||||
* @arg {Number} [content.components[].style] The style of the component (type 2 only) - If 0-4, `custom_id` is required; if 5, `url` is required
|
||||
* @arg {Number} content.components[].type The type of component - If 1, it is a collection and a `components` array (nested) is required; if 2, it is a button; if 3, it is a select menu
|
||||
* @arg {String} [content.components[].url] The URL that the component should open for users (type 2 style 5 only)
|
||||
* @arg {String} [content.content] A content string
|
||||
* @arg {Object} [content.embed] An embed object. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
|
||||
* @arg {Array<Object>} [content.embeds] An array of embed objects. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
|
||||
* @arg {Object | Array<Object>} [file] A file object (or an Array of them)
|
||||
* @arg {Buffer} file.file A buffer containing file data
|
||||
* @arg {String} file.name What to name the file
|
||||
* @returns {Promise<Message>}
|
||||
*/
|
||||
async editOriginalMessage(content: string | InteractionContentEdit, file?: FileContent | FileContent[]) {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error(
|
||||
'editOriginalMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, editParent, pong, or result first.',
|
||||
)
|
||||
}
|
||||
if (content !== undefined) {
|
||||
if (typeof content !== 'object' || content === null) {
|
||||
content = {
|
||||
content: '' + content,
|
||||
}
|
||||
} else if (content.content !== undefined && typeof content.content !== 'string') {
|
||||
content.content = '' + content.content
|
||||
}
|
||||
}
|
||||
if (file) {
|
||||
content.file = file
|
||||
}
|
||||
|
||||
return await this.client.editWebhookMessage.call(this.client, this.applicationID, this.token, '@original', content)
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledges the interaction by editing the parent message. If already acknowledged runs editOriginalMessage (Message Component only)
|
||||
* Note: You can **not** use more than 1 initial interaction response per interaction, use edit if you have already responded with a different interaction response.
|
||||
* Warning: Will error with ephemeral messages.
|
||||
* @arg {String | Object} content What to edit the message with
|
||||
* @arg {Object} [content.allowedMentions] A list of mentions to allow (overrides default)
|
||||
* @arg {Boolean} [content.allowedMentions.everyone] Whether or not to allow @everyone/@here.
|
||||
* @arg {Boolean} [content.allowedMentions.repliedUser] Whether or not to mention the author of the message being replied to.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.roles] Whether or not to allow all role mentions, or an array of specific role mentions to allow.
|
||||
* @arg {Boolean | Array<String>} [content.allowedMentions.users] Whether or not to allow all user mentions, or an array of specific user mentions to allow.
|
||||
* @arg {Array<Object>} [content.components] An array of component objects
|
||||
* @arg {String} [content.components[].custom_id] The ID of the component (type 2 style 0-4 and type 3 only)
|
||||
* @arg {Boolean} [content.components[].disabled] Whether the component is disabled (type 2 and 3 only)
|
||||
* @arg {Object} [content.components[].emoji] The emoji to be displayed in the component (type 2)
|
||||
* @arg {String} [content.components[].label] The label to be displayed in the component (type 2)
|
||||
* @arg {Number} [content.components[].max_values] The maximum number of items that can be chosen (1-25, default 1)
|
||||
* @arg {Number} [content.components[].min_values] The minimum number of items that must be chosen (0-25, default 1)
|
||||
* @arg {Array<Object>} [content.components[].options] The options for this component (type 3 only)
|
||||
* @arg {Boolean} [content.components[].options[].default] Whether this option should be the default value selected
|
||||
* @arg {String} [content.components[].options[].description] The description for this option
|
||||
* @arg {Object} [content.components[].options[].emoji] The emoji to be displayed in this option
|
||||
* @arg {String} content.components[].options[].label The label for this option
|
||||
* @arg {Number | String} content.components[].options[].value The value for this option
|
||||
* @arg {String} [content.components[].placeholder] The placeholder text for the component when no option is selected (type 3 only)
|
||||
* @arg {Number} [content.components[].style] The style of the component (type 2 only) - If 0-4, `custom_id` is required; if 5, `url` is required
|
||||
* @arg {Number} content.components[].type The type of component - If 1, it is a collection and a `components` array (nested) is required; if 2, it is a button; if 3, it is a select menu
|
||||
* @arg {String} [content.components[].url] The URL that the component should open for users (type 2 style 5 only)
|
||||
* @arg {String} [content.content] A content string
|
||||
* @arg {Object} [content.embed] An embed object. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
|
||||
* @arg {Array<Object>} [content.embeds] An array of embed objects. See [the official Discord API documentation entry](https://discord.com/developers/docs/resources/channel#embed-object) for object structure
|
||||
* @arg {Boolean} [content.flags] 64 for Ephemeral
|
||||
* @arg {Boolean} [content.tts] Set the message TTS flag
|
||||
* @arg {Object | Array<Object>} [file] A file object (or an Array of them)
|
||||
* @arg {Buffer} file.file A buffer containing file data
|
||||
* @arg {String} file.name What to name the file
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async editParent(content: InteractionContentEdit, file?: FileContent | FileContent[]) {
|
||||
if (this.acknowledged) {
|
||||
return await this.editOriginalMessage(content)
|
||||
}
|
||||
if (content !== undefined) {
|
||||
if (typeof content !== 'object' || content === null) {
|
||||
content = {
|
||||
content: '' + content,
|
||||
}
|
||||
} else if (content.content !== undefined && typeof content.content !== 'string') {
|
||||
content.content = '' + content.content
|
||||
}
|
||||
}
|
||||
|
||||
return await this.client.createInteractionResponse
|
||||
.call(
|
||||
this.client,
|
||||
this.id,
|
||||
this.token,
|
||||
{
|
||||
type: InteractionResponseTypes.UpdateMessage,
|
||||
data: content,
|
||||
},
|
||||
file,
|
||||
)
|
||||
.then(() => this.update())
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Original response message (or the parent message for components)
|
||||
* Warning: Will error with ephemeral messages.
|
||||
* @returns {Promise<Message>}
|
||||
*/
|
||||
async getOriginalMessage() {
|
||||
if (!this.acknowledged) {
|
||||
throw new Error(
|
||||
'getOriginalMessage cannot be used to acknowledge an interaction, please use acknowledge, createMessage, defer, deferUpdate, editParent, or pong first.',
|
||||
)
|
||||
}
|
||||
return await this.client.getWebhookMessage.call(this.client, this.applicationID, this.token, '@original')
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledges the ping interaction with a pong response (Ping Only)
|
||||
* Note: You can **not** use more than 1 initial interaction response per interaction.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async pong() {
|
||||
if (this.acknowledged) {
|
||||
throw new Error('You have already acknowledged this interaction.')
|
||||
}
|
||||
return await this.client.createInteractionResponse
|
||||
.call(this.client, this.id, this.token, {
|
||||
type: InteractionResponseTypes.Pong,
|
||||
})
|
||||
.then(() => this.update())
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledges the autocomplete interaction with a result of choices.
|
||||
* Note: You can **not** use more than 1 initial interaction response per interaction.
|
||||
* @arg {Array<Object>} choices The autocomplete choices to return to the user
|
||||
* @arg {String | Number} choices[].name The choice display name
|
||||
* @arg {String} choices[].value The choice value to return to the bot
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async result(choices: ApplicationCommandOptionChoice[]) {
|
||||
if (this.acknowledged) {
|
||||
throw new Error('You have already acknowledged this interaction.')
|
||||
}
|
||||
return await this.client.createInteractionResponse
|
||||
.call(this.client, this.id, this.token, {
|
||||
type: InteractionResponseTypes.ApplicationCommandAutocompleteResult,
|
||||
data: { choices },
|
||||
})
|
||||
.then(() => this.update())
|
||||
}
|
||||
}
|
||||
|
||||
export default UnknownInteraction
|
||||
38
packages/client/src/Structures/users/Extended.ts
Normal file
38
packages/client/src/Structures/users/Extended.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { PremiumTypes, DiscordUser } from "@discordeno/types"
|
||||
import type Client from "../../Client.js"
|
||||
import User from "./User.js"
|
||||
|
||||
|
||||
export class ExtendedUser extends User {
|
||||
email?: string | null
|
||||
verified?: boolean
|
||||
mfaEnabled?: boolean
|
||||
premiumType?: PremiumTypes
|
||||
|
||||
constructor(data: DiscordUser, client: Client) {
|
||||
super(data, client)
|
||||
this.update(data)
|
||||
}
|
||||
|
||||
update(data: DiscordUser): void {
|
||||
super.update(data)
|
||||
if (data.email !== undefined) {
|
||||
this.email = data.email
|
||||
}
|
||||
if (data.verified !== undefined) {
|
||||
this.verified = data.verified
|
||||
}
|
||||
if (data.mfa_enabled !== undefined) {
|
||||
this.mfaEnabled = data.mfa_enabled
|
||||
}
|
||||
if (data.premium_type !== undefined) {
|
||||
this.premiumType = data.premium_type
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON(['email', 'mfaEnabled', 'premium', 'verified', ...props])
|
||||
}
|
||||
}
|
||||
|
||||
export default ExtendedUser
|
||||
128
packages/client/src/Structures/users/User.ts
Normal file
128
packages/client/src/Structures/users/User.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
/* eslint-disable no-useless-call */
|
||||
import type { BigString, DiscordUser, UserFlags } from '@discordeno/types'
|
||||
import Base from '../../Base.js'
|
||||
import type Client from '../../Client.js'
|
||||
import type { ImageFormat, ImageSize } from '../../Client.js'
|
||||
import { BANNER, DEFAULT_USER_AVATAR, USER_AVATAR } from '../../Endpoints.js'
|
||||
import type PrivateChannel from '../channels/Private.js'
|
||||
|
||||
export class User extends Base {
|
||||
client: Client
|
||||
bot: boolean
|
||||
system: boolean
|
||||
_avatar!: bigint | null
|
||||
username!: string
|
||||
discriminator!: string
|
||||
publicFlags?: UserFlags
|
||||
_banner!: bigint | null
|
||||
accentColor?: number
|
||||
|
||||
constructor(data: DiscordUser, client: Client) {
|
||||
super(data.id)
|
||||
|
||||
this.client = client
|
||||
this.bot = !!data.bot
|
||||
this.system = !!data.system
|
||||
|
||||
this.update(data)
|
||||
}
|
||||
|
||||
/** @deprecated Use User.client Supported for Eris api compatibility. */
|
||||
get _client(): Client {
|
||||
return this.client
|
||||
}
|
||||
|
||||
get avatar(): string | null {
|
||||
if (!this._avatar) return null
|
||||
|
||||
return this.client.iconBigintToHash(this._avatar)
|
||||
}
|
||||
|
||||
set avatar(value: BigString | null) {
|
||||
this._avatar = typeof value === 'string' ? this.client.iconHashToBigInt(value) : value
|
||||
}
|
||||
|
||||
get banner(): string | null {
|
||||
if (!this._banner) return null
|
||||
|
||||
return this.client.iconBigintToHash(this._banner)
|
||||
}
|
||||
|
||||
set banner(value: BigString | null) {
|
||||
this._banner = typeof value === 'string' ? this.client.iconHashToBigInt(value) : value
|
||||
}
|
||||
|
||||
get avatarURL(): string | null {
|
||||
return this.avatar ? this.client._formatImage(USER_AVATAR(this.id, this.avatar)) : this.defaultAvatarURL
|
||||
}
|
||||
|
||||
get bannerURL(): string | null {
|
||||
if (!this.banner) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.client._formatImage(BANNER(this.id, this.banner))
|
||||
}
|
||||
|
||||
get defaultAvatar(): string {
|
||||
// @ts-expect-error some eris magic at play here
|
||||
return (this.discriminator % 5).toString()
|
||||
}
|
||||
|
||||
get defaultAvatarURL(): string {
|
||||
return `${this.client.CDN_URL}${DEFAULT_USER_AVATAR(this.defaultAvatar)}.png`
|
||||
}
|
||||
|
||||
get mention(): string {
|
||||
return `<@${this.id}>`
|
||||
}
|
||||
|
||||
get staticAvatarURL(): string {
|
||||
return this.avatar ? this.client._formatImage(USER_AVATAR(this.id, this.avatar), 'jpg') : this.defaultAvatarURL
|
||||
}
|
||||
|
||||
update(data: DiscordUser): void {
|
||||
if (data.avatar !== undefined) {
|
||||
this.avatar = data.avatar
|
||||
}
|
||||
if (data.username !== undefined) {
|
||||
this.username = data.username
|
||||
}
|
||||
if (data.discriminator !== undefined) {
|
||||
this.discriminator = data.discriminator
|
||||
}
|
||||
if (data.public_flags !== undefined) {
|
||||
this.publicFlags = data.public_flags
|
||||
}
|
||||
if (data.banner !== undefined) {
|
||||
this.banner = data.banner
|
||||
}
|
||||
if (data.accent_color !== undefined) {
|
||||
this.accentColor = data.accent_color
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the user's avatar with the given format and size */
|
||||
dynamicAvatarURL(format?: ImageFormat, size?: ImageSize): string {
|
||||
return this.avatar ? this.client._formatImage(USER_AVATAR(this.id, this.avatar), format, size) : this.defaultAvatarURL
|
||||
}
|
||||
|
||||
/** Get the user's banner with the given format and size */
|
||||
dynamicBannerURL(format?: ImageFormat, size?: ImageSize): string | null {
|
||||
return this.banner ? this.client._formatImage(BANNER(this.id, this.banner), format, size) : null
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a DM channel with the user, or create one if it does not exist
|
||||
* @returns {Promise<PrivateChannel>}
|
||||
*/
|
||||
async getDMChannel(): Promise<PrivateChannel> {
|
||||
return await this.client.getDMChannel.call(this.client, this.id)
|
||||
}
|
||||
|
||||
toJSON(props: string[] = []): Record<string, any> {
|
||||
return super.toJSON(['accentColor', 'avatar', 'banner', 'bot', 'discriminator', 'publicFlags', 'system', 'username', ...props])
|
||||
}
|
||||
}
|
||||
|
||||
export default User
|
||||
2030
packages/client/src/gateway/Shard.ts
Normal file
2030
packages/client/src/gateway/Shard.ts
Normal file
File diff suppressed because it is too large
Load Diff
157
packages/client/src/gateway/ShardManager.ts
Normal file
157
packages/client/src/gateway/ShardManager.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import Base from '../Base.js'
|
||||
import type Client from '../Client.js'
|
||||
import Collection from '../Collection.js'
|
||||
import type { ShardManagerOptions } from '../typings.js'
|
||||
import Shard from './Shard.js'
|
||||
|
||||
export class ShardManager extends Collection<number, Shard> {
|
||||
/** The client manager */
|
||||
client: Client
|
||||
/** The options that were used to configure this manager. */
|
||||
options: ShardManagerOptions
|
||||
/** The buckets that this manager is handling. */
|
||||
buckets: Map<number, number>
|
||||
/** The queue in which to connect a shard. */
|
||||
connectQueue: Shard[]
|
||||
/** The timeout to use for connecting a shard. */
|
||||
connectTimeout: NodeJS.Timeout | null
|
||||
|
||||
constructor(client: Client, options: ShardManagerOptions = {}) {
|
||||
super()
|
||||
this.client = client
|
||||
|
||||
this.options = Object.assign(
|
||||
{
|
||||
concurrency: 1,
|
||||
},
|
||||
options,
|
||||
)
|
||||
|
||||
this.buckets = new Map()
|
||||
this.connectQueue = []
|
||||
this.connectTimeout = null
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `.client` instead.
|
||||
*/
|
||||
get _client(): Client {
|
||||
return this.client
|
||||
}
|
||||
|
||||
connect(shard: Shard) {
|
||||
this.connectQueue.push(shard)
|
||||
this.tryConnect()
|
||||
}
|
||||
|
||||
get concurrency(): number {
|
||||
return this.options.concurrency as number
|
||||
}
|
||||
|
||||
setConcurrency(concurrency: number) {
|
||||
this.options.concurrency = concurrency
|
||||
}
|
||||
|
||||
spawn(id: number) {
|
||||
let shard = this.get(id)
|
||||
|
||||
if (!shard) {
|
||||
shard = new Shard(id, this.client)
|
||||
this.set(id, shard)
|
||||
|
||||
shard
|
||||
.on('ready', () => {
|
||||
this.client.emit('shardReady', shard!.id)
|
||||
if (this.client.ready) return
|
||||
|
||||
for (const other of this.values()) if (!other.ready) return
|
||||
|
||||
this.client.ready = true
|
||||
this.client.startTime = Date.now()
|
||||
|
||||
this.client.emit('ready')
|
||||
})
|
||||
.on('resume', () => {
|
||||
this.client.emit('shardResume', shard!.id)
|
||||
if (this.client.ready) return
|
||||
|
||||
for (const other of this.values()) if (!other.ready) return
|
||||
|
||||
this.client.ready = true
|
||||
this.client.startTime = Date.now()
|
||||
this.client.emit('ready')
|
||||
})
|
||||
.on('disconnect', (error) => {
|
||||
this.client.emit('shardDisconnect', error, shard!.id)
|
||||
for (const other of this.values()) if (other.ready) return
|
||||
|
||||
this.client.ready = false
|
||||
this.client.startTime = 0
|
||||
this.client.emit('disconnect')
|
||||
})
|
||||
}
|
||||
|
||||
if (shard.status === 'disconnected') {
|
||||
return this.connect(shard)
|
||||
}
|
||||
}
|
||||
|
||||
tryConnect() {
|
||||
// nothing in queue
|
||||
if (this.connectQueue.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
// loop over the connectQueue
|
||||
for (const shard of this.connectQueue) {
|
||||
// find the bucket for our shard
|
||||
const rateLimitKey = shard.id % this.concurrency || 0
|
||||
const lastConnect = this.buckets.get(rateLimitKey) ?? 0
|
||||
|
||||
// has enough time passed since the last connect for this bucket (5s/bucket)?
|
||||
// alternatively if we have a sessionID, we can skip this check
|
||||
if (!shard.sessionID && Date.now() - lastConnect < 5000) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Are there any connecting shards in the same bucket we should wait on?
|
||||
if (this.some((s) => s.connecting && (s.id % this.concurrency || 0) === rateLimitKey)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// connect the shard
|
||||
shard.connect()
|
||||
this.buckets.set(rateLimitKey, Date.now())
|
||||
|
||||
// remove the shard from the queue
|
||||
const index = this.connectQueue.findIndex((s) => s.id === shard.id)
|
||||
this.connectQueue.splice(index, 1)
|
||||
}
|
||||
|
||||
// set the next timeout if we have more shards to connect
|
||||
if (!this.connectTimeout && this.connectQueue.length > 0) {
|
||||
this.connectTimeout = setTimeout(() => {
|
||||
this.connectTimeout = null
|
||||
this.tryConnect()
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
|
||||
_readyPacketCB(shardID: number) {
|
||||
const rateLimitKey = shardID % this.concurrency || 0
|
||||
this.buckets.set(rateLimitKey, Date.now())
|
||||
|
||||
this.tryConnect()
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `[ShardManager ${this.size}]`
|
||||
}
|
||||
|
||||
toJSON(props = []) {
|
||||
return Base.prototype.toJSON.call(this, ['buckets', 'connectQueue', 'connectTimeout', 'options', ...props])
|
||||
}
|
||||
}
|
||||
|
||||
export default ShardManager
|
||||
48
packages/client/src/index.ts
Normal file
48
packages/client/src/index.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import Client from './Client.js'
|
||||
export default Client
|
||||
|
||||
export * from './Base.js'
|
||||
export * from './Client.js'
|
||||
export * from './Collection.js'
|
||||
export * from './Endpoints.js'
|
||||
export * from './gateway/Shard.js'
|
||||
export * from './gateway/ShardManager.js'
|
||||
export * from './Structures/channels/Category.js'
|
||||
export * from './Structures/channels/Channel.js'
|
||||
export * from './Structures/channels/Guild.js'
|
||||
export * from './Structures/channels/News.js'
|
||||
export * from './Structures/channels/Private.js'
|
||||
export * from './Structures/channels/Stage.js'
|
||||
export * from './Structures/channels/Text.js'
|
||||
export * from './Structures/channels/TextVoice.js'
|
||||
export * from './Structures/channels/threads/Member.js'
|
||||
export * from './Structures/channels/threads/NewsThread.js'
|
||||
export * from './Structures/channels/threads/PrivateThread.js'
|
||||
export * from './Structures/channels/threads/PublicThread.js'
|
||||
export * from './Structures/channels/threads/Thread.js'
|
||||
export * from './Structures/channels/Voice.js'
|
||||
export * from './Structures/guilds/AuditLogEntry.js'
|
||||
export * from './Structures/guilds/Guild.js'
|
||||
export * from './Structures/guilds/Integration.js'
|
||||
export * from './Structures/guilds/Member.js'
|
||||
export * from './Structures/guilds/Preview.js'
|
||||
export * from './Structures/guilds/Role.js'
|
||||
export * from './Structures/guilds/StageInstance.js'
|
||||
export * from './Structures/guilds/Template.js'
|
||||
export * from './Structures/guilds/Unavailable.js'
|
||||
export * from './Structures/guilds/VoiceState.js'
|
||||
export * from './Structures/interactions/Autocomplete.js'
|
||||
export * from './Structures/interactions/Command.js'
|
||||
export * from './Structures/interactions/Component.js'
|
||||
export * from './Structures/interactions/Interaction.js'
|
||||
export * from './Structures/interactions/Ping.js'
|
||||
export * from './Structures/interactions/Unknown.js'
|
||||
export * from './Structures/Invite.js'
|
||||
export * from './Structures/Message.js'
|
||||
export * from './Structures/Permission.js'
|
||||
export * from './Structures/PermissionOverwrite.js'
|
||||
export * from './Structures/users/Extended.js'
|
||||
export * from './Structures/users/User.js'
|
||||
export * from './typings.js'
|
||||
export * from './utils/BrowserWebSocket.js'
|
||||
export * from './utils/Bucket.js'
|
||||
3851
packages/client/src/typings.ts
Normal file
3851
packages/client/src/typings.ts
Normal file
File diff suppressed because it is too large
Load Diff
82
packages/client/src/utils/BrowserWebSocket.ts
Normal file
82
packages/client/src/utils/BrowserWebSocket.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
let EventEmitter
|
||||
try {
|
||||
EventEmitter = require('eventemitter3')
|
||||
} catch (err) {
|
||||
EventEmitter = require('events').EventEmitter
|
||||
}
|
||||
|
||||
class BrowserWebSocketError extends Error {
|
||||
static CONNECTING: 0
|
||||
static OPEN: 1
|
||||
static CLOSING: 2
|
||||
static CLOSED: 3
|
||||
|
||||
readyState: number = 0
|
||||
event: Event
|
||||
|
||||
constructor(message: string | undefined, event: Event) {
|
||||
super(message)
|
||||
|
||||
this.event = event
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a browser's websocket usable by Eris
|
||||
* @extends EventEmitter
|
||||
* @prop {String} url The URL to connect to
|
||||
*/
|
||||
class BrowserWebSocket extends EventEmitter {
|
||||
_ws: WebSocket
|
||||
|
||||
constructor(url: string) {
|
||||
super()
|
||||
|
||||
if (typeof window === 'undefined') {
|
||||
throw new Error('BrowserWebSocket cannot be used outside of a browser environment')
|
||||
}
|
||||
|
||||
this._ws = new window.WebSocket(url)
|
||||
this._ws.onopen = () => this.emit('open')
|
||||
this._ws.onmessage = this._onMessage.bind(this)
|
||||
this._ws.onerror = (event) => this.emit('error', new BrowserWebSocketError('Unknown error', event))
|
||||
this._ws.onclose = (event) => this.emit('close', event.code, event.reason)
|
||||
}
|
||||
|
||||
get readyState() {
|
||||
return this._ws.readyState
|
||||
}
|
||||
|
||||
close(code?: number, reason?: string) {
|
||||
return this._ws.close(code, reason)
|
||||
}
|
||||
|
||||
removeEventListener(type: string | symbol, listener: (...args: any[]) => void): this {
|
||||
return this.removeListener(type, listener)
|
||||
}
|
||||
|
||||
send(data: string | ArrayBufferLike | Blob | ArrayBufferView) {
|
||||
return this._ws.send(data)
|
||||
}
|
||||
|
||||
terminate() {
|
||||
return this._ws.close()
|
||||
}
|
||||
|
||||
async _onMessage(event: MessageEvent<any>) {
|
||||
if (event.data instanceof window.Blob) {
|
||||
this.emit('message', await event.data.arrayBuffer())
|
||||
} else {
|
||||
this.emit('message', event.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BrowserWebSocket.CONNECTING = 0
|
||||
BrowserWebSocket.OPEN = 1
|
||||
BrowserWebSocket.CLOSING = 2
|
||||
BrowserWebSocket.CLOSED = 3
|
||||
|
||||
export default BrowserWebSocket
|
||||
101
packages/client/src/utils/Bucket.ts
Normal file
101
packages/client/src/utils/Bucket.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
interface BucketOptions {
|
||||
latencyRef?: { latency: number }
|
||||
reservedTokens?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle ratelimiting something
|
||||
* @prop {Number} interval How long (in ms) to wait between clearing used tokens
|
||||
* @prop {Number} lastReset Timestamp of last token clearing
|
||||
* @prop {Number} lastSend Timestamp of last token consumption
|
||||
* @prop {Number} tokenLimit The max number tokens the bucket can consume per interval
|
||||
* @prop {Number} tokens How many tokens the bucket has consumed in this interval
|
||||
*/
|
||||
class Bucket {
|
||||
interval: number
|
||||
latencyRef: { latency: number }
|
||||
lastReset: number
|
||||
lastSend: number
|
||||
tokenLimit: number
|
||||
tokens: number
|
||||
reservedTokens: number
|
||||
_queue: Array<{ func: Function; priority: boolean }>
|
||||
timeout: NodeJS.Timeout | null = null
|
||||
|
||||
/**
|
||||
* Construct a Bucket
|
||||
* @arg {Number} tokenLimit The max number of tokens the bucket can consume per interval
|
||||
* @arg {Number} interval How long (in ms) to wait between clearing used tokens
|
||||
* @arg {Object} [options] Optional parameters
|
||||
* @arg {Object} options.latencyRef A latency reference object
|
||||
* @arg {Number} options.latencyRef.latency Interval between consuming tokens
|
||||
* @arg {Number} options.reservedTokens How many tokens to reserve for priority operations
|
||||
*/
|
||||
constructor(tokenLimit: number, interval: number, options: BucketOptions = {} as BucketOptions) {
|
||||
this.tokenLimit = tokenLimit
|
||||
this.interval = interval
|
||||
this.latencyRef = options.latencyRef ?? { latency: 0 }
|
||||
this.lastReset = this.tokens = this.lastSend = 0
|
||||
this.reservedTokens = options.reservedTokens ?? 0
|
||||
this._queue = []
|
||||
}
|
||||
|
||||
check() {
|
||||
if (this.timeout ?? this._queue.length === 0) {
|
||||
return
|
||||
}
|
||||
if (this.lastReset + this.interval + this.tokenLimit * this.latencyRef.latency < Date.now()) {
|
||||
this.lastReset = Date.now()
|
||||
this.tokens = Math.max(0, this.tokens - this.tokenLimit)
|
||||
}
|
||||
|
||||
let val
|
||||
let tokensAvailable = this.tokens < this.tokenLimit
|
||||
let unreservedTokensAvailable = this.tokens < this.tokenLimit - this.reservedTokens
|
||||
while (this._queue.length > 0 && (unreservedTokensAvailable || (tokensAvailable && this._queue[0].priority))) {
|
||||
this.tokens++
|
||||
tokensAvailable = this.tokens < this.tokenLimit
|
||||
unreservedTokensAvailable = this.tokens < this.tokenLimit - this.reservedTokens
|
||||
const item = this._queue.shift()
|
||||
val = this.latencyRef.latency - Date.now() + this.lastSend
|
||||
if (this.latencyRef.latency === 0 || val <= 0) {
|
||||
item!.func()
|
||||
this.lastSend = Date.now()
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
item!.func()
|
||||
}, val)
|
||||
this.lastSend = Date.now() + val
|
||||
}
|
||||
}
|
||||
|
||||
if (this._queue.length > 0 && !this.timeout) {
|
||||
this.timeout = setTimeout(
|
||||
() => {
|
||||
this.timeout = null
|
||||
this.check()
|
||||
},
|
||||
this.tokens < this.tokenLimit
|
||||
? this.latencyRef.latency
|
||||
: Math.max(0, this.lastReset + this.interval + this.tokenLimit * this.latencyRef.latency - Date.now()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue something in the Bucket
|
||||
* @arg {Function} func A callback to call when a token can be consumed
|
||||
* @arg {Boolean} [priority=false] Whether or not the callback should use reserved tokens
|
||||
*/
|
||||
queue(func: () => void, priority = false) {
|
||||
if (priority) {
|
||||
this._queue.unshift({ func, priority })
|
||||
} else {
|
||||
this._queue.push({ func, priority })
|
||||
}
|
||||
this.check()
|
||||
}
|
||||
}
|
||||
|
||||
export default Bucket
|
||||
1
packages/client/tests/empty.spec.ts
Normal file
1
packages/client/tests/empty.spec.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { }
|
||||
16
packages/client/tsconfig.json
Normal file
16
packages/client/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "tsconfig/base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*.ts",
|
||||
"./src/**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
11
packages/client/tsconfig.test.json
Normal file
11
packages/client/tsconfig.test.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "tsconfig/test.json",
|
||||
"include": [
|
||||
"tests",
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"src"
|
||||
]
|
||||
}
|
||||
@@ -58,7 +58,7 @@ import type {
|
||||
*/
|
||||
export function createBot(options: CreateBotOptions): Bot {
|
||||
if (!options.rest) options.rest = { token: options.token }
|
||||
if (!options.gateway)
|
||||
if (!options.gateway) {
|
||||
options.gateway = {
|
||||
token: options.token,
|
||||
events: {
|
||||
@@ -79,6 +79,7 @@ export function createBot(options: CreateBotOptions): Bot {
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
options.rest.token = options.token
|
||||
options.gateway.token = options.token
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -500,3 +500,19 @@ export interface ModifyGuildEmoji extends WithReason {
|
||||
/** Roles allowed to use this emoji */
|
||||
roles?: BigString[] | null
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/topics/gateway#request-guild-members */
|
||||
export interface RequestGuildMembers {
|
||||
/** id of the guild to get members for */
|
||||
guildId: BigString
|
||||
/** String that username starts with, or an empty string to return all members */
|
||||
query?: string
|
||||
/** Maximum number of members to send matching the query; a limit of 0 can be used with an empty string query to return all members */
|
||||
limit: number
|
||||
/** Used to specify if we want the presences of the matched members */
|
||||
presences?: boolean
|
||||
/** Used to specify which users you wish to fetch */
|
||||
userIds?: BigString[]
|
||||
/** Nonce to identify the Guild Members Chunk response */
|
||||
nonce?: string
|
||||
}
|
||||
|
||||
@@ -133,34 +133,28 @@ export enum ButtonStyles {
|
||||
Link,
|
||||
}
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/channel#allowed-mentions-object-allowed-mention-types */
|
||||
// export enum AllowedMentionsTypes {
|
||||
// /** Controls role mentions */
|
||||
// RoleMentions = 'roles',
|
||||
// /** Controls user mentions */
|
||||
// UserMentions = 'users',
|
||||
// /** Controls @everyone and @here mentions */
|
||||
// EveryoneMentions = 'everyone',
|
||||
// }
|
||||
/** https://discord.com/developers/docs/resources/channel#allowed-mentions-object-allowed-mention-types */
|
||||
export enum AllowedMentionsTypes {
|
||||
/** Controls role mentions */
|
||||
RoleMentions = 'roles',
|
||||
/** Controls user mentions */
|
||||
UserMentions = 'users',
|
||||
/** Controls @everyone and @here mentions */
|
||||
EveryoneMentions = 'everyone',
|
||||
}
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/webhook#webhook-object-webhook-types */
|
||||
// export enum WebhookTypes {
|
||||
// /** Incoming Webhooks can post messages to channels with a generated token */
|
||||
// Incoming = 1,
|
||||
// /** Channel Follower Webhooks are internal webhooks used with Channel Following to post new messages into channels */
|
||||
// ChannelFollower,
|
||||
// /** Application webhooks are webhooks used with Interactions */
|
||||
// Application,
|
||||
// }
|
||||
/** https://discord.com/developers/docs/resources/webhook#webhook-object-webhook-types */
|
||||
export enum WebhookTypes {
|
||||
/** Incoming Webhooks can post messages to channels with a generated token */
|
||||
Incoming = 1,
|
||||
/** Channel Follower Webhooks are internal webhooks used with Channel Following to post new messages into channels */
|
||||
ChannelFollower,
|
||||
/** Application webhooks are webhooks used with Interactions */
|
||||
Application,
|
||||
}
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/channel#embed-object-embed-types */
|
||||
// export type EmbedTypes =
|
||||
// | 'rich'
|
||||
// | 'image'
|
||||
// | 'video'
|
||||
// | 'gifv'
|
||||
// | 'article'
|
||||
// | 'link'
|
||||
/** https://discord.com/developers/docs/resources/channel#embed-object-embed-types */
|
||||
export type EmbedTypes = 'rich' | 'image' | 'video' | 'gifv' | 'article' | 'link'
|
||||
|
||||
/** https://discord.com/developers/docs/resources/guild#guild-object-default-message-notification-level */
|
||||
export enum DefaultMessageNotificationLevels {
|
||||
@@ -374,13 +368,13 @@ export enum MessageTypes {
|
||||
AutoModerationAction,
|
||||
}
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/channel#message-object-message-activity-types */
|
||||
// export enum MessageActivityTypes {
|
||||
// Join = 1,
|
||||
// Spectate,
|
||||
// Listen,
|
||||
// JoinRequest,
|
||||
// }
|
||||
/** https://discord.com/developers/docs/resources/channel#message-object-message-activity-types */
|
||||
export enum MessageActivityTypes {
|
||||
Join = 1,
|
||||
Spectate,
|
||||
Listen,
|
||||
JoinRequest,
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/sticker#sticker-object-sticker-types */
|
||||
export enum StickerTypes {
|
||||
@@ -421,113 +415,113 @@ export enum ApplicationCommandOptionTypes {
|
||||
Attachment,
|
||||
}
|
||||
|
||||
// /** https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events */
|
||||
// export enum AuditLogEvents {
|
||||
// /** Server settings were updated */
|
||||
// GuildUpdate = 1,
|
||||
// /** Channel was created */
|
||||
// ChannelCreate = 10,
|
||||
// /** Channel settings were updated */
|
||||
// ChannelUpdate,
|
||||
// /** Channel was deleted */
|
||||
// ChannelDelete,
|
||||
// /** Permission overwrite was added to a channel */
|
||||
// ChannelOverwriteCreate,
|
||||
// /** Permission overwrite was updated for a channel */
|
||||
// ChannelOverwriteUpdate,
|
||||
// /** Permission overwrite was deleted from a channel */
|
||||
// ChannelOverwriteDelete,
|
||||
// /** Member was removed from server */
|
||||
// MemberKick = 20,
|
||||
// /** Members were pruned from server */
|
||||
// MemberPrune,
|
||||
// /** Member was banned from server */
|
||||
// MemberBanAdd,
|
||||
// /** Server ban was lifted for a member */
|
||||
// MemberBanRemove,
|
||||
// /** Member was updated in server */
|
||||
// MemberUpdate,
|
||||
// /** Member was added or removed from a role */
|
||||
// MemberRoleUpdate,
|
||||
// /** Member was moved to a different voice channel */
|
||||
// MemberMove,
|
||||
// /** Member was disconnected from a voice channel */
|
||||
// MemberDisconnect,
|
||||
// /** Bot user was added to server */
|
||||
// BotAdd,
|
||||
// /** Role was created */
|
||||
// RoleCreate = 30,
|
||||
// /** Role was edited */
|
||||
// RoleUpdate,
|
||||
// /** Role was deleted */
|
||||
// RoleDelete,
|
||||
// /** Server invite was created */
|
||||
// InviteCreate = 40,
|
||||
// /** Server invite was updated */
|
||||
// InviteUpdate,
|
||||
// /** Server invite was deleted */
|
||||
// InviteDelete,
|
||||
// /** Webhook was created */
|
||||
// WebhookCreate = 50,
|
||||
// /** Webhook properties or channel were updated */
|
||||
// WebhookUpdate,
|
||||
// /** Webhook was deleted */
|
||||
// WebhookDelete,
|
||||
// /** Emoji was created */
|
||||
// EmojiCreate = 60,
|
||||
// /** Emoji name was updated */
|
||||
// EmojiUpdate,
|
||||
// /** Emoji was deleted */
|
||||
// EmojiDelete,
|
||||
// /** Single message was deleted */
|
||||
// MessageDelete = 72,
|
||||
// /** Multiple messages were deleted */
|
||||
// MessageBulkDelete,
|
||||
// /** Messaged was pinned to a channel */
|
||||
// MessagePin,
|
||||
// /** Message was unpinned from a channel */
|
||||
// MessageUnpin,
|
||||
// /** App was added to server */
|
||||
// IntegrationCreate = 80,
|
||||
// /** App was updated (as an example, its scopes were updated) */
|
||||
// IntegrationUpdate,
|
||||
// /** App was removed from server */
|
||||
// IntegrationDelete,
|
||||
// /** Stage instance was created (stage channel becomes live) */
|
||||
// StageInstanceCreate,
|
||||
// /** Stage instace details were updated */
|
||||
// StageInstanceUpdate,
|
||||
// /** Stage instance was deleted (stage channel no longer live) */
|
||||
// StageInstanceDelete,
|
||||
// /** Sticker was created */
|
||||
// StickerCreate = 90,
|
||||
// /** Sticker details were updated */
|
||||
// StickerUpdate,
|
||||
// /** Sticker was deleted */
|
||||
// StickerDelete,
|
||||
// /** Event was created */
|
||||
// GuildScheduledEventCreate = 100,
|
||||
// /** Event was updated */
|
||||
// GuildScheduledEventUpdate,
|
||||
// /** Event was cancelled */
|
||||
// GuildScheduledEventDelete,
|
||||
// /** Thread was created in a channel */
|
||||
// ThreadCreate = 110,
|
||||
// /** Thread was updated */
|
||||
// ThreadUpdate,
|
||||
// /** Thread was deleted */
|
||||
// ThreadDelete,
|
||||
// /** Permissions were updated for a command */
|
||||
// ApplicationCommandPermissionUpdate = 121,
|
||||
// /** Auto moderation rule was created */
|
||||
// AutoModerationRuleCreate = 140,
|
||||
// /** Auto moderation rule was updated */
|
||||
// AutoModerationRuleUpdate,
|
||||
// /** Auto moderation rule was deleted */
|
||||
// AutoModerationRuleDelete,
|
||||
// /** Message was blocked by AutoMod according to a rule. */
|
||||
// AutoModerationBlockMessage,
|
||||
// }
|
||||
/** https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events */
|
||||
export enum AuditLogEvents {
|
||||
/** Server settings were updated */
|
||||
GuildUpdate = 1,
|
||||
/** Channel was created */
|
||||
ChannelCreate = 10,
|
||||
/** Channel settings were updated */
|
||||
ChannelUpdate,
|
||||
/** Channel was deleted */
|
||||
ChannelDelete,
|
||||
/** Permission overwrite was added to a channel */
|
||||
ChannelOverwriteCreate,
|
||||
/** Permission overwrite was updated for a channel */
|
||||
ChannelOverwriteUpdate,
|
||||
/** Permission overwrite was deleted from a channel */
|
||||
ChannelOverwriteDelete,
|
||||
/** Member was removed from server */
|
||||
MemberKick = 20,
|
||||
/** Members were pruned from server */
|
||||
MemberPrune,
|
||||
/** Member was banned from server */
|
||||
MemberBanAdd,
|
||||
/** Server ban was lifted for a member */
|
||||
MemberBanRemove,
|
||||
/** Member was updated in server */
|
||||
MemberUpdate,
|
||||
/** Member was added or removed from a role */
|
||||
MemberRoleUpdate,
|
||||
/** Member was moved to a different voice channel */
|
||||
MemberMove,
|
||||
/** Member was disconnected from a voice channel */
|
||||
MemberDisconnect,
|
||||
/** Bot user was added to server */
|
||||
BotAdd,
|
||||
/** Role was created */
|
||||
RoleCreate = 30,
|
||||
/** Role was edited */
|
||||
RoleUpdate,
|
||||
/** Role was deleted */
|
||||
RoleDelete,
|
||||
/** Server invite was created */
|
||||
InviteCreate = 40,
|
||||
/** Server invite was updated */
|
||||
InviteUpdate,
|
||||
/** Server invite was deleted */
|
||||
InviteDelete,
|
||||
/** Webhook was created */
|
||||
WebhookCreate = 50,
|
||||
/** Webhook properties or channel were updated */
|
||||
WebhookUpdate,
|
||||
/** Webhook was deleted */
|
||||
WebhookDelete,
|
||||
/** Emoji was created */
|
||||
EmojiCreate = 60,
|
||||
/** Emoji name was updated */
|
||||
EmojiUpdate,
|
||||
/** Emoji was deleted */
|
||||
EmojiDelete,
|
||||
/** Single message was deleted */
|
||||
MessageDelete = 72,
|
||||
/** Multiple messages were deleted */
|
||||
MessageBulkDelete,
|
||||
/** Messaged was pinned to a channel */
|
||||
MessagePin,
|
||||
/** Message was unpinned from a channel */
|
||||
MessageUnpin,
|
||||
/** App was added to server */
|
||||
IntegrationCreate = 80,
|
||||
/** App was updated (as an example, its scopes were updated) */
|
||||
IntegrationUpdate,
|
||||
/** App was removed from server */
|
||||
IntegrationDelete,
|
||||
/** Stage instance was created (stage channel becomes live) */
|
||||
StageInstanceCreate,
|
||||
/** Stage instace details were updated */
|
||||
StageInstanceUpdate,
|
||||
/** Stage instance was deleted (stage channel no longer live) */
|
||||
StageInstanceDelete,
|
||||
/** Sticker was created */
|
||||
StickerCreate = 90,
|
||||
/** Sticker details were updated */
|
||||
StickerUpdate,
|
||||
/** Sticker was deleted */
|
||||
StickerDelete,
|
||||
/** Event was created */
|
||||
GuildScheduledEventCreate = 100,
|
||||
/** Event was updated */
|
||||
GuildScheduledEventUpdate,
|
||||
/** Event was cancelled */
|
||||
GuildScheduledEventDelete,
|
||||
/** Thread was created in a channel */
|
||||
ThreadCreate = 110,
|
||||
/** Thread was updated */
|
||||
ThreadUpdate,
|
||||
/** Thread was deleted */
|
||||
ThreadDelete,
|
||||
/** Permissions were updated for a command */
|
||||
ApplicationCommandPermissionUpdate = 121,
|
||||
/** Auto moderation rule was created */
|
||||
AutoModerationRuleCreate = 140,
|
||||
/** Auto moderation rule was updated */
|
||||
AutoModerationRuleUpdate,
|
||||
/** Auto moderation rule was deleted */
|
||||
AutoModerationRuleDelete,
|
||||
/** Message was blocked by AutoMod according to a rule. */
|
||||
AutoModerationBlockMessage,
|
||||
}
|
||||
|
||||
export enum ScheduledEventPrivacyLevel {
|
||||
/** the scheduled event is public and available in discovery. DISCORD DEVS DISABLED THIS! WILL ERROR IF USED! */
|
||||
@@ -583,91 +577,91 @@ export enum ApplicationCommandPermissionTypes {
|
||||
// Embedded = 1 << 8,
|
||||
// }
|
||||
|
||||
// /** https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags */
|
||||
// export enum BitwisePermissionFlags {
|
||||
// /** Allows creation of instant invites */
|
||||
// CREATE_INSTANT_INVITE = 0x0000000000000001,
|
||||
// /** Allows kicking members */
|
||||
// KICK_MEMBERS = 0x0000000000000002,
|
||||
// /** Allows banning members */
|
||||
// BAN_MEMBERS = 0x0000000000000004,
|
||||
// /** Allows all permissions and bypasses channel permission overwrites */
|
||||
// ADMINISTRATOR = 0x0000000000000008,
|
||||
// /** Allows management and editing of channels */
|
||||
// MANAGE_CHANNELS = 0x0000000000000010,
|
||||
// /** Allows management and editing of the guild */
|
||||
// MANAGE_GUILD = 0x0000000000000020,
|
||||
// /** Allows for the addition of reactions to messages */
|
||||
// ADD_REACTIONS = 0x0000000000000040,
|
||||
// /** Allows for viewing of audit logs */
|
||||
// VIEW_AUDIT_LOG = 0x0000000000000080,
|
||||
// /** Allows for using priority speaker in a voice channel */
|
||||
// PRIORITY_SPEAKER = 0x0000000000000100,
|
||||
// /** Allows the user to go live */
|
||||
// STREAM = 0x0000000000000200,
|
||||
// /** Allows guild members to view a channel, which includes reading messages in text channels and joining voice channels */
|
||||
// VIEW_CHANNEL = 0x0000000000000400,
|
||||
// /** Allows for sending messages in a channel. (does not allow sending messages in threads) */
|
||||
// SEND_MESSAGES = 0x0000000000000800,
|
||||
// /** Allows for sending of /tts messages */
|
||||
// SEND_TTS_MESSAGES = 0x0000000000001000,
|
||||
// /** Allows for deletion of other users messages */
|
||||
// MANAGE_MESSAGES = 0x0000000000002000,
|
||||
// /** Links sent by users with this permission will be auto-embedded */
|
||||
// EMBED_LINKS = 0x0000000000004000,
|
||||
// /** Allows for uploading images and files */
|
||||
// ATTACH_FILES = 0x0000000000008000,
|
||||
// /** Allows for reading of message history */
|
||||
// READ_MESSAGE_HISTORY = 0x0000000000010000,
|
||||
// /** Allows for using the @everyone tag to notify all users in a channel, and the @here tag to notify all online users in a channel */
|
||||
// MENTION_EVERYONE = 0x0000000000020000,
|
||||
// /** Allows the usage of custom emojis from other servers */
|
||||
// USE_EXTERNAL_EMOJIS = 0x0000000000040000,
|
||||
// /** Allows for viewing guild insights */
|
||||
// VIEW_GUILD_INSIGHTS = 0x0000000000080000,
|
||||
// /** Allows for joining of a voice channel */
|
||||
// CONNECT = 0x0000000000100000,
|
||||
// /** Allows for speaking in a voice channel */
|
||||
// SPEAK = 0x0000000000200000,
|
||||
// /** Allows for muting members in a voice channel */
|
||||
// MUTE_MEMBERS = 0x0000000000400000,
|
||||
// /** Allows for deafening of members in a voice channel */
|
||||
// DEAFEN_MEMBERS = 0x0000000000800000,
|
||||
// /** Allows for moving of members between voice channels */
|
||||
// MOVE_MEMBERS = 0x0000000001000000,
|
||||
// /** Allows for using voice-activity-detection in a voice channel */
|
||||
// USE_VAD = 0x0000000002000000,
|
||||
// /** Allows for modification of own nickname */
|
||||
// CHANGE_NICKNAME = 0x0000000004000000,
|
||||
// /** Allows for modification of other users nicknames */
|
||||
// MANAGE_NICKNAMES = 0x0000000008000000,
|
||||
// /** Allows management and editing of roles */
|
||||
// MANAGE_ROLES = 0x0000000010000000,
|
||||
// /** Allows management and editing of webhooks */
|
||||
// MANAGE_WEBHOOKS = 0x0000000020000000,
|
||||
// /** Allows management and editing of emojis and stickers */
|
||||
// MANAGE_EMOJIS_AND_STICKERS = 0x0000000040000000,
|
||||
// /** Allows members to use application commands in text channels */
|
||||
// USE_SLASH_COMMANDS = 0x0000000080000000,
|
||||
// /** Allows for requesting to speak in stage channels. */
|
||||
// REQUEST_TO_SPEAK = 0x0000000100000000,
|
||||
// /** Allows for creating, editing, and deleting scheduled events */
|
||||
// MANAGE_EVENTS = 0x0000000200000000,
|
||||
// /** Allows for deleting and archiving threads, and viewing all private threads */
|
||||
// MANAGE_THREADS = 0x0000000400000000,
|
||||
// /** Allows for creating public and announcement threads */
|
||||
// CREATE_PUBLIC_THREADS = 0x0000000800000000,
|
||||
// /** Allows for creating private threads */
|
||||
// CREATE_PRIVATE_THREADS = 0x0000001000000000,
|
||||
// /** Allows the usage of custom stickers from other servers */
|
||||
// USE_EXTERNAL_STICKERS = 0x0000002000000000,
|
||||
// /** Allows for sending messages in threads */
|
||||
// SEND_MESSAGES_IN_THREADS = 0x0000004000000000,
|
||||
// /** Allows for launching activities (applications with the `EMBEDDED` flag) in a voice channel. */
|
||||
// USE_EMBEDDED_ACTIVITIES = 0x0000008000000000,
|
||||
// /** Allows for timing out users to prevent them from sending or reacting to messages in chat and threads, and from speaking in voice and stage channels */
|
||||
// MODERATE_MEMBERS = 0x0000010000000000,
|
||||
// }
|
||||
/** https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags */
|
||||
export enum BitwisePermissionFlags {
|
||||
/** Allows creation of instant invites */
|
||||
CREATE_INSTANT_INVITE = 0x0000000000000001,
|
||||
/** Allows kicking members */
|
||||
KICK_MEMBERS = 0x0000000000000002,
|
||||
/** Allows banning members */
|
||||
BAN_MEMBERS = 0x0000000000000004,
|
||||
/** Allows all permissions and bypasses channel permission overwrites */
|
||||
ADMINISTRATOR = 0x0000000000000008,
|
||||
/** Allows management and editing of channels */
|
||||
MANAGE_CHANNELS = 0x0000000000000010,
|
||||
/** Allows management and editing of the guild */
|
||||
MANAGE_GUILD = 0x0000000000000020,
|
||||
/** Allows for the addition of reactions to messages */
|
||||
ADD_REACTIONS = 0x0000000000000040,
|
||||
/** Allows for viewing of audit logs */
|
||||
VIEW_AUDIT_LOG = 0x0000000000000080,
|
||||
/** Allows for using priority speaker in a voice channel */
|
||||
PRIORITY_SPEAKER = 0x0000000000000100,
|
||||
/** Allows the user to go live */
|
||||
STREAM = 0x0000000000000200,
|
||||
/** Allows guild members to view a channel, which includes reading messages in text channels and joining voice channels */
|
||||
VIEW_CHANNEL = 0x0000000000000400,
|
||||
/** Allows for sending messages in a channel. (does not allow sending messages in threads) */
|
||||
SEND_MESSAGES = 0x0000000000000800,
|
||||
/** Allows for sending of /tts messages */
|
||||
SEND_TTS_MESSAGES = 0x0000000000001000,
|
||||
/** Allows for deletion of other users messages */
|
||||
MANAGE_MESSAGES = 0x0000000000002000,
|
||||
/** Links sent by users with this permission will be auto-embedded */
|
||||
EMBED_LINKS = 0x0000000000004000,
|
||||
/** Allows for uploading images and files */
|
||||
ATTACH_FILES = 0x0000000000008000,
|
||||
/** Allows for reading of message history */
|
||||
READ_MESSAGE_HISTORY = 0x0000000000010000,
|
||||
/** Allows for using the @everyone tag to notify all users in a channel, and the @here tag to notify all online users in a channel */
|
||||
MENTION_EVERYONE = 0x0000000000020000,
|
||||
/** Allows the usage of custom emojis from other servers */
|
||||
USE_EXTERNAL_EMOJIS = 0x0000000000040000,
|
||||
/** Allows for viewing guild insights */
|
||||
VIEW_GUILD_INSIGHTS = 0x0000000000080000,
|
||||
/** Allows for joining of a voice channel */
|
||||
CONNECT = 0x0000000000100000,
|
||||
/** Allows for speaking in a voice channel */
|
||||
SPEAK = 0x0000000000200000,
|
||||
/** Allows for muting members in a voice channel */
|
||||
MUTE_MEMBERS = 0x0000000000400000,
|
||||
/** Allows for deafening of members in a voice channel */
|
||||
DEAFEN_MEMBERS = 0x0000000000800000,
|
||||
/** Allows for moving of members between voice channels */
|
||||
MOVE_MEMBERS = 0x0000000001000000,
|
||||
/** Allows for using voice-activity-detection in a voice channel */
|
||||
USE_VAD = 0x0000000002000000,
|
||||
/** Allows for modification of own nickname */
|
||||
CHANGE_NICKNAME = 0x0000000004000000,
|
||||
/** Allows for modification of other users nicknames */
|
||||
MANAGE_NICKNAMES = 0x0000000008000000,
|
||||
/** Allows management and editing of roles */
|
||||
MANAGE_ROLES = 0x0000000010000000,
|
||||
/** Allows management and editing of webhooks */
|
||||
MANAGE_WEBHOOKS = 0x0000000020000000,
|
||||
/** Allows management and editing of emojis and stickers */
|
||||
MANAGE_EMOJIS_AND_STICKERS = 0x0000000040000000,
|
||||
/** Allows members to use application commands in text channels */
|
||||
USE_SLASH_COMMANDS = 0x0000000080000000,
|
||||
/** Allows for requesting to speak in stage channels. */
|
||||
REQUEST_TO_SPEAK = 0x0000000100000000,
|
||||
/** Allows for creating, editing, and deleting scheduled events */
|
||||
MANAGE_EVENTS = 0x0000000200000000,
|
||||
/** Allows for deleting and archiving threads, and viewing all private threads */
|
||||
MANAGE_THREADS = 0x0000000400000000,
|
||||
/** Allows for creating public and announcement threads */
|
||||
CREATE_PUBLIC_THREADS = 0x0000000800000000,
|
||||
/** Allows for creating private threads */
|
||||
CREATE_PRIVATE_THREADS = 0x0000001000000000,
|
||||
/** Allows the usage of custom stickers from other servers */
|
||||
USE_EXTERNAL_STICKERS = 0x0000002000000000,
|
||||
/** Allows for sending messages in threads */
|
||||
SEND_MESSAGES_IN_THREADS = 0x0000004000000000,
|
||||
/** Allows for launching activities (applications with the `EMBEDDED` flag) in a voice channel. */
|
||||
USE_EMBEDDED_ACTIVITIES = 0x0000008000000000,
|
||||
/** Allows for timing out users to prevent them from sending or reacting to messages in chat and threads, and from speaking in voice and stage channels */
|
||||
MODERATE_MEMBERS = 0x0000010000000000,
|
||||
}
|
||||
|
||||
// export type PermissionStrings = keyof typeof BitwisePermissionFlags
|
||||
|
||||
@@ -1108,152 +1102,152 @@ export type GatewayDispatchEventNames =
|
||||
|
||||
export type GatewayEventNames = GatewayDispatchEventNames | 'READY' | 'RESUMED'
|
||||
|
||||
// /** https://discord.com/developers/docs/topics/gateway#list-of-intents */
|
||||
// export enum GatewayIntents {
|
||||
// /**
|
||||
// * - GUILD_CREATE
|
||||
// * - GUILD_DELETE
|
||||
// * - GUILD_ROLE_CREATE
|
||||
// * - GUILD_ROLE_UPDATE
|
||||
// * - GUILD_ROLE_DELETE
|
||||
// * - CHANNEL_CREATE
|
||||
// * - CHANNEL_UPDATE
|
||||
// * - CHANNEL_DELETE
|
||||
// * - CHANNEL_PINS_UPDATE
|
||||
// * - THREAD_CREATE
|
||||
// * - THREAD_UPDATE
|
||||
// * - THREAD_DELETE
|
||||
// * - THREAD_LIST_SYNC
|
||||
// * - THREAD_MEMBER_UPDATE
|
||||
// * - THREAD_MEMBERS_UPDATE
|
||||
// * - STAGE_INSTANCE_CREATE
|
||||
// * - STAGE_INSTANCE_UPDATE
|
||||
// * - STAGE_INSTANCE_DELETE
|
||||
// */
|
||||
// Guilds = 1 << 0,
|
||||
// /**
|
||||
// * - GUILD_MEMBER_ADD
|
||||
// * - GUILD_MEMBER_UPDATE
|
||||
// * - GUILD_MEMBER_REMOVE
|
||||
// */
|
||||
// GuildMembers = 1 << 1,
|
||||
// /**
|
||||
// * - GUILD_BAN_ADD
|
||||
// * - GUILD_BAN_REMOVE
|
||||
// */
|
||||
// GuildBans = 1 << 2,
|
||||
// /**
|
||||
// * - GUILD_EMOJIS_UPDATE
|
||||
// */
|
||||
// GuildEmojis = 1 << 3,
|
||||
// /**
|
||||
// * - GUILD_INTEGRATIONS_UPDATE
|
||||
// * - INTEGRATION_CREATE
|
||||
// * - INTEGRATION_UPDATE
|
||||
// * - INTEGRATION_DELETE
|
||||
// */
|
||||
// GuildIntegrations = 1 << 4,
|
||||
// /** Enables the following events:
|
||||
// * - WEBHOOKS_UPDATE
|
||||
// */
|
||||
// GuildWebhooks = 1 << 5,
|
||||
// /**
|
||||
// * - INVITE_CREATE
|
||||
// * - INVITE_DELETE
|
||||
// */
|
||||
// GuildInvites = 1 << 6,
|
||||
// /**
|
||||
// * - VOICE_STATE_UPDATE
|
||||
// */
|
||||
// GuildVoiceStates = 1 << 7,
|
||||
// /**
|
||||
// * - PRESENCE_UPDATE
|
||||
// */
|
||||
// GuildPresences = 1 << 8,
|
||||
// /**
|
||||
// * - MESSAGE_CREATE
|
||||
// * - MESSAGE_UPDATE
|
||||
// * - MESSAGE_DELETE
|
||||
// */
|
||||
// GuildMessages = 1 << 9,
|
||||
// /**
|
||||
// * - MESSAGE_REACTION_ADD
|
||||
// * - MESSAGE_REACTION_REMOVE
|
||||
// * - MESSAGE_REACTION_REMOVE_ALL
|
||||
// * - MESSAGE_REACTION_REMOVE_EMOJI
|
||||
// */
|
||||
// GuildMessageReactions = 1 << 10,
|
||||
// /**
|
||||
// * - TYPING_START
|
||||
// */
|
||||
// GuildMessageTyping = 1 << 11,
|
||||
// /**
|
||||
// * - CHANNEL_CREATE
|
||||
// * - MESSAGE_CREATE
|
||||
// * - MESSAGE_UPDATE
|
||||
// * - MESSAGE_DELETE
|
||||
// * - CHANNEL_PINS_UPDATE
|
||||
// */
|
||||
// DirectMessages = 1 << 12,
|
||||
// /**
|
||||
// * - MESSAGE_REACTION_ADD
|
||||
// * - MESSAGE_REACTION_REMOVE
|
||||
// * - MESSAGE_REACTION_REMOVE_ALL
|
||||
// * - MESSAGE_REACTION_REMOVE_EMOJI
|
||||
// */
|
||||
// DirectMessageReactions = 1 << 13,
|
||||
// /**
|
||||
// * - TYPING_START
|
||||
// */
|
||||
// DirectMessageTyping = 1 << 14,
|
||||
/** https://discord.com/developers/docs/topics/gateway#list-of-intents */
|
||||
export enum GatewayIntents {
|
||||
/**
|
||||
* - GUILD_CREATE
|
||||
* - GUILD_DELETE
|
||||
* - GUILD_ROLE_CREATE
|
||||
* - GUILD_ROLE_UPDATE
|
||||
* - GUILD_ROLE_DELETE
|
||||
* - CHANNEL_CREATE
|
||||
* - CHANNEL_UPDATE
|
||||
* - CHANNEL_DELETE
|
||||
* - CHANNEL_PINS_UPDATE
|
||||
* - THREAD_CREATE
|
||||
* - THREAD_UPDATE
|
||||
* - THREAD_DELETE
|
||||
* - THREAD_LIST_SYNC
|
||||
* - THREAD_MEMBER_UPDATE
|
||||
* - THREAD_MEMBERS_UPDATE
|
||||
* - STAGE_INSTANCE_CREATE
|
||||
* - STAGE_INSTANCE_UPDATE
|
||||
* - STAGE_INSTANCE_DELETE
|
||||
*/
|
||||
Guilds = 1 << 0,
|
||||
/**
|
||||
* - GUILD_MEMBER_ADD
|
||||
* - GUILD_MEMBER_UPDATE
|
||||
* - GUILD_MEMBER_REMOVE
|
||||
*/
|
||||
GuildMembers = 1 << 1,
|
||||
/**
|
||||
* - GUILD_BAN_ADD
|
||||
* - GUILD_BAN_REMOVE
|
||||
*/
|
||||
GuildBans = 1 << 2,
|
||||
/**
|
||||
* - GUILD_EMOJIS_UPDATE
|
||||
*/
|
||||
GuildEmojis = 1 << 3,
|
||||
/**
|
||||
* - GUILD_INTEGRATIONS_UPDATE
|
||||
* - INTEGRATION_CREATE
|
||||
* - INTEGRATION_UPDATE
|
||||
* - INTEGRATION_DELETE
|
||||
*/
|
||||
GuildIntegrations = 1 << 4,
|
||||
/** Enables the following events:
|
||||
* - WEBHOOKS_UPDATE
|
||||
*/
|
||||
GuildWebhooks = 1 << 5,
|
||||
/**
|
||||
* - INVITE_CREATE
|
||||
* - INVITE_DELETE
|
||||
*/
|
||||
GuildInvites = 1 << 6,
|
||||
/**
|
||||
* - VOICE_STATE_UPDATE
|
||||
*/
|
||||
GuildVoiceStates = 1 << 7,
|
||||
/**
|
||||
* - PRESENCE_UPDATE
|
||||
*/
|
||||
GuildPresences = 1 << 8,
|
||||
/**
|
||||
* - MESSAGE_CREATE
|
||||
* - MESSAGE_UPDATE
|
||||
* - MESSAGE_DELETE
|
||||
*/
|
||||
GuildMessages = 1 << 9,
|
||||
/**
|
||||
* - MESSAGE_REACTION_ADD
|
||||
* - MESSAGE_REACTION_REMOVE
|
||||
* - MESSAGE_REACTION_REMOVE_ALL
|
||||
* - MESSAGE_REACTION_REMOVE_EMOJI
|
||||
*/
|
||||
GuildMessageReactions = 1 << 10,
|
||||
/**
|
||||
* - TYPING_START
|
||||
*/
|
||||
GuildMessageTyping = 1 << 11,
|
||||
/**
|
||||
* - CHANNEL_CREATE
|
||||
* - MESSAGE_CREATE
|
||||
* - MESSAGE_UPDATE
|
||||
* - MESSAGE_DELETE
|
||||
* - CHANNEL_PINS_UPDATE
|
||||
*/
|
||||
DirectMessages = 1 << 12,
|
||||
/**
|
||||
* - MESSAGE_REACTION_ADD
|
||||
* - MESSAGE_REACTION_REMOVE
|
||||
* - MESSAGE_REACTION_REMOVE_ALL
|
||||
* - MESSAGE_REACTION_REMOVE_EMOJI
|
||||
*/
|
||||
DirectMessageReactions = 1 << 13,
|
||||
/**
|
||||
* - TYPING_START
|
||||
*/
|
||||
DirectMessageTyping = 1 << 14,
|
||||
|
||||
// /**
|
||||
// * This intent will add `content` values to all message objects.
|
||||
// */
|
||||
// MessageContent = 1 << 15,
|
||||
// /**
|
||||
// * - GUILD_SCHEDULED_EVENT_CREATE
|
||||
// * - GUILD_SCHEDULED_EVENT_UPDATE
|
||||
// * - GUILD_SCHEDULED_EVENT_DELETE
|
||||
// * - GUILD_SCHEDULED_EVENT_USER_ADD this is experimental and unstable.
|
||||
// * - GUILD_SCHEDULED_EVENT_USER_REMOVE this is experimental and unstable.
|
||||
// */
|
||||
// GuildScheduledEvents = 1 << 16,
|
||||
/**
|
||||
* This intent will add `content` values to all message objects.
|
||||
*/
|
||||
MessageContent = 1 << 15,
|
||||
/**
|
||||
* - GUILD_SCHEDULED_EVENT_CREATE
|
||||
* - GUILD_SCHEDULED_EVENT_UPDATE
|
||||
* - GUILD_SCHEDULED_EVENT_DELETE
|
||||
* - GUILD_SCHEDULED_EVENT_USER_ADD this is experimental and unstable.
|
||||
* - GUILD_SCHEDULED_EVENT_USER_REMOVE this is experimental and unstable.
|
||||
*/
|
||||
GuildScheduledEvents = 1 << 16,
|
||||
|
||||
// /**
|
||||
// * - AUTO_MODERATION_RULE_CREATE
|
||||
// * - AUTO_MODERATION_RULE_UPDATE
|
||||
// * - AUTO_MODERATION_RULE_DELETE
|
||||
// */
|
||||
// AutoModerationConfiguration = 1 << 20,
|
||||
// /**
|
||||
// * - AUTO_MODERATION_ACTION_EXECUTION
|
||||
// */
|
||||
// AutoModerationExecution = 1 << 21,
|
||||
// }
|
||||
/**
|
||||
* - AUTO_MODERATION_RULE_CREATE
|
||||
* - AUTO_MODERATION_RULE_UPDATE
|
||||
* - AUTO_MODERATION_RULE_DELETE
|
||||
*/
|
||||
AutoModerationConfiguration = 1 << 20,
|
||||
/**
|
||||
* - AUTO_MODERATION_ACTION_EXECUTION
|
||||
*/
|
||||
AutoModerationExecution = 1 << 21,
|
||||
}
|
||||
|
||||
// // ALIASES JUST FOR BETTER UX IN THIS CASE
|
||||
// ALIASES JUST FOR BETTER UX IN THIS CASE
|
||||
|
||||
// /** https://discord.com/developers/docs/topics/gateway#list-of-intents */
|
||||
// export const Intents = GatewayIntents
|
||||
/** https://discord.com/developers/docs/topics/gateway#list-of-intents */
|
||||
export const Intents = GatewayIntents
|
||||
|
||||
// /** https://discord.com/developers/docs/interactions/slash-commands#interaction-response-interactionresponsetype */
|
||||
// export enum InteractionResponseTypes {
|
||||
// /** ACK a `Ping` */
|
||||
// Pong = 1,
|
||||
// /** Respond to an interaction with a message */
|
||||
// ChannelMessageWithSource = 4,
|
||||
// /** ACK an interaction and edit a response later, the user sees a loading state */
|
||||
// DeferredChannelMessageWithSource = 5,
|
||||
// /** For components, ACK an interaction and edit the original message later; the user does not see a loading state */
|
||||
// DeferredUpdateMessage = 6,
|
||||
// /** For components, edit the message the component was attached to */
|
||||
// UpdateMessage = 7,
|
||||
// /** For Application Command Options, send an autocomplete result */
|
||||
// ApplicationCommandAutocompleteResult = 8,
|
||||
// /** For Command or Component interactions, send a Modal response */
|
||||
// Modal = 9,
|
||||
// }
|
||||
/** https://discord.com/developers/docs/interactions/slash-commands#interaction-response-interactionresponsetype */
|
||||
export enum InteractionResponseTypes {
|
||||
/** ACK a `Ping` */
|
||||
Pong = 1,
|
||||
/** Respond to an interaction with a message */
|
||||
ChannelMessageWithSource = 4,
|
||||
/** ACK an interaction and edit a response later, the user sees a loading state */
|
||||
DeferredChannelMessageWithSource = 5,
|
||||
/** For components, ACK an interaction and edit the original message later; the user does not see a loading state */
|
||||
DeferredUpdateMessage = 6,
|
||||
/** For components, edit the message the component was attached to */
|
||||
UpdateMessage = 7,
|
||||
/** For Application Command Options, send an autocomplete result */
|
||||
ApplicationCommandAutocompleteResult = 8,
|
||||
/** For Command or Component interactions, send a Modal response */
|
||||
Modal = 9,
|
||||
}
|
||||
|
||||
export enum SortOrderTypes {
|
||||
/** Sort forum posts by activity */
|
||||
@@ -1443,8 +1437,8 @@ export type Camelize<T> = T extends any[]
|
||||
? Array<Camelize<T[number]>>
|
||||
: T
|
||||
: T extends Record<any, any>
|
||||
? { [K in keyof T as CamelCase<K & string>]: Camelize<T[K]> }
|
||||
: T
|
||||
? { [K in keyof T as CamelCase<K & string>]: Camelize<T[K]> }
|
||||
: T
|
||||
|
||||
// /** Non object primitives */
|
||||
// export type Primitive =
|
||||
|
||||
19
packages/utils/src/hash.ts
Normal file
19
packages/utils/src/hash.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export function iconHashToBigInt (hash: string): bigint {
|
||||
// The icon is animated so it needs special handling
|
||||
if (hash.startsWith('a_')) {
|
||||
// Change the `a_` to just be `a`
|
||||
hash = `a${hash.substring(2)}`
|
||||
} else {
|
||||
// The icon is not animated but it could be that it starts with a 0 so we just put a `b` in front so nothing breaks
|
||||
hash = `b${hash}`
|
||||
}
|
||||
|
||||
return BigInt(`0x${hash}`)
|
||||
}
|
||||
|
||||
export function iconBigintToHash (icon: bigint): string {
|
||||
// Convert the bigint back to a hash
|
||||
const hash = icon.toString(16)
|
||||
// Hashes starting with a are animated and with b are not so need to handle that
|
||||
return hash.startsWith('a') ? `a_${hash.substring(1)}` : hash.substring(1)
|
||||
}
|
||||
23
packages/utils/src/token.ts
Normal file
23
packages/utils/src/token.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Buffer } from 'node:buffer'
|
||||
|
||||
/** Removes the Bot before the token. */
|
||||
export function removeTokenPrefix (
|
||||
token?: string,
|
||||
type: 'GATEWAY' | 'REST' = 'REST'
|
||||
): string {
|
||||
// If no token is provided, throw an error
|
||||
if (token === undefined) {
|
||||
throw new Error(
|
||||
`The ${type} was not given a token. Please provide a token and try again.`
|
||||
)
|
||||
}
|
||||
// If the token does not have a prefix just return token
|
||||
if (!token.startsWith('Bot ')) return token
|
||||
// Remove the prefix and return only the token.
|
||||
return token.substring(token.indexOf(' ') + 1)
|
||||
}
|
||||
|
||||
/** Get the bot id from the bot token. WARNING: Discord staff has mentioned this may not be stable forever. Use at your own risk. However, note for over 5 years this has never broken. */
|
||||
export function getBotIdFromToken (token: string): bigint {
|
||||
return BigInt(Buffer.from(token.split('.')[0], 'base64').toString())
|
||||
}
|
||||
25
yarn.lock
25
yarn.lock
@@ -21,6 +21,31 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@discordeno/client@workspace:packages/client":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@discordeno/client@workspace:packages/client"
|
||||
dependencies:
|
||||
"@discordeno/gateway": 18.0.0-alpha.1
|
||||
"@discordeno/rest": 18.0.0-alpha.1
|
||||
"@discordeno/types": 18.0.0-alpha.1
|
||||
"@discordeno/utils": 18.0.0-alpha.1
|
||||
"@swc/cli": ^0.1.57
|
||||
"@swc/core": ^1.3.21
|
||||
"@types/chai": ^4
|
||||
"@types/mocha": ^10
|
||||
"@types/node": ^18.11.9
|
||||
"@types/sinon": ^10.0.13
|
||||
c8: ^7.12.0
|
||||
chai: ^4.3.7
|
||||
eslint: ^8.0.1
|
||||
eslint-config-discordeno: "*"
|
||||
mocha: ^10.1.0
|
||||
sinon: ^15.0.0
|
||||
tsconfig: "*"
|
||||
typescript: ^4.9.3
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@discordeno/gateway@18.0.0-alpha.1, @discordeno/gateway@workspace:packages/gateway":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@discordeno/gateway@workspace:packages/gateway"
|
||||
|
||||
Reference in New Issue
Block a user