feat: thread methods and shorter (#184)

* feat: threads methods

* fix: typing

* fix: fixes
This commit is contained in:
Marcos Susaña 2024-04-16 15:07:11 -04:00 committed by GitHub
parent 838ebd5c27
commit 773c0a38c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 163 additions and 35 deletions

View File

@ -17,6 +17,7 @@ import {
ReactionShorter, ReactionShorter,
RoleShorter, RoleShorter,
TemplateShorter, TemplateShorter,
ThreadShorter,
UsersShorter, UsersShorter,
WebhookShorter, WebhookShorter,
filterSplit, filterSplit,
@ -50,6 +51,7 @@ export class BaseClient {
roles = new RoleShorter(this); roles = new RoleShorter(this);
reactions = new ReactionShorter(this); reactions = new ReactionShorter(this);
emojis = new EmojiShorter(this); emojis = new EmojiShorter(this);
threads = new ThreadShorter(this);
debugger?: Logger; debugger?: Logger;

View File

@ -12,6 +12,7 @@ export * from './shorters/reactions';
export * from './shorters/roles'; export * from './shorters/roles';
export * from './shorters/templates'; export * from './shorters/templates';
export * from './shorters/users'; export * from './shorters/users';
export * from './shorters/threads';
export * from './shorters/webhook'; export * from './shorters/webhook';
export * from './types/options'; export * from './types/options';
export * from './types/resolvables'; export * from './types/resolvables';

View File

@ -5,9 +5,10 @@ import {
type RESTPostAPIGuildForumThreadsJSONBody, type RESTPostAPIGuildForumThreadsJSONBody,
} from 'discord-api-types/v10'; } from 'discord-api-types/v10';
import { BaseChannel, Message, type GuildMember, type GuildRole } from '../../structures'; import { BaseChannel, Message, type GuildMember, type GuildRole } from '../../structures';
import channelFrom, { type AllChannels, type ThreadChannel } from '../../structures/channels'; import channelFrom, { type AllChannels } from '../../structures/channels';
import { PermissionsBitField } from '../../structures/extra/Permissions'; import { PermissionsBitField } from '../../structures/extra/Permissions';
import { BaseShorter } from './base'; import { BaseShorter } from './base';
import { MergeOptions } from '../it/utils';
export class ChannelShorter extends BaseShorter { export class ChannelShorter extends BaseShorter {
/** /**
@ -35,8 +36,9 @@ export class ChannelShorter extends BaseShorter {
* @returns A Promise that resolves to the deleted channel. * @returns A Promise that resolves to the deleted channel.
*/ */
async delete(id: string, optional: ChannelShorterOptionalParams = { guildId: '@me' }): Promise<AllChannels> { async delete(id: string, optional: ChannelShorterOptionalParams = { guildId: '@me' }): Promise<AllChannels> {
const res = await this.client.proxy.channels(id).delete({ reason: optional.reason }); const options = MergeOptions<ChannelShorterOptionalParams>({ guildId: '@me' }, optional);
await this.client.cache.channels?.removeIfNI(BaseChannel.__intent__(optional.guildId!), res.id, optional.guildId!); const res = await this.client.proxy.channels(id).delete({ reason: options.reason });
await this.client.cache.channels?.removeIfNI(BaseChannel.__intent__(options.guildId!), res.id, options.guildId!);
return channelFrom(res, this.client); return channelFrom(res, this.client);
} }
@ -52,18 +54,14 @@ export class ChannelShorter extends BaseShorter {
body: RESTPatchAPIChannelJSONBody, body: RESTPatchAPIChannelJSONBody,
optional: ChannelShorterOptionalParams = { guildId: '@me' }, optional: ChannelShorterOptionalParams = { guildId: '@me' },
): Promise<AllChannels> { ): Promise<AllChannels> {
const res = await this.client.proxy.channels(id).patch({ body, reason: optional.reason }); const options = MergeOptions<ChannelShorterOptionalParams>({ guildId: '@me' }, optional);
await this.client.cache.channels?.setIfNI( const res = await this.client.proxy.channels(id).patch({ body, reason: options.reason });
BaseChannel.__intent__(optional.guildId!), await this.client.cache.channels?.setIfNI(BaseChannel.__intent__(options.guildId!), res.id, options.guildId!, res);
res.id,
optional.guildId!,
res,
);
if (body.permission_overwrites && 'permission_overwrites' in res) if (body.permission_overwrites && 'permission_overwrites' in res)
await this.client.cache.overwrites?.setIfNI( await this.client.cache.overwrites?.setIfNI(
BaseChannel.__intent__(optional.guildId!), BaseChannel.__intent__(options.guildId!),
res.id, res.id,
optional.guildId!, options.guildId!,
res.permission_overwrites, res.permission_overwrites,
); );
return channelFrom(res, this.client); return channelFrom(res, this.client);
@ -116,13 +114,7 @@ export class ChannelShorter extends BaseShorter {
body: RESTPostAPIChannelThreadsJSONBody | RESTPostAPIGuildForumThreadsJSONBody, body: RESTPostAPIChannelThreadsJSONBody | RESTPostAPIGuildForumThreadsJSONBody,
reason?: string, reason?: string,
) { ) {
return ( return this.client.threads.create(channelId, body, reason);
this.client.proxy
.channels(channelId)
.threads.post({ body, reason })
// When testing this, discord returns the thread object, but in discord api types it does not.
.then(thread => channelFrom(thread, this.client) as ThreadChannel)
);
} }
async memberPermissions(channelId: string, member: GuildMember, checkAdmin = true): Promise<PermissionsBitField> { async memberPermissions(channelId: string, member: GuildMember, checkAdmin = true): Promise<PermissionsBitField> {

View File

@ -185,6 +185,15 @@ export class GuildShorter extends BaseShorter {
*/ */
editPositions: (guildId: string, body: RESTPatchAPIGuildChannelPositionsJSONBody) => editPositions: (guildId: string, body: RESTPatchAPIGuildChannelPositionsJSONBody) =>
this.client.proxy.guilds(guildId).channels.patch({ body }), this.client.proxy.guilds(guildId).channels.patch({ body }),
addFollower: async (channelId: string, webhook_channel_id: string, reason?: string) => {
return this.client.proxy.channels(channelId).followers.post({
body: {
webhook_channel_id,
},
reason,
});
},
}; };
} }

View File

@ -4,8 +4,8 @@ import type {
RESTPostAPIChannelMessagesThreadsJSONBody, RESTPostAPIChannelMessagesThreadsJSONBody,
} from 'discord-api-types/v10'; } from 'discord-api-types/v10';
import { resolveFiles } from '../../builders'; import { resolveFiles } from '../../builders';
import { Message, MessagesMethods, type ThreadChannel } from '../../structures'; import { Message, MessagesMethods } from '../../structures';
import channelFrom from '../../structures/channels';
import type { MessageCreateBodyRequest, MessageUpdateBodyRequest } from '../types/write'; import type { MessageCreateBodyRequest, MessageUpdateBodyRequest } from '../types/write';
import { BaseShorter } from './base'; import { BaseShorter } from './base';
@ -70,12 +70,6 @@ export class MessageShorter extends BaseShorter {
messageId: string, messageId: string,
options: RESTPostAPIChannelMessagesThreadsJSONBody & { reason?: string }, options: RESTPostAPIChannelMessagesThreadsJSONBody & { reason?: string },
) { ) {
const { reason, ...body } = options; return this.client.threads.fromMessage(channelId, messageId, options);
return this.client.proxy
.channels(channelId)
.messages(messageId)
.threads.post({ body, reason })
.then(thread => channelFrom(thread, this.client) as ThreadChannel);
} }
} }

