mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
refactor(Webhooks)!: remove WebhookClient (#11266)
BREAKING CHANGE: WebhookClient has been removed, use @discordjs/core instead or fetch webhooks. Alternative solutions are in the works BREAKING CHANGE: `WebhookURLInvalid` is no longer an error code (obsolete). Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
This commit is contained in:
@@ -1,119 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { DiscordjsError, ErrorCodes } = require('../errors/index.js');
|
||||
const { Webhook } = require('../structures/Webhook.js');
|
||||
const { parseWebhookURL } = require('../util/Util.js');
|
||||
const { BaseClient } = require('./BaseClient.js');
|
||||
|
||||
/**
|
||||
* The webhook client.
|
||||
*
|
||||
* @implements {Webhook}
|
||||
* @extends {BaseClient}
|
||||
*/
|
||||
class WebhookClient extends BaseClient {
|
||||
/**
|
||||
* Represents the credentials used for a webhook in the form of its id and token.
|
||||
*
|
||||
* @typedef {Object} WebhookClientDataIdWithToken
|
||||
* @property {Snowflake} id The webhook's id
|
||||
* @property {string} token The webhook's token
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the credentials used for a webhook in the form of a URL.
|
||||
*
|
||||
* @typedef {Object} WebhookClientDataURL
|
||||
* @property {string} url The full URL for the webhook
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the credentials used for a webhook.
|
||||
*
|
||||
* @typedef {WebhookClientDataIdWithToken|WebhookClientDataURL} WebhookClientData
|
||||
*/
|
||||
|
||||
/**
|
||||
* Options for a webhook client.
|
||||
*
|
||||
* @typedef {Object} WebhookClientOptions
|
||||
* @property {MessageMentionOptions} [allowedMentions] Default value for {@link BaseMessageOptions#allowedMentions}
|
||||
* @property {RESTOptions} [rest] Options for the REST manager
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {WebhookClientData} data The data of the webhook
|
||||
* @param {WebhookClientOptions} [options] Options for the webhook client
|
||||
*/
|
||||
constructor(data, options) {
|
||||
super(options);
|
||||
Object.defineProperty(this, 'client', { value: this });
|
||||
let { id, token } = data;
|
||||
|
||||
if ('url' in data) {
|
||||
const parsed = parseWebhookURL(data.url);
|
||||
if (!parsed) {
|
||||
throw new DiscordjsError(ErrorCodes.WebhookURLInvalid);
|
||||
}
|
||||
|
||||
({ id, token } = parsed);
|
||||
}
|
||||
|
||||
this.id = id;
|
||||
Object.defineProperty(this, 'token', { value: token, writable: true, configurable: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* The options the webhook client was instantiated with.
|
||||
*
|
||||
* @type {WebhookClientOptions}
|
||||
* @name WebhookClient#options
|
||||
*/
|
||||
|
||||
// These are here only for documentation purposes - they are implemented by Webhook
|
||||
|
||||
/* eslint-disable jsdoc/check-param-names, getter-return */
|
||||
/**
|
||||
* Sends a message with this webhook.
|
||||
*
|
||||
* @param {string|MessagePayload|WebhookMessageCreateOptions} options The content for the reply
|
||||
* @returns {Promise<APIMessage>}
|
||||
*/
|
||||
async send() {}
|
||||
|
||||
/**
|
||||
* Gets a message that was sent by this webhook.
|
||||
*
|
||||
* @param {Snowflake} message The id of the message to fetch
|
||||
* @param {WebhookFetchMessageOptions} [options] The options to provide to fetch the message.
|
||||
* @returns {Promise<APIMessage>} Returns the message sent by this webhook
|
||||
*/
|
||||
async fetchMessage() {}
|
||||
|
||||
/**
|
||||
* Edits a message that was sent by this webhook.
|
||||
*
|
||||
* @param {MessageResolvable} message The message to edit
|
||||
* @param {string|MessagePayload|WebhookMessageEditOptions} options The options to provide
|
||||
* @returns {Promise<APIMessage>} Returns the message edited by this webhook
|
||||
*/
|
||||
async editMessage() {}
|
||||
|
||||
sendSlackMessage() {}
|
||||
|
||||
edit() {}
|
||||
|
||||
delete() {}
|
||||
|
||||
deleteMessage() {}
|
||||
|
||||
get createdTimestamp() {}
|
||||
|
||||
get createdAt() {}
|
||||
|
||||
get url() {}
|
||||
}
|
||||
|
||||
Webhook.applyToClass(WebhookClient);
|
||||
|
||||
exports.WebhookClient = WebhookClient;
|
||||
@@ -77,7 +77,6 @@
|
||||
*
|
||||
* @property {'WebhookMessage'} WebhookMessage
|
||||
* @property {'WebhookTokenUnavailable'} WebhookTokenUnavailable
|
||||
* @property {'WebhookURLInvalid'} WebhookURLInvalid
|
||||
* @property {'WebhookApplication'} WebhookApplication
|
||||
*
|
||||
* @property {'MessageReferenceMissing'} MessageReferenceMissing
|
||||
@@ -212,7 +211,6 @@ const keys = [
|
||||
|
||||
'WebhookMessage',
|
||||
'WebhookTokenUnavailable',
|
||||
'WebhookURLInvalid',
|
||||
'WebhookApplication',
|
||||
|
||||
'MessageReferenceMissing',
|
||||
|
||||
@@ -82,7 +82,6 @@ const Messages = {
|
||||
|
||||
[ErrorCodes.WebhookMessage]: 'The message was not sent by a webhook.',
|
||||
[ErrorCodes.WebhookTokenUnavailable]: 'This action requires a webhook token, but none is available.',
|
||||
[ErrorCodes.WebhookURLInvalid]: 'The provided webhook URL is not valid.',
|
||||
[ErrorCodes.WebhookApplication]: 'This message webhook belongs to an application and cannot be fetched.',
|
||||
|
||||
[ErrorCodes.MessageReferenceMissing]: 'The message does not reference another message',
|
||||
|
||||
@@ -8,7 +8,6 @@ exports.Client = require('./client/Client.js').Client;
|
||||
exports.Shard = require('./sharding/Shard.js').Shard;
|
||||
exports.ShardClientUtil = require('./sharding/ShardClientUtil.js').ShardClientUtil;
|
||||
exports.ShardingManager = require('./sharding/ShardingManager.js').ShardingManager;
|
||||
exports.WebhookClient = require('./client/WebhookClient.js').WebhookClient;
|
||||
|
||||
// Errors
|
||||
exports.DiscordjsError = require('./errors/DJSError.js').DiscordjsError;
|
||||
|
||||
@@ -47,15 +47,14 @@ class MessagePayload {
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the target is a {@link Webhook} or a {@link WebhookClient}
|
||||
* Whether or not the target is a {@link Webhook}
|
||||
*
|
||||
* @type {boolean}
|
||||
* @readonly
|
||||
*/
|
||||
get isWebhook() {
|
||||
const { Webhook } = require('./Webhook.js');
|
||||
const { WebhookClient } = require('../client/WebhookClient.js');
|
||||
return this.target instanceof Webhook || this.target instanceof WebhookClient;
|
||||
return this.target instanceof Webhook;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,7 +301,7 @@ exports.MessagePayload = MessagePayload;
|
||||
/**
|
||||
* A target for a message.
|
||||
*
|
||||
* @typedef {TextBasedChannels|ChannelManager|Webhook|WebhookClient|BaseInteraction|InteractionWebhook|
|
||||
* @typedef {TextBasedChannels|ChannelManager|Webhook|BaseInteraction|InteractionWebhook|
|
||||
* Message|MessageManager} MessageTarget
|
||||
*/
|
||||
|
||||
|
||||
@@ -95,9 +95,9 @@ class Webhook {
|
||||
/**
|
||||
* The owner of the webhook
|
||||
*
|
||||
* @type {?(User|APIUser)}
|
||||
* @type {?User}
|
||||
*/
|
||||
this.owner = this.client.users?._add(data.user) ?? data.user;
|
||||
this.owner = this.client.users._add(data.user);
|
||||
} else {
|
||||
this.owner ??= null;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ class Webhook {
|
||||
*
|
||||
* @type {?(Guild|APIGuild)}
|
||||
*/
|
||||
this.sourceGuild = this.client.guilds?.cache.get(data.source_guild.id) ?? data.source_guild;
|
||||
this.sourceGuild = this.client.guilds.cache.get(data.source_guild.id) ?? data.source_guild;
|
||||
} else {
|
||||
this.sourceGuild ??= null;
|
||||
}
|
||||
@@ -130,7 +130,7 @@ class Webhook {
|
||||
*
|
||||
* @type {?(AnnouncementChannel|APIChannel)}
|
||||
*/
|
||||
this.sourceChannel = this.client.channels?.cache.get(data.source_channel?.id) ?? data.source_channel;
|
||||
this.sourceChannel = this.client.channels.cache.get(data.source_channel?.id) ?? data.source_channel;
|
||||
} else {
|
||||
this.sourceChannel ??= null;
|
||||
}
|
||||
@@ -248,7 +248,6 @@ class Webhook {
|
||||
auth: false,
|
||||
});
|
||||
|
||||
if (!this.client.channels) return data;
|
||||
return (
|
||||
this.client.channels.cache.get(data.channel_id)?.messages._add(data, false) ??
|
||||
new (getMessage())(this.client, data)
|
||||
@@ -345,7 +344,6 @@ class Webhook {
|
||||
auth: false,
|
||||
});
|
||||
|
||||
if (!this.client.channels) return data;
|
||||
return (
|
||||
this.client.channels.cache.get(data.channel_id)?.messages._add(data, false) ??
|
||||
new (getMessage())(this.client, data)
|
||||
@@ -384,10 +382,7 @@ class Webhook {
|
||||
},
|
||||
);
|
||||
|
||||
const channelManager = this.client.channels;
|
||||
if (!channelManager) return data;
|
||||
|
||||
const messageManager = channelManager.cache.get(data.channel_id)?.messages;
|
||||
const messageManager = this.client.channels.cache.get(data.channel_id)?.messages;
|
||||
if (!messageManager) return new (getMessage())(this.client, data);
|
||||
|
||||
const existing = messageManager.cache.get(data.id);
|
||||
|
||||
@@ -469,11 +469,19 @@ function cleanCodeBlockContent(text) {
|
||||
return text.replaceAll('```', '`\u200B``');
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the credentials used for a webhook in the form of its id and token.
|
||||
*
|
||||
* @typedef {Object} WebhookDataIdWithToken
|
||||
* @property {Snowflake} id The webhook's id
|
||||
* @property {string} token The webhook's token
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parses a webhook URL for the id and token.
|
||||
*
|
||||
* @param {string} url The URL to parse
|
||||
* @returns {?WebhookClientDataIdWithToken} `null` if the URL is invalid, otherwise the id and the token
|
||||
* @returns {?WebhookDataIdWithToken} `null` if the URL is invalid, otherwise the id and the token
|
||||
*/
|
||||
function parseWebhookURL(url) {
|
||||
const matches =
|
||||
|
||||
@@ -7,8 +7,8 @@ const { setTimeout: sleep } = require('node:timers/promises');
|
||||
const util = require('node:util');
|
||||
const { GatewayIntentBits } = require('discord-api-types/v10');
|
||||
const { fetch } = require('undici');
|
||||
const { Client, MessageAttachment, Embed } = require('../src/index.js');
|
||||
const { owner, token, webhookChannel, webhookToken } = require('./auth.js');
|
||||
const { Client, MessageAttachment, Embed, WebhookClient } = require('../src/index.js');
|
||||
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages] });
|
||||
|
||||
@@ -84,30 +84,26 @@ client.on('messageCreate', async message => {
|
||||
if (message.author.id !== owner) return;
|
||||
const match = message.content.match(/^do (.+)$/);
|
||||
const hooks = [
|
||||
{ type: 'WebhookClient', hook: new WebhookClient({ id: webhookChannel, token: webhookToken }) },
|
||||
{ type: 'TextChannel#fetchWebhooks', hook: await message.channel.fetchWebhooks().then(x => x.first()) },
|
||||
{ type: 'Guild#fetchWebhooks', hook: await message.guild.fetchWebhooks().then(x => x.first()) },
|
||||
];
|
||||
if (match?.[1] === 'it') {
|
||||
/* eslint-disable no-await-in-loop */
|
||||
for (const { type, hook } of hooks) {
|
||||
for (const [i, test] of tests.entries()) {
|
||||
await message.channel.send(`**#${i}-Hook: ${type}**\n\`\`\`js\n${test.toString()}\`\`\``);
|
||||
await test(message, hook).catch(e => message.channel.send(`Error!\n\`\`\`\n${e}\`\`\``));
|
||||
await test(message, hook).catch(error => message.channel.send(`Error!\n\`\`\`\n${error}\`\`\``));
|
||||
await sleep(1_000);
|
||||
}
|
||||
}
|
||||
/* eslint-enable no-await-in-loop */
|
||||
} else if (match) {
|
||||
const n = parseInt(match[1]) || 0;
|
||||
const n = Number.parseInt(match[1]) || 0;
|
||||
const test = tests.slice(n)[0];
|
||||
const i = tests.indexOf(test);
|
||||
/* eslint-disable no-await-in-loop */
|
||||
|
||||
for (const { type, hook } of hooks) {
|
||||
await message.channel.send(`**#${i}-Hook: ${type}**\n\`\`\`js\n${test.toString()}\`\`\``);
|
||||
await test(message, hook).catch(e => message.channel.send(`Error!\n\`\`\`\n${e}\`\`\``));
|
||||
await test(message, hook).catch(error => message.channel.send(`Error!\n\`\`\`\n${error}\`\`\``));
|
||||
}
|
||||
/* eslint-enable no-await-in-loop */
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
45
packages/discord.js/typings/index.d.ts
vendored
45
packages/discord.js/typings/index.d.ts
vendored
@@ -493,11 +493,11 @@ export abstract class Base {
|
||||
}
|
||||
|
||||
export class BaseClient<Events extends {}> extends AsyncEventEmitter<Events> implements AsyncDisposable {
|
||||
public constructor(options?: ClientOptions | WebhookClientOptions);
|
||||
public constructor(options?: ClientOptions);
|
||||
private decrementMaxListeners(): void;
|
||||
private incrementMaxListeners(): void;
|
||||
|
||||
public options: ClientOptions | WebhookClientOptions;
|
||||
public options: ClientOptions;
|
||||
public rest: REST;
|
||||
public destroy(): void;
|
||||
public toJSON(...props: Record<string, boolean | string>[]): unknown;
|
||||
@@ -3742,7 +3742,7 @@ export function fetchRecommendedShardCount(token: string, options?: FetchRecomme
|
||||
export function flatten(obj: unknown, ...props: Record<string, boolean | string>[]): unknown;
|
||||
|
||||
export function parseEmoji(text: string): PartialEmoji | null;
|
||||
export function parseWebhookURL(url: string): WebhookClientDataIdWithToken | null;
|
||||
export function parseWebhookURL(url: string): WebhookDataIdWithToken | null;
|
||||
export function resolveColor(color: ColorResolvable): number;
|
||||
export function resolveSKUId(resolvable: SKUResolvable): Snowflake | null;
|
||||
export function verifyString(data: string, error?: typeof Error, errorMessage?: string, allowEmpty?: boolean): string;
|
||||
@@ -3828,7 +3828,7 @@ export class Webhook<Type extends WebhookType = WebhookType> {
|
||||
public readonly client: Client;
|
||||
public guildId: Snowflake;
|
||||
public name: string;
|
||||
public owner: Type extends WebhookType.Incoming ? APIUser | User | null : APIUser | User;
|
||||
public owner: Type extends WebhookType.Incoming ? User | null : User;
|
||||
public sourceGuild: Type extends WebhookType.ChannelFollower ? APIPartialGuild | Guild : null;
|
||||
public sourceChannel: Type extends WebhookType.ChannelFollower ? AnnouncementChannel | APIPartialChannel : null;
|
||||
public token: Type extends WebhookType.Incoming
|
||||
@@ -3847,7 +3847,7 @@ export class Webhook<Type extends WebhookType = WebhookType> {
|
||||
| VoiceChannel
|
||||
| null;
|
||||
public isUserCreated(): this is Webhook<WebhookType.Incoming> & {
|
||||
owner: APIUser | User;
|
||||
owner: User;
|
||||
};
|
||||
public isApplicationCreated(): this is Webhook<WebhookType.Application>;
|
||||
public isIncoming(): this is Webhook<WebhookType.Incoming>;
|
||||
@@ -3861,20 +3861,6 @@ export class Webhook<Type extends WebhookType = WebhookType> {
|
||||
public send(options: MessagePayload | WebhookMessageCreateOptions | string): Promise<Message<true>>;
|
||||
}
|
||||
|
||||
export interface WebhookClient extends WebhookFields, BaseClient<{}> {}
|
||||
export class WebhookClient extends BaseClient<{}> {
|
||||
public constructor(data: WebhookClientData, options?: WebhookClientOptions);
|
||||
public readonly client: this;
|
||||
public options: WebhookClientOptions;
|
||||
public token: string;
|
||||
public editMessage(
|
||||
message: MessageResolvable,
|
||||
options: MessagePayload | WebhookMessageEditOptions | string,
|
||||
): Promise<APIMessage>;
|
||||
public fetchMessage(message: Snowflake, options?: WebhookFetchMessageOptions): Promise<APIMessage>;
|
||||
public send(options: MessagePayload | WebhookMessageCreateOptions | string): Promise<APIMessage>;
|
||||
}
|
||||
|
||||
export class Widget extends Base {
|
||||
private constructor(client: Client<true>, data: APIGuildWidget);
|
||||
private _patch(data: APIGuildWidget): void;
|
||||
@@ -4064,7 +4050,6 @@ export enum DiscordjsErrorCodes {
|
||||
|
||||
WebhookMessage = 'WebhookMessage',
|
||||
WebhookTokenUnavailable = 'WebhookTokenUnavailable',
|
||||
WebhookURLInvalid = 'WebhookURLInvalid',
|
||||
WebhookApplication = 'WebhookApplication',
|
||||
|
||||
MessageReferenceMissing = 'MessageReferenceMissing',
|
||||
@@ -5576,7 +5561,8 @@ export interface ClientFetchInviteOptions {
|
||||
withCounts?: boolean;
|
||||
}
|
||||
|
||||
export interface ClientOptions extends WebhookClientOptions {
|
||||
export interface ClientOptions {
|
||||
allowedMentions?: MessageMentionOptions;
|
||||
closeTimeout?: number;
|
||||
enforceNonce?: boolean;
|
||||
failIfNotExists?: boolean;
|
||||
@@ -5585,6 +5571,7 @@ export interface ClientOptions extends WebhookClientOptions {
|
||||
makeCache?: CacheFactory;
|
||||
partials?: readonly Partials[];
|
||||
presence?: PresenceData;
|
||||
rest?: Partial<RESTOptions>;
|
||||
sweepers?: SweeperOptions;
|
||||
waitGuildTimeout?: number;
|
||||
ws?: Partial<WebSocketManagerOptions>;
|
||||
@@ -6873,8 +6860,7 @@ export type MessageTarget =
|
||||
| Message
|
||||
| MessageManager
|
||||
| TextBasedChannel
|
||||
| Webhook<WebhookType.Incoming>
|
||||
| WebhookClient;
|
||||
| Webhook<WebhookType.Incoming>;
|
||||
|
||||
export interface MultipleShardRespawnOptions {
|
||||
respawnDelay?: number;
|
||||
@@ -7248,22 +7234,11 @@ export interface VoiceStateEditOptions {
|
||||
suppressed?: boolean;
|
||||
}
|
||||
|
||||
export type WebhookClientData = WebhookClientDataIdWithToken | WebhookClientDataURL;
|
||||
|
||||
export interface WebhookClientDataIdWithToken {
|
||||
export interface WebhookDataIdWithToken {
|
||||
id: Snowflake;
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface WebhookClientDataURL {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface WebhookClientOptions {
|
||||
allowedMentions?: MessageMentionOptions;
|
||||
rest?: Partial<RESTOptions>;
|
||||
}
|
||||
|
||||
export interface WebhookDeleteOptions {
|
||||
reason?: string;
|
||||
token?: string;
|
||||
|
||||
@@ -10,7 +10,6 @@ import type {
|
||||
APIInteractionDataResolvedChannel,
|
||||
APIInteractionDataResolvedGuildMember,
|
||||
APIInteractionGuildMember,
|
||||
APIMessage,
|
||||
APIPartialChannel,
|
||||
APIPartialGuild,
|
||||
APIRole,
|
||||
@@ -231,7 +230,6 @@ import {
|
||||
UserSelectMenuComponent,
|
||||
UserSelectMenuInteraction,
|
||||
Webhook,
|
||||
WebhookClient,
|
||||
} from './index.js';
|
||||
|
||||
// Test type transformation:
|
||||
@@ -2683,7 +2681,6 @@ expectType<UserMention>(user.toString());
|
||||
expectType<UserMention>(guildMember.toString());
|
||||
|
||||
declare const webhook: Webhook;
|
||||
declare const webhookClient: WebhookClient;
|
||||
declare const interactionWebhook: InteractionWebhook;
|
||||
declare const snowflake: Snowflake;
|
||||
|
||||
@@ -2692,10 +2689,6 @@ expectType<Promise<Message<true>>>(webhook.editMessage(snowflake, 'content'));
|
||||
expectType<Promise<Message<true>>>(webhook.fetchMessage(snowflake));
|
||||
expectType<Promise<Webhook>>(webhook.edit({ name: 'name' }));
|
||||
|
||||
expectType<Promise<APIMessage>>(webhookClient.send('content'));
|
||||
expectType<Promise<APIMessage>>(webhookClient.editMessage(snowflake, 'content'));
|
||||
expectType<Promise<APIMessage>>(webhookClient.fetchMessage(snowflake));
|
||||
|
||||
expectType<Client<true>>(interactionWebhook.client);
|
||||
expectType<Promise<Message>>(interactionWebhook.send('content'));
|
||||
expectType<Promise<Message>>(interactionWebhook.editMessage(snowflake, 'content'));
|
||||
|
||||
Reference in New Issue
Block a user