feat: Shorters utilities (#172)

* feat: more shorters, voice state

* fix: voice states resource types

* fix: add clarification

* fix: fixes

* fix: voice state updates

* fix: xd?

---------

Co-authored-by: MARCROCK22 <57925328+MARCROCK22@users.noreply.github.com>
This commit is contained in:
Marcos Susaña 2024-04-01 18:07:41 -04:00 committed by GitHub
parent ef8e2e6487
commit 329b96387e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 140 additions and 12 deletions

View File

@ -1,12 +1,32 @@
import type { GatewayVoiceState } from 'discord-api-types/v10';
import type { ReturnCache } from '../..';
import { fakePromise } from '../../common';
import { VoiceState } from '../../structures';
import { GuildBasedResource } from './default/guild-based';
export class VoiceStates extends GuildBasedResource<VoiceStateResource> {
export class VoiceStates extends GuildBasedResource {
namespace = 'voice_state';
override parse(data: any, id: string, guild_id: string): VoiceStateResource {
override get(memberId: string, guildId: string): ReturnCache<VoiceState | undefined> {
return fakePromise(super.get(memberId, guildId)).then(state =>
state ? new VoiceState(this.client, state) : undefined,
);
}
override bulk(ids: string[], guild: string): ReturnCache<VoiceState[]> {
return fakePromise(super.bulk(ids, guild)).then(
states =>
states.map(state => (state ? new VoiceState(this.client, state) : undefined)).filter(y => !!y) as VoiceState[],
);
}
override values(guildId: string): ReturnCache<VoiceState[]> {
return fakePromise(super.values(guildId)).then(states => states.map(state => new VoiceState(this.client, state)));
}
override parse(data: any, id: string, guild_id: string): ReturnCache<VoiceState> {
const { member, ...rest } = super.parse(data, id, guild_id);
return rest;
return new VoiceState(this.client, rest);
}
}

View File

@ -1,6 +1,11 @@
import { PermissionFlagsBits, type RESTPatchAPIChannelJSONBody } from 'discord-api-types/v10';
import {
PermissionFlagsBits,
type RESTPatchAPIChannelJSONBody,
type RESTPostAPIChannelThreadsJSONBody,
type RESTPostAPIGuildForumThreadsJSONBody,
} from 'discord-api-types/v10';
import { BaseChannel, Message, type GuildMember, type GuildRole } from '../../structures';
import channelFrom, { type AllChannels } from '../../structures/channels';
import channelFrom, { type AllChannels, type ThreadChannel } from '../../structures/channels';
import { PermissionsBitField } from '../../structures/extra/Permissions';
import { BaseShorter } from './base';
@ -100,6 +105,26 @@ export class ChannelShorter extends BaseShorter {
return this.client.proxy.channels(channelId).pins(messageId).delete({ reason });
}
/**
* 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 thread(
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 memberPermissions(channelId: string, member: GuildMember, checkAdmin = true): Promise<PermissionsBitField> {
const permissions = await member.fetchPermissions();

View File

@ -208,4 +208,12 @@ export class MemberShorter extends BaseShorter {
return new PermissionsBitField(roles.map(x => BigInt(x.permissions.bits)));
}
presence(memberId: string) {
return this.client.cache.presences?.get(memberId);
}
voice(guildId: string, memberId: string) {
return this.client.cache.voiceStates?.get(memberId, guildId);
}
}

View File

@ -4,7 +4,8 @@ import type {
RESTPostAPIChannelMessagesThreadsJSONBody,
} from 'discord-api-types/v10';
import { resolveFiles } from '../../builders';
import { Message, MessagesMethods, ThreadChannel } from '../../structures';
import { Message, MessagesMethods, type ThreadChannel } from '../../structures';
import channelFrom from '../../structures/channels';
import type { MessageCreateBodyRequest, MessageUpdateBodyRequest } from '../types/write';
import { BaseShorter } from './base';
@ -50,7 +51,7 @@ export class MessageShorter extends BaseShorter {
.messages(messageId)
.delete({ reason })
.then(() => {
return this.client.components?.onMessageDelete(messageId);
void this.client.components?.onMessageDelete(messageId);
});
}
fetch(messageId: string, channelId: string) {
@ -64,7 +65,7 @@ export class MessageShorter extends BaseShorter {
return this.client.proxy.channels(channelId).messages['bulk-delete'].post({ body: { messages }, reason });
}
thread(
async thread(
channelId: string,
messageId: string,
options: RESTPostAPIChannelMessagesThreadsJSONBody & { reason?: string },
@ -75,6 +76,6 @@ export class MessageShorter extends BaseShorter {
.channels(channelId)
.messages(messageId)
.threads.post({ body, reason })
.then(x => new ThreadChannel(this.client, x));
.then(thread => channelFrom(thread, this.client) as ThreadChannel);
}
}

View File

@ -68,11 +68,11 @@ export class BaseGuildMember extends DiscordBase {
}
presence() {
return this.cache.presences?.get(this.id);
return this.client.members.presence(this.id);
}
voice() {
return this.cache.voiceStates?.get(this.id, this.guildId);
return this.client.members.voice(this.guildId, this.id);
}
toString() {

View File

@ -47,7 +47,7 @@ export class User extends DiscordBase<APIUser> {
}
presence() {
return this.cache.presences?.get(this.id);
return this.client.members.presence(this.id);
}
toString() {

View File

@ -0,0 +1,59 @@
import type { GuildMember, UsingClient } from '../';
import type { VoiceStateResource } from '../cache/resources/voice-states';
import type { ObjectToLower } from '../common';
import { Base } from './extra/Base';
export interface VoiceState extends Base, ObjectToLower<VoiceStateResource> {}
export class VoiceState extends Base {
constructor(
client: UsingClient,
data: VoiceStateResource,
private withMember?: GuildMember,
) {
super(client);
this.__patchThis(data);
}
isMuted() {
return this.mute || this.selfMute;
}
async member(force?: boolean) {
return (this.withMember ??= await this.client.members.fetch(this.guildId, this.userId, force));
}
async user(force?: boolean) {
return this.client.users.fetch(this.userId, force);
}
async channel(force?: boolean) {
if (!this.channelId) return;
return this.client.channels.fetch(this.channelId, force);
}
async setMute(mute = !this.mute, reason?: string) {
return this.client.members.edit(this.guildId, this.userId, { mute }, reason).then(member => {
this.mute = mute;
return member;
});
}
async setDeaf(deaf = !this.deaf, reason?: string) {
return this.client.members.edit(this.guildId, this.userId, { deaf }, reason).then(member => {
this.deaf = deaf;
return member;
});
}
async disconnect(reason?: string) {
return this.setChannel(null, reason);
}
async setChannel(channel_id: null | string, reason?: string) {
return this.client.members.edit(this.guildId, this.userId, { channel_id }, reason).then(member => {
this.channelId = channel_id;
return member;
});
}
}

View File

@ -20,6 +20,7 @@ import {
type RESTPatchAPIGuildChannelPositionsJSONBody,
type RESTPostAPIChannelWebhookJSONBody,
type RESTPostAPIGuildChannelJSONBody,
type RESTPostAPIGuildForumThreadsJSONBody,
type SortOrderType,
type ThreadAutoArchiveDuration,
} from 'discord-api-types/v10';
@ -326,10 +327,15 @@ export class ThreadOnlyMethods extends DiscordBase {
setThreadRateLimit(rate: number, reason?: string) {
return this.edit({ default_thread_rate_limit_per_user: rate }, reason);
}
async thread(body: RESTPostAPIGuildForumThreadsJSONBody, reason?: string) {
return this.client.channels.thread(this.id, body, reason);
}
}
export interface VoiceChannelMethods extends BaseChannel<ChannelType> {}
export class VoiceChannelMethods extends DiscordBase {
guildId?: string;
setBitrate(bitrate: number | null, reason?: string) {
return this.edit({ bitrate }, reason);
}
@ -345,6 +351,14 @@ export class VoiceChannelMethods extends DiscordBase {
setVideoQuality(quality: keyof typeof VideoQualityMode, reason?: string) {
return this.edit({ video_quality_mode: VideoQualityMode[quality] }, reason);
}
async states() {
if (!this.guildId) return [];
const states = await this.cache.voiceStates?.values(this.guildId);
if (!states?.length) return [];
const filter = states.filter(state => state.channelId === this.id);
return filter;
}
}
export class WebhookGuildMethods extends DiscordBase {

View File

@ -11,5 +11,6 @@ export * from './Interaction';
export * from './Message';
export * from './Sticker';
export * from './User';
export * from './VoiceState';
export * from './Webhook';
export * from './channels';