View File

@ -0,0 +1,125 @@
import type {
APIThreadMember,
RESTGetAPIChannelThreadMembersQuery,
RESTGetAPIChannelThreadsArchivedQuery,
RESTPatchAPIChannelJSONBody,
RESTPostAPIChannelMessagesThreadsJSONBody,
RESTPostAPIChannelThreadsJSONBody,
RESTPostAPIGuildForumThreadsJSONBody,
} from 'discord-api-types/v10';
import type { ThreadChannel } from '../../structures';
import channelFrom from '../../structures/channels';
import { BaseShorter } from './base';
import type { MakeRequired, When } from '../types/util';
export class ThreadShorter extends BaseShorter {
/**
* Creates a new thread in the channel (only guild based channels).
* @param channelId The ID of the parent channel.
* @param reason The reason for unpinning the message.
* @returns A promise that resolves when the thread is succesfully created.
*/
async create(
channelId: string,
body: RESTPostAPIChannelThreadsJSONBody | RESTPostAPIGuildForumThreadsJSONBody,
reason?: string,
) {
return (
this.client.proxy
.channels(channelId)
.threads.post({ body, reason })
// When testing this, discord returns the thread object, but in discord api types it does not.
.then(thread => channelFrom(thread, this.client) as ThreadChannel)
);
}
async fromMessage(
channelId: string,
messageId: string,
options: RESTPostAPIChannelMessagesThreadsJSONBody & { reason?: string },
) {
const { reason, ...body } = options;
return this.client.proxy
.channels(channelId)
.messages(messageId)
.threads.post({ body, reason })
.then(thread => channelFrom(thread, this.client) as ThreadChannel);
}
async join(threadId: string) {
return this.client.proxy.channels(threadId)['thread-members']('@me').put();
}
async leave(threadId: string) {
return this.client.proxy.channels(threadId)['thread-members']('@me').delete();
}
async lock(threadId: string, locked = true, reason?: string) {
return this.edit(threadId, { locked }, reason).then(x => channelFrom(x, this.client) as ThreadChannel);
}
async edit(threadId: string, body: RESTPatchAPIChannelJSONBody, reason?: string) {
return this.client.channels.edit(threadId, body, { reason });
}
async removeMember(threadId: string, memberId: string) {
return this.client.proxy.channels(threadId)['thread-members'](memberId).delete();
}
async fetchMember<WithMember extends boolean = false>(
threadId: string,
memberId: string,
with_member: WithMember,
): Promise<When<WithMember, Required<APIThreadMember>, GetAPIChannelThreadMemberResult>> {
return this.client.proxy.channels(threadId)['thread-members'](memberId).get({
query: {
with_member,
},
}) as never;
}
async addMember(threadId: string, memberId: string) {
return this.client.proxy.channels(threadId)['thread-members'](memberId).put();
}
async listMembers<T extends RESTGetAPIChannelThreadMembersQuery = RESTGetAPIChannelThreadMembersQuery>(
threadId: string,
query?: T,
): Promise<InferWithMemberOnList<T>> {
return this.client.proxy.channels(threadId)['thread-members'].get({ query }) as never;
}
async listArchivedThreads(
channelId: string,
type: 'public' | 'private',
query?: RESTGetAPIChannelThreadsArchivedQuery,
) {
const data = await this.client.proxy.channels(channelId).threads.archived[type].get({ query });
return {
threads: data.threads.map(thread => channelFrom(thread, this.client) as ThreadChannel),
members: data.members as GetAPIChannelThreadMemberResult[],
hasMore: data.has_more,
};
}
async listJoinedArchivedPrivate(channelId: string, query?: RESTGetAPIChannelThreadsArchivedQuery) {
const data = await this.client.proxy.channels(channelId).users('@me').threads.archived.private.get({ query });
return {
threads: data.threads.map(thread => channelFrom(thread, this.client) as ThreadChannel),
members: data.members as GetAPIChannelThreadMemberResult[],
hasMore: data.has_more,
};
}
}
export type GetAPIChannelThreadMemberResult = MakeRequired<APIThreadMember, 'id' | 'user_id'>;
type InferWithMemberOnList<T extends RESTGetAPIChannelThreadMembersQuery> = T extends {
with_member: infer B;
}
? B extends true
? Required<APIThreadMember>[]
: GetAPIChannelThreadMemberResult[]
: GetAPIChannelThreadMemberResult[];

View File

@ -455,6 +455,16 @@ export class ThreadChannel extends BaseChannel<
channelId: this.parentId!, channelId: this.parentId!,
}); });
async join() {
await this.client.threads.join(this.id);
return this;
}
async leave() {
await this.client.threads.leave(this.id);
return this;
}
setRatelimitPerUser(rate_limit_per_user: number | null | undefined) { setRatelimitPerUser(rate_limit_per_user: number | null | undefined) {
return this.edit({ rate_limit_per_user }); return this.edit({ rate_limit_per_user });
} }
@ -506,13 +516,8 @@ export interface NewsChannel extends ObjectToLower<APINewsChannel>, WebhookChann
export class NewsChannel extends BaseChannel<ChannelType.GuildAnnouncement> { export class NewsChannel extends BaseChannel<ChannelType.GuildAnnouncement> {
declare type: ChannelType.GuildAnnouncement; declare type: ChannelType.GuildAnnouncement;
addFollower(webhook_channel_id: string, reason?: string) { addFollower(webhookChannelId: string, reason?: string) {
return this.api.channels(this.id).followers.post({ return this.client.guilds.channels.addFollower(this.id, webhookChannelId, reason);
body: {
webhook_channel_id,
},
reason,
});
} }
} }