mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-14 02:23:31 +01:00
feat: @discordjs/structures (#10900)
* chore: init /structures * feat: base structure * feat: initial structures design attempt * refactor(Structure): use unknown to store in kData * feat(Structure): add Invite refactor(Structure): patch to _patch * refactor: symbol names and override location * fix: don't possibly return 0 if discord borks Co-authored-by: Synbulat Biishev <signin@syjalo.dev> * refactor: use getter value instead of api Co-authored-by: Synbulat Biishev <signin@syjalo.dev> * refactor: cache createdTimestamp value Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com> * docs: better docs for what's done so far * feat: add Mixin * refactor(User): remove bitfield getters and add displayName * feat(structures): add Connection * feat(structures): add Channel base * refactor(Mixin): trace prototype chain, allow construction * fix(structures): fix mixin behavior * fix(structures): data optimization call behavior from perf testing * feat: channel mixins * chore: update deps * feat: channels and mixins * chore: more typeguard tests * fix: tests and some other issues * feat: add ChannelWebhookMixin * fix: more tests * chore: tests and docs * chore: docs * fix: remove unneccessary omitted * chore: apply code suggestions * refactor: change how extended invite works * fix: type imports * Apply suggestions from code review Co-authored-by: Almeida <github@almeidx.dev> * fix: tests * chore: add jsdoc * refactor: apply code suggestions * fix: don't instantiate sub-structures * fix: don't do null default twice * chore: use formatters, add _cache * chore: lockfile * chore: move MixinTypes to declaratiion file * fix: tests * fix: don't include source d.ts files for docs * feat: bitfields * feat: more bitfields * refactor: remove DirectoryChannel structure * chore: apply suggestions from code review * chore: remove unused import * refactor: use symbol for mixin toJSON, remove _ prefix * chore: apply suggestions from code review * refactor: remove bitfield casts * refactor: remove special case for threadchannel types * fix: apply code review suggestions * refactor: bitfields always store bigint * fix: tests * chore: apply suggestions from code review * fix: lint * refactor: conditional structuredClone * Apply suggestions from code review Co-authored-by: ckohen <chaikohen@gmail.com> * fix: code review errors * fix: lint * chore: bump dtypes * Update packages/structures/cliff.toml Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> * docs: link to VideoQualityMode * chore: typo in comment * chore: small nits in docs links * chore: small nits * docs: forgot one * chore: update template * chore: typos and things * chore: apply suggestions from code review * fix: tests and typeguards * chore: don't clone appliedTags * refactor: use a symbol for patch method * fix: add missing readonly * chore: remove todo comment * refactor: use symbol for clone * fix: add constraint to DataType * chore: apply suggestions * fix: dtypes bump * chore: fix comment * chore: add todo comment * chore: mark bitfield as todo chore: mark bit field as todo and edit readme --------- Co-authored-by: ckohen <chaikohen@gmail.com> Co-authored-by: Synbulat Biishev <signin@syjalo.dev> Co-authored-by: Almeida <github@almeidx.dev> Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
46
packages/structures/src/channels/AnnouncementChannel.ts
Normal file
46
packages/structures/src/channels/AnnouncementChannel.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { APINewsChannel, ChannelType } from 'discord-api-types/v10';
|
||||
import { Mixin } from '../Mixin.js';
|
||||
import type { MixinTypes } from '../MixinTypes.d.ts';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import { Channel } from './Channel.js';
|
||||
import { ChannelParentMixin } from './mixins/ChannelParentMixin.js';
|
||||
import { ChannelPermissionMixin } from './mixins/ChannelPermissionMixin.js';
|
||||
import { ChannelPinMixin } from './mixins/ChannelPinMixin.js';
|
||||
import { ChannelSlowmodeMixin } from './mixins/ChannelSlowmodeMixin.js';
|
||||
import { ChannelTopicMixin } from './mixins/ChannelTopicMixin.js';
|
||||
import { TextChannelMixin } from './mixins/TextChannelMixin.js';
|
||||
|
||||
export interface AnnouncementChannel<Omitted extends keyof APINewsChannel | '' = ''>
|
||||
extends MixinTypes<
|
||||
Channel<ChannelType.GuildAnnouncement>,
|
||||
[
|
||||
TextChannelMixin<ChannelType.GuildAnnouncement>,
|
||||
ChannelParentMixin<ChannelType.GuildAnnouncement>,
|
||||
ChannelPermissionMixin<ChannelType.GuildAnnouncement>,
|
||||
ChannelPinMixin<ChannelType.GuildAnnouncement>,
|
||||
ChannelSlowmodeMixin<ChannelType.GuildAnnouncement>,
|
||||
ChannelTopicMixin<ChannelType.GuildAnnouncement>,
|
||||
]
|
||||
> {}
|
||||
|
||||
/**
|
||||
* Sample Implementation of a structure for announcement channels, usable by direct end consumers.
|
||||
*/
|
||||
export class AnnouncementChannel<Omitted extends keyof APINewsChannel | '' = ''> extends Channel<
|
||||
ChannelType.GuildAnnouncement,
|
||||
Omitted
|
||||
> {
|
||||
public constructor(data: Partialize<APINewsChannel, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
Mixin(AnnouncementChannel, [
|
||||
TextChannelMixin,
|
||||
ChannelParentMixin,
|
||||
ChannelPermissionMixin,
|
||||
ChannelPinMixin,
|
||||
ChannelSlowmodeMixin,
|
||||
ChannelTopicMixin,
|
||||
]);
|
||||
@@ -0,0 +1,49 @@
|
||||
import type { APIAnnouncementThreadChannel, ChannelType } from 'discord-api-types/v10';
|
||||
import { Mixin } from '../Mixin.js';
|
||||
import type { MixinTypes } from '../MixinTypes.d.ts';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import { Channel } from './Channel.js';
|
||||
import { ChannelOwnerMixin } from './mixins/ChannelOwnerMixin.js';
|
||||
import { ChannelParentMixin } from './mixins/ChannelParentMixin.js';
|
||||
import { ChannelPinMixin } from './mixins/ChannelPinMixin.js';
|
||||
import { ChannelSlowmodeMixin } from './mixins/ChannelSlowmodeMixin.js';
|
||||
import { GuildChannelMixin } from './mixins/GuildChannelMixin.js';
|
||||
import { TextChannelMixin } from './mixins/TextChannelMixin.js';
|
||||
import { ThreadChannelMixin } from './mixins/ThreadChannelMixin.js';
|
||||
|
||||
export interface AnnouncementThreadChannel<Omitted extends keyof APIAnnouncementThreadChannel | '' = ''>
|
||||
extends MixinTypes<
|
||||
Channel<ChannelType.AnnouncementThread>,
|
||||
[
|
||||
TextChannelMixin<ChannelType.AnnouncementThread>,
|
||||
ChannelOwnerMixin<ChannelType.AnnouncementThread>,
|
||||
ChannelParentMixin<ChannelType.AnnouncementThread>,
|
||||
ChannelPinMixin<ChannelType.AnnouncementThread>,
|
||||
ChannelSlowmodeMixin<ChannelType.AnnouncementThread>,
|
||||
GuildChannelMixin<ChannelType.AnnouncementThread>,
|
||||
ThreadChannelMixin<ChannelType.AnnouncementThread>,
|
||||
]
|
||||
> {}
|
||||
|
||||
/**
|
||||
* Sample Implementation of a structure for announcement threads, usable by direct end consumers.
|
||||
*/
|
||||
export class AnnouncementThreadChannel<Omitted extends keyof APIAnnouncementThreadChannel | '' = ''> extends Channel<
|
||||
ChannelType.AnnouncementThread,
|
||||
Omitted
|
||||
> {
|
||||
public constructor(data: Partialize<APIAnnouncementThreadChannel, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData?.(data);
|
||||
}
|
||||
}
|
||||
|
||||
Mixin(AnnouncementThreadChannel, [
|
||||
TextChannelMixin,
|
||||
ChannelOwnerMixin,
|
||||
ChannelParentMixin,
|
||||
ChannelPinMixin,
|
||||
ChannelSlowmodeMixin,
|
||||
GuildChannelMixin,
|
||||
ThreadChannelMixin,
|
||||
]);
|
||||
28
packages/structures/src/channels/CategoryChannel.ts
Normal file
28
packages/structures/src/channels/CategoryChannel.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { APIGuildCategoryChannel, ChannelType } from 'discord-api-types/v10';
|
||||
import { Mixin } from '../Mixin.js';
|
||||
import type { MixinTypes } from '../MixinTypes.d.ts';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import { Channel } from './Channel.js';
|
||||
import { ChannelPermissionMixin } from './mixins/ChannelPermissionMixin.js';
|
||||
import { GuildChannelMixin } from './mixins/GuildChannelMixin.js';
|
||||
|
||||
export interface CategoryChannel<Omitted extends keyof APIGuildCategoryChannel | '' = ''>
|
||||
extends MixinTypes<
|
||||
Channel<ChannelType.GuildCategory>,
|
||||
[ChannelPermissionMixin<ChannelType.GuildCategory>, GuildChannelMixin<ChannelType.GuildCategory>]
|
||||
> {}
|
||||
|
||||
/**
|
||||
* Sample Implementation of a structure for category channels, usable by direct end consumers.
|
||||
*/
|
||||
export class CategoryChannel<Omitted extends keyof APIGuildCategoryChannel | '' = ''> extends Channel<
|
||||
ChannelType.GuildCategory,
|
||||
Omitted
|
||||
> {
|
||||
public constructor(data: Partialize<APIGuildCategoryChannel, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
Mixin(CategoryChannel, [ChannelPermissionMixin, GuildChannelMixin]);
|
||||
185
packages/structures/src/channels/Channel.ts
Normal file
185
packages/structures/src/channels/Channel.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
import { DiscordSnowflake } from '@sapphire/snowflake';
|
||||
import type { APIChannel, APIPartialChannel, ChannelType, ChannelFlags } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { ChannelFlagsBitField } from '../bitfields/ChannelFlagsBitField.js';
|
||||
import { kData, kPatch } from '../utils/symbols.js';
|
||||
import { isIdSet } from '../utils/type-guards.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import type { ChannelPermissionMixin } from './mixins/ChannelPermissionMixin.js';
|
||||
import type { ChannelWebhookMixin } from './mixins/ChannelWebhookMixin.js';
|
||||
import type { DMChannelMixin } from './mixins/DMChannelMixin.js';
|
||||
import type { GuildChannelMixin } from './mixins/GuildChannelMixin.js';
|
||||
import type { TextChannelMixin } from './mixins/TextChannelMixin.js';
|
||||
import type { ThreadChannelMixin } from './mixins/ThreadChannelMixin.js';
|
||||
import type { ThreadOnlyChannelMixin } from './mixins/ThreadOnlyChannelMixin.js';
|
||||
import type { VoiceChannelMixin } from './mixins/VoiceChannelMixin.js';
|
||||
|
||||
export type PartialChannel = Channel<ChannelType, Exclude<keyof APIChannel, keyof APIPartialChannel>>;
|
||||
|
||||
/**
|
||||
* The data stored by a {@link Channel} structure based on its {@link (Channel:class)."type"} property.
|
||||
*/
|
||||
export type ChannelDataType<Type extends ChannelType | 'unknown'> = Type extends ChannelType
|
||||
? Extract<APIChannel, { type: Type }>
|
||||
: APIPartialChannel;
|
||||
|
||||
/**
|
||||
* Represents any channel on Discord.
|
||||
*
|
||||
* @typeParam Type - Specify the type of the channel being constructed for more accurate data types
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks Although this class _can_ be instantiated directly for any channel type,
|
||||
* it's intended to be subclassed with the appropriate mixins for each channel type.
|
||||
*/
|
||||
export class Channel<
|
||||
Type extends ChannelType | 'unknown' = ChannelType,
|
||||
Omitted extends keyof ChannelDataType<Type> | '' = '',
|
||||
> extends Structure<ChannelDataType<Type>, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each Channel.
|
||||
*
|
||||
* @remarks This template is only guaranteed to apply to channels constructed directly via `new Channel()`.
|
||||
* Use the appropriate subclass template to remove data from that channel type.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIChannel> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the channel
|
||||
*/
|
||||
public constructor(data: Partialize<ChannelDataType<Type>, Omitted>) {
|
||||
super(data as ChannelDataType<Type>);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.[kPatch]}
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public override [kPatch](data: Partial<ChannelDataType<Type>>) {
|
||||
return super[kPatch](data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the channel
|
||||
*/
|
||||
public get id() {
|
||||
return this[kData].id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the channel
|
||||
*/
|
||||
public get type() {
|
||||
// This cast can be incorrect when type is omitted and if the wrong type of channel was constructed
|
||||
return this[kData].type as Type extends 'unknown' ? number : Type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the channel, null for DMs
|
||||
*
|
||||
* @privateRemarks The type of `name` can be narrowed in Guild Channels and DM channels to string and null respectively,
|
||||
* respecting Omit behaviors
|
||||
*/
|
||||
public get name() {
|
||||
return this[kData].name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The flags that are applied to the channel.
|
||||
*
|
||||
* @privateRemarks The type of `flags` can be narrowed in Guild Channels and DMChannel to ChannelFlags, and in GroupDM channel
|
||||
* to null, respecting Omit behaviors
|
||||
*/
|
||||
public get flags() {
|
||||
const flags =
|
||||
'flags' in this[kData] && typeof this[kData].flags === 'number' ? (this[kData].flags as ChannelFlags) : null;
|
||||
return flags ? new ChannelFlagsBitField(flags) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The timestamp the channel was created at
|
||||
*/
|
||||
public get createdTimestamp() {
|
||||
return isIdSet(this.id) ? DiscordSnowflake.timestampFrom(this.id) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time the channel was created at
|
||||
*/
|
||||
public get createdAt() {
|
||||
const createdTimestamp = this.createdTimestamp;
|
||||
return createdTimestamp ? new Date(createdTimestamp) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this channel is a thread channel
|
||||
*
|
||||
* @privateRemarks Overridden to `true` on `ThreadChannelMixin`
|
||||
*/
|
||||
public isThread(): this is ThreadChannelMixin & this {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this channel can contain messages
|
||||
*
|
||||
* @privateRemarks Overridden to `true` on `TextChannelMixin`
|
||||
*/
|
||||
public isTextBased(): this is TextChannelMixin & this {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this channel is in a guild
|
||||
*
|
||||
* @privateRemarks Overridden to `true` on `GuildChannelMixin`
|
||||
*/
|
||||
public isGuildBased(): this is GuildChannelMixin & this {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this channel is a DM or DM Group
|
||||
*
|
||||
* @privateRemarks Overridden to `true` on `DMChannelMixin`
|
||||
*/
|
||||
public isDMBased(): this is DMChannelMixin & this {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this channel has voice connection capabilities
|
||||
*
|
||||
* @privateRemarks Overridden to `true` on `VoiceChannelMixin`
|
||||
*/
|
||||
public isVoiceBased(): this is VoiceChannelMixin & this {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this channel only allows thread creation
|
||||
*
|
||||
* @privateRemarks Overridden to `true` on `ThreadOnlyChannelMixin`
|
||||
*/
|
||||
public isThreadOnly(): this is ThreadOnlyChannelMixin & this {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this channel can have permission overwrites
|
||||
*
|
||||
* @privateRemarks Overridden to `true` on `ChannelPermissionsMixin`
|
||||
*/
|
||||
public isPermissionCapable(): this is ChannelPermissionMixin & this {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this channel can have webhooks
|
||||
*
|
||||
* @privateRemarks Overridden to `true` on `ChannelWebhooksMixin`
|
||||
*/
|
||||
public isWebhookCapable(): this is ChannelWebhookMixin & this {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
26
packages/structures/src/channels/DMChannel.ts
Normal file
26
packages/structures/src/channels/DMChannel.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { APIDMChannel, ChannelType } from 'discord-api-types/v10';
|
||||
import { Mixin } from '../Mixin.js';
|
||||
import type { MixinTypes } from '../MixinTypes.d.ts';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import { Channel } from './Channel.js';
|
||||
import { ChannelPinMixin } from './mixins/ChannelPinMixin.js';
|
||||
import { DMChannelMixin } from './mixins/DMChannelMixin.js';
|
||||
import { TextChannelMixin } from './mixins/TextChannelMixin.js';
|
||||
|
||||
export interface DMChannel<Omitted extends keyof APIDMChannel | '' = ''>
|
||||
extends MixinTypes<
|
||||
Channel<ChannelType.DM>,
|
||||
[DMChannelMixin<ChannelType.DM>, TextChannelMixin<ChannelType.DM>, ChannelPinMixin<ChannelType.DM>]
|
||||
> {}
|
||||
|
||||
/**
|
||||
* Sample Implementation of a structure for dm channels, usable by direct end consumers.
|
||||
*/
|
||||
export class DMChannel<Omitted extends keyof APIDMChannel | '' = ''> extends Channel<ChannelType.DM, Omitted> {
|
||||
public constructor(data: Partialize<APIDMChannel, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
Mixin(DMChannel, [DMChannelMixin, TextChannelMixin, ChannelPinMixin]);
|
||||
44
packages/structures/src/channels/ForumChannel.ts
Normal file
44
packages/structures/src/channels/ForumChannel.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import type { APIGuildForumChannel, ChannelType } from 'discord-api-types/v10';
|
||||
import { Mixin } from '../Mixin.js';
|
||||
import type { MixinTypes } from '../MixinTypes.d.ts';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import { Channel } from './Channel.js';
|
||||
import { ChannelParentMixin } from './mixins/ChannelParentMixin.js';
|
||||
import { ChannelPermissionMixin } from './mixins/ChannelPermissionMixin.js';
|
||||
import { ChannelTopicMixin } from './mixins/ChannelTopicMixin.js';
|
||||
import { ThreadOnlyChannelMixin } from './mixins/ThreadOnlyChannelMixin.js';
|
||||
|
||||
export interface ForumChannel<Omitted extends keyof APIGuildForumChannel | '' = ''>
|
||||
extends MixinTypes<
|
||||
Channel<ChannelType.GuildForum>,
|
||||
[
|
||||
ChannelParentMixin<ChannelType.GuildForum>,
|
||||
ChannelPermissionMixin<ChannelType.GuildForum>,
|
||||
ChannelTopicMixin<ChannelType.GuildForum>,
|
||||
ThreadOnlyChannelMixin<ChannelType.GuildForum>,
|
||||
]
|
||||
> {}
|
||||
|
||||
/**
|
||||
* Sample Implementation of a structure for forum channels, usable by direct end consumers.
|
||||
*/
|
||||
export class ForumChannel<Omitted extends keyof APIGuildForumChannel | '' = ''> extends Channel<
|
||||
ChannelType.GuildForum,
|
||||
Omitted
|
||||
> {
|
||||
public constructor(data: Partialize<APIGuildForumChannel, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The default forum layout view used to display posts in this channel.
|
||||
* Defaults to 0, which indicates a layout view has not been set by a channel admin.
|
||||
*/
|
||||
public get defaultForumLayout() {
|
||||
return this[kData].default_forum_layout;
|
||||
}
|
||||
}
|
||||
|
||||
Mixin(ForumChannel, [ChannelParentMixin, ChannelPermissionMixin, ChannelTopicMixin, ThreadOnlyChannelMixin]);
|
||||
57
packages/structures/src/channels/ForumTag.ts
Normal file
57
packages/structures/src/channels/ForumTag.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import type { APIGuildForumTag } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents metadata of a thread channel on Discord.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class ForumTag<Omitted extends keyof APIGuildForumTag | '' = ''> extends Structure<APIGuildForumTag, Omitted> {
|
||||
public constructor(data: Partialize<APIGuildForumTag, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the tag.
|
||||
*/
|
||||
public get id() {
|
||||
return this[kData].id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the tag.
|
||||
*/
|
||||
public get name() {
|
||||
return this[kData].name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this tag can only be added to or removed from threads by a member with the {@link discord-api-types/v10#(PermissionFlagsBits:variable) | ManageThreads} permission.
|
||||
*/
|
||||
public get moderated() {
|
||||
return this[kData].moderated;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of a guild's custom emoji.
|
||||
*/
|
||||
public get emojiId() {
|
||||
return this[kData].emoji_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The unicode character of the emoji.
|
||||
*/
|
||||
public get emojiName() {
|
||||
return this[kData].emoji_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The textual representation of this tag's emoji. Either a unicode character or a guild emoji mention.
|
||||
*/
|
||||
public get emoji() {
|
||||
return this.emojiName ?? `<:_:${this.emojiId}>`;
|
||||
}
|
||||
}
|
||||
35
packages/structures/src/channels/GroupDMChannel.ts
Normal file
35
packages/structures/src/channels/GroupDMChannel.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import type { APIGroupDMChannel, ChannelType } from 'discord-api-types/v10';
|
||||
import { Mixin } from '../Mixin.js';
|
||||
import type { MixinTypes } from '../MixinTypes.d.ts';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import { Channel } from './Channel.js';
|
||||
import { ChannelOwnerMixin } from './mixins/ChannelOwnerMixin.js';
|
||||
import { DMChannelMixin } from './mixins/DMChannelMixin.js';
|
||||
import { GroupDMMixin } from './mixins/GroupDMMixin.js';
|
||||
import { TextChannelMixin } from './mixins/TextChannelMixin.js';
|
||||
|
||||
export interface GroupDMChannel<Omitted extends keyof APIGroupDMChannel | '' = ''>
|
||||
extends MixinTypes<
|
||||
Channel<ChannelType.GroupDM>,
|
||||
[
|
||||
DMChannelMixin<ChannelType.GroupDM>,
|
||||
TextChannelMixin<ChannelType.GroupDM>,
|
||||
ChannelOwnerMixin<ChannelType.GroupDM>,
|
||||
GroupDMMixin,
|
||||
]
|
||||
> {}
|
||||
|
||||
/**
|
||||
* Sample Implementation of a structure for group dm channels, usable by direct end consumers.
|
||||
*/
|
||||
export class GroupDMChannel<Omitted extends keyof APIGroupDMChannel | '' = ''> extends Channel<
|
||||
ChannelType.GroupDM,
|
||||
Omitted
|
||||
> {
|
||||
public constructor(data: Partialize<APIGroupDMChannel, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
Mixin(GroupDMChannel, [DMChannelMixin, TextChannelMixin, ChannelOwnerMixin, GroupDMMixin]);
|
||||
35
packages/structures/src/channels/MediaChannel.ts
Normal file
35
packages/structures/src/channels/MediaChannel.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import type { APIGuildMediaChannel, ChannelType } from 'discord-api-types/v10';
|
||||
import { Mixin } from '../Mixin.js';
|
||||
import type { MixinTypes } from '../MixinTypes.d.ts';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import { Channel } from './Channel.js';
|
||||
import { ChannelParentMixin } from './mixins/ChannelParentMixin.js';
|
||||
import { ChannelPermissionMixin } from './mixins/ChannelPermissionMixin.js';
|
||||
import { ChannelTopicMixin } from './mixins/ChannelTopicMixin.js';
|
||||
import { ThreadOnlyChannelMixin } from './mixins/ThreadOnlyChannelMixin.js';
|
||||
|
||||
export interface MediaChannel<Omitted extends keyof APIGuildMediaChannel | '' = ''>
|
||||
extends MixinTypes<
|
||||
Channel<ChannelType.GuildMedia>,
|
||||
[
|
||||
ChannelParentMixin<ChannelType.GuildMedia>,
|
||||
ChannelPermissionMixin<ChannelType.GuildMedia>,
|
||||
ChannelTopicMixin<ChannelType.GuildMedia>,
|
||||
ThreadOnlyChannelMixin<ChannelType.GuildMedia>,
|
||||
]
|
||||
> {}
|
||||
|
||||
/**
|
||||
* Sample Implementation of a structure for media channels, usable by direct end consumers.
|
||||
*/
|
||||
export class MediaChannel<Omitted extends keyof APIGuildMediaChannel | '' = ''> extends Channel<
|
||||
ChannelType.GuildMedia,
|
||||
Omitted
|
||||
> {
|
||||
public constructor(data: Partialize<APIGuildMediaChannel, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
Mixin(MediaChannel, [ChannelParentMixin, ChannelPermissionMixin, ChannelTopicMixin, ThreadOnlyChannelMixin]);
|
||||
94
packages/structures/src/channels/PermissionOverwrite.ts
Normal file
94
packages/structures/src/channels/PermissionOverwrite.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import type { APIOverwrite } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { PermissionsBitField } from '../bitfields/PermissionsBitField.js';
|
||||
import { kAllow, kData, kDeny } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents metadata of a thread channel on Discord.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class PermissionOverwrite<Omitted extends keyof APIOverwrite | '' = 'allow' | 'deny'> extends Structure<
|
||||
APIOverwrite,
|
||||
Omitted
|
||||
> {
|
||||
protected [kAllow]: bigint | null = null;
|
||||
|
||||
protected [kDeny]: bigint | null = null;
|
||||
|
||||
public constructor(data: Partialize<APIOverwrite, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each ThreadMetadata
|
||||
*
|
||||
* @remarks This template has defaults, if you want to remove additional data and keep the defaults,
|
||||
* use `Object.defineProperties`. To override the defaults, set this value directly.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIOverwrite> = {
|
||||
set allow(_: string) {},
|
||||
set deny(_: string) {},
|
||||
};
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.optimizeData}
|
||||
*/
|
||||
protected override optimizeData(data: Partial<APIOverwrite>) {
|
||||
if (data.allow) {
|
||||
this[kAllow] = BigInt(data.allow);
|
||||
}
|
||||
|
||||
if (data.deny) {
|
||||
this[kDeny] = BigInt(data.deny);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The permission bit set allowed by this overwrite.
|
||||
*/
|
||||
public get allow() {
|
||||
const allow = this[kAllow];
|
||||
return typeof allow === 'bigint' ? new PermissionsBitField(allow) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The permission bit set denied by this overwrite.
|
||||
*/
|
||||
public get deny() {
|
||||
const deny = this[kDeny];
|
||||
return typeof deny === 'bigint' ? new PermissionsBitField(deny) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The role or user id for this overwrite.
|
||||
*/
|
||||
public get id() {
|
||||
return this[kData].id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of this overwrite.
|
||||
*/
|
||||
public get type() {
|
||||
return this[kData].type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.toJSON}
|
||||
*/
|
||||
public override toJSON() {
|
||||
const clone = super.toJSON();
|
||||
if (this[kAllow]) {
|
||||
clone.allow = this[kAllow].toString();
|
||||
}
|
||||
|
||||
if (this[kDeny]) {
|
||||
clone.deny = this[kDeny].toString();
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
46
packages/structures/src/channels/PrivateThreadChannel.ts
Normal file
46
packages/structures/src/channels/PrivateThreadChannel.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { APIPrivateThreadChannel, ChannelType } from 'discord-api-types/v10';
|
||||
import { Mixin } from '../Mixin.js';
|
||||
import type { MixinTypes } from '../MixinTypes.d.ts';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import { Channel } from './Channel.js';
|
||||
import { ChannelOwnerMixin } from './mixins/ChannelOwnerMixin.js';
|
||||
import { ChannelParentMixin } from './mixins/ChannelParentMixin.js';
|
||||
import { ChannelPinMixin } from './mixins/ChannelPinMixin.js';
|
||||
import { ChannelSlowmodeMixin } from './mixins/ChannelSlowmodeMixin.js';
|
||||
import { TextChannelMixin } from './mixins/TextChannelMixin.js';
|
||||
import { ThreadChannelMixin } from './mixins/ThreadChannelMixin.js';
|
||||
|
||||
export interface PrivateThreadChannel<Omitted extends keyof APIPrivateThreadChannel | '' = ''>
|
||||
extends MixinTypes<
|
||||
Channel<ChannelType.PrivateThread>,
|
||||
[
|
||||
TextChannelMixin<ChannelType.PrivateThread>,
|
||||
ChannelOwnerMixin<ChannelType.PrivateThread>,
|
||||
ChannelParentMixin<ChannelType.PrivateThread>,
|
||||
ChannelPinMixin<ChannelType.PrivateThread>,
|
||||
ChannelSlowmodeMixin<ChannelType.PrivateThread>,
|
||||
ThreadChannelMixin<ChannelType.PrivateThread>,
|
||||
]
|
||||
> {}
|
||||
|
||||
/**
|
||||
* Sample Implementation of a structure for private thread channels, usable by direct end consumers.
|
||||
*/
|
||||
export class PrivateThreadChannel<Omitted extends keyof APIPrivateThreadChannel | '' = ''> extends Channel<
|
||||
ChannelType.PrivateThread,
|
||||
Omitted
|
||||
> {
|
||||
public constructor(data: Partialize<APIPrivateThreadChannel, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
Mixin(PrivateThreadChannel, [
|
||||
TextChannelMixin,
|
||||
ChannelOwnerMixin,
|
||||
ChannelParentMixin,
|
||||
ChannelPinMixin,
|
||||
ChannelSlowmodeMixin,
|
||||
ThreadChannelMixin,
|
||||
]);
|
||||
49
packages/structures/src/channels/PublicThreadChannel.ts
Normal file
49
packages/structures/src/channels/PublicThreadChannel.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { APIPublicThreadChannel, ChannelType } from 'discord-api-types/v10';
|
||||
import { Mixin } from '../Mixin.js';
|
||||
import type { MixinTypes } from '../MixinTypes.d.ts';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import { Channel } from './Channel.js';
|
||||
import { AppliedTagsMixin } from './mixins/AppliedTagsMixin.js';
|
||||
import { ChannelOwnerMixin } from './mixins/ChannelOwnerMixin.js';
|
||||
import { ChannelParentMixin } from './mixins/ChannelParentMixin.js';
|
||||
import { ChannelPinMixin } from './mixins/ChannelPinMixin.js';
|
||||
import { ChannelSlowmodeMixin } from './mixins/ChannelSlowmodeMixin.js';
|
||||
import { TextChannelMixin } from './mixins/TextChannelMixin.js';
|
||||
import { ThreadChannelMixin } from './mixins/ThreadChannelMixin.js';
|
||||
|
||||
export interface PublicThreadChannel<Omitted extends keyof APIPublicThreadChannel | '' = ''>
|
||||
extends MixinTypes<
|
||||
Channel<ChannelType.PublicThread>,
|
||||
[
|
||||
TextChannelMixin<ChannelType.PublicThread>,
|
||||
ChannelOwnerMixin<ChannelType.PublicThread>,
|
||||
ChannelParentMixin<ChannelType.PublicThread>,
|
||||
ChannelPinMixin<ChannelType.PublicThread>,
|
||||
ChannelSlowmodeMixin<ChannelType.PublicThread>,
|
||||
ThreadChannelMixin<ChannelType.PublicThread>,
|
||||
AppliedTagsMixin,
|
||||
]
|
||||
> {}
|
||||
|
||||
/**
|
||||
* Sample Implementation of a structure for public thread channels, usable by direct end consumers.
|
||||
*/
|
||||
export class PublicThreadChannel<Omitted extends keyof APIPublicThreadChannel | '' = ''> extends Channel<
|
||||
ChannelType.PublicThread,
|
||||
Omitted
|
||||
> {
|
||||
public constructor(data: Partialize<APIPublicThreadChannel, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
Mixin(PublicThreadChannel, [
|
||||
TextChannelMixin,
|
||||
ChannelOwnerMixin,
|
||||
ChannelParentMixin,
|
||||
ChannelPinMixin,
|
||||
ChannelSlowmodeMixin,
|
||||
ThreadChannelMixin,
|
||||
AppliedTagsMixin,
|
||||
]);
|
||||
40
packages/structures/src/channels/StageChannel.ts
Normal file
40
packages/structures/src/channels/StageChannel.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { APIGuildStageVoiceChannel, ChannelType } from 'discord-api-types/v10';
|
||||
import { Mixin } from '../Mixin.js';
|
||||
import type { MixinTypes } from '../MixinTypes.d.ts';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import { Channel } from './Channel.js';
|
||||
import { ChannelParentMixin } from './mixins/ChannelParentMixin.js';
|
||||
import { ChannelPermissionMixin } from './mixins/ChannelPermissionMixin.js';
|
||||
import { ChannelSlowmodeMixin } from './mixins/ChannelSlowmodeMixin.js';
|
||||
import { ChannelWebhookMixin } from './mixins/ChannelWebhookMixin.js';
|
||||
import { VoiceChannelMixin } from './mixins/VoiceChannelMixin.js';
|
||||
|
||||
export interface StageChannel<Omitted extends keyof APIGuildStageVoiceChannel | '' = ''>
|
||||
extends MixinTypes<
|
||||
Channel<ChannelType.GuildStageVoice>,
|
||||
[
|
||||
ChannelParentMixin<ChannelType.GuildStageVoice>,
|
||||
ChannelPermissionMixin<ChannelType.GuildStageVoice>,
|
||||
ChannelSlowmodeMixin<ChannelType.GuildStageVoice>,
|
||||
ChannelWebhookMixin<ChannelType.GuildStageVoice>,
|
||||
VoiceChannelMixin<ChannelType.GuildStageVoice>,
|
||||
]
|
||||
> {}
|
||||
|
||||
export class StageChannel<Omitted extends keyof APIGuildStageVoiceChannel | '' = ''> extends Channel<
|
||||
ChannelType.GuildStageVoice,
|
||||
Omitted
|
||||
> {
|
||||
public constructor(data: Partialize<APIGuildStageVoiceChannel, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
Mixin(StageChannel, [
|
||||
ChannelParentMixin,
|
||||
ChannelPermissionMixin,
|
||||
ChannelSlowmodeMixin,
|
||||
ChannelWebhookMixin,
|
||||
VoiceChannelMixin,
|
||||
]);
|
||||
43
packages/structures/src/channels/TextChannel.ts
Normal file
43
packages/structures/src/channels/TextChannel.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import type { APITextChannel, ChannelType } from 'discord-api-types/v10';
|
||||
import { Mixin } from '../Mixin.js';
|
||||
import type { MixinTypes } from '../MixinTypes.d.ts';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import { Channel } from './Channel.js';
|
||||
import { ChannelParentMixin } from './mixins/ChannelParentMixin.js';
|
||||
import { ChannelPermissionMixin } from './mixins/ChannelPermissionMixin.js';
|
||||
import { ChannelPinMixin } from './mixins/ChannelPinMixin.js';
|
||||
import { ChannelSlowmodeMixin } from './mixins/ChannelSlowmodeMixin.js';
|
||||
import { ChannelTopicMixin } from './mixins/ChannelTopicMixin.js';
|
||||
import { TextChannelMixin } from './mixins/TextChannelMixin.js';
|
||||
|
||||
export interface TextChannel<Omitted extends keyof APITextChannel | '' = ''>
|
||||
extends MixinTypes<
|
||||
Channel<ChannelType.GuildText>,
|
||||
[
|
||||
TextChannelMixin<ChannelType.GuildText>,
|
||||
ChannelParentMixin<ChannelType.GuildText>,
|
||||
ChannelPermissionMixin<ChannelType.GuildText>,
|
||||
ChannelPinMixin<ChannelType.GuildText>,
|
||||
ChannelSlowmodeMixin<ChannelType.GuildText>,
|
||||
ChannelTopicMixin<ChannelType.GuildText>,
|
||||
]
|
||||
> {}
|
||||
|
||||
export class TextChannel<Omitted extends keyof APITextChannel | '' = ''> extends Channel<
|
||||
ChannelType.GuildText,
|
||||
Omitted
|
||||
> {
|
||||
public constructor(data: Partialize<APITextChannel, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
Mixin(TextChannel, [
|
||||
TextChannelMixin,
|
||||
ChannelParentMixin,
|
||||
ChannelPermissionMixin,
|
||||
ChannelPinMixin,
|
||||
ChannelSlowmodeMixin,
|
||||
ChannelTopicMixin,
|
||||
]);
|
||||
120
packages/structures/src/channels/ThreadMetadata.ts
Normal file
120
packages/structures/src/channels/ThreadMetadata.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import type { APIThreadMetadata } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kArchiveTimestamp, kCreatedTimestamp, kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents metadata of a thread channel on Discord.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class ThreadMetadata<
|
||||
Omitted extends keyof APIThreadMetadata | '' = 'archive_timestamp' | 'create_timestamp',
|
||||
> extends Structure<APIThreadMetadata, Omitted> {
|
||||
protected [kArchiveTimestamp]: number | null = null;
|
||||
|
||||
protected [kCreatedTimestamp]: number | null = null;
|
||||
|
||||
public constructor(data: Partialize<APIThreadMetadata, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each ThreadMetadata
|
||||
*
|
||||
* @remarks This template has defaults, if you want to remove additional data and keep the defaults,
|
||||
* use `Object.defineProperties`. To override the defaults, set this value directly.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIThreadMetadata> = {
|
||||
set create_timestamp(_: string) {},
|
||||
set archive_timestamp(_: string) {},
|
||||
};
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.optimizeData}
|
||||
*/
|
||||
protected override optimizeData(data: Partial<APIThreadMetadata>) {
|
||||
if (data.create_timestamp) {
|
||||
this[kCreatedTimestamp] = Date.parse(data.create_timestamp);
|
||||
}
|
||||
|
||||
if (data.archive_timestamp) {
|
||||
this[kArchiveTimestamp] = Date.parse(data.archive_timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the thread is archived.
|
||||
*/
|
||||
public get archived() {
|
||||
return this[kData].archived;
|
||||
}
|
||||
|
||||
/**
|
||||
* The timestamp when the thread's archive status was last changed, used for calculating recent activity.
|
||||
*/
|
||||
public get archivedTimestamp() {
|
||||
return this[kArchiveTimestamp];
|
||||
}
|
||||
|
||||
/**
|
||||
* The timestamp when the thread was created; only populated for threads created after 2022-01-09.
|
||||
*/
|
||||
public get createdTimestamp() {
|
||||
return this[kCreatedTimestamp];
|
||||
}
|
||||
|
||||
/**
|
||||
* The thread will stop showing in the channel list after auto_archive_duration minutes of inactivity,
|
||||
*/
|
||||
public get autoArchiveDuration() {
|
||||
return this[kData].auto_archive_duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether non-moderators can add other non-moderators to a thread; only available on private threads.
|
||||
*/
|
||||
public get invitable() {
|
||||
return this[kData].invitable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the thread is locked; when a thread is locked, only users with {@link discord-api-types/v10#(PermissionFlagsBits:variable) | ManageThreads} can unarchive it.
|
||||
*/
|
||||
public get locked() {
|
||||
return this[kData].locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time the thread was archived at
|
||||
*/
|
||||
public get archivedAt() {
|
||||
const archivedTimestamp = this.archivedTimestamp;
|
||||
return archivedTimestamp ? new Date(archivedTimestamp) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time the thread was created at
|
||||
*/
|
||||
public get createdAt() {
|
||||
const createdTimestamp = this.createdTimestamp;
|
||||
return createdTimestamp ? new Date(createdTimestamp) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.toJSON}
|
||||
*/
|
||||
public override toJSON() {
|
||||
const data = super.toJSON();
|
||||
if (this[kArchiveTimestamp]) {
|
||||
data.archive_timestamp = new Date(this[kArchiveTimestamp]).toISOString();
|
||||
}
|
||||
|
||||
if (this[kCreatedTimestamp]) {
|
||||
data.create_timestamp = new Date(this[kCreatedTimestamp]).toISOString();
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
40
packages/structures/src/channels/VoiceChannel.ts
Normal file
40
packages/structures/src/channels/VoiceChannel.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { APIGuildVoiceChannel, ChannelType } from 'discord-api-types/v10';
|
||||
import { Mixin } from '../Mixin.js';
|
||||
import type { MixinTypes } from '../MixinTypes.d.ts';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import { Channel } from './Channel.js';
|
||||
import { ChannelParentMixin } from './mixins/ChannelParentMixin.js';
|
||||
import { ChannelPermissionMixin } from './mixins/ChannelPermissionMixin.js';
|
||||
import { ChannelSlowmodeMixin } from './mixins/ChannelSlowmodeMixin.js';
|
||||
import { ChannelWebhookMixin } from './mixins/ChannelWebhookMixin.js';
|
||||
import { VoiceChannelMixin } from './mixins/VoiceChannelMixin.js';
|
||||
|
||||
export interface VoiceChannel<Omitted extends keyof APIGuildVoiceChannel | '' = ''>
|
||||
extends MixinTypes<
|
||||
Channel<ChannelType.GuildVoice>,
|
||||
[
|
||||
ChannelParentMixin<ChannelType.GuildVoice>,
|
||||
ChannelPermissionMixin<ChannelType.GuildVoice>,
|
||||
ChannelSlowmodeMixin<ChannelType.GuildVoice>,
|
||||
ChannelWebhookMixin<ChannelType.GuildVoice>,
|
||||
VoiceChannelMixin<ChannelType.GuildVoice>,
|
||||
]
|
||||
> {}
|
||||
|
||||
export class VoiceChannel<Omitted extends keyof APIGuildVoiceChannel | '' = ''> extends Channel<
|
||||
ChannelType.GuildVoice,
|
||||
Omitted
|
||||
> {
|
||||
public constructor(data: Partialize<APIGuildVoiceChannel, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
}
|
||||
|
||||
Mixin(VoiceChannel, [
|
||||
ChannelParentMixin,
|
||||
ChannelPermissionMixin,
|
||||
ChannelSlowmodeMixin,
|
||||
ChannelWebhookMixin,
|
||||
VoiceChannelMixin,
|
||||
]);
|
||||
21
packages/structures/src/channels/index.ts
Normal file
21
packages/structures/src/channels/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export * from './mixins/index.js';
|
||||
|
||||
export * from './ForumTag.js';
|
||||
export * from './PermissionOverwrite.js';
|
||||
export * from './ThreadMetadata.js';
|
||||
|
||||
export * from './Channel.js';
|
||||
|
||||
export * from './AnnouncementChannel.js';
|
||||
export * from './AnnouncementThreadChannel.js';
|
||||
export * from './CategoryChannel.js';
|
||||
// export * from './DirectoryChannel.js';
|
||||
export * from './DMChannel.js';
|
||||
export * from './ForumChannel.js';
|
||||
export * from './GroupDMChannel.js';
|
||||
export * from './MediaChannel.js';
|
||||
export * from './PrivateThreadChannel.js';
|
||||
export * from './PublicThreadChannel.js';
|
||||
export * from './StageChannel.js';
|
||||
export * from './TextChannel.js';
|
||||
export * from './VoiceChannel.js';
|
||||
14
packages/structures/src/channels/mixins/AppliedTagsMixin.ts
Normal file
14
packages/structures/src/channels/mixins/AppliedTagsMixin.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { ChannelType } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Channel } from '../Channel.js';
|
||||
|
||||
export interface AppliedTagsMixin extends Channel<ChannelType.PublicThread> {}
|
||||
|
||||
export class AppliedTagsMixin {
|
||||
/**
|
||||
* The ids of the set of tags that have been applied to a thread in a {@link (ForumChannel:class)} or a {@link (MediaChannel:class)}.
|
||||
*/
|
||||
public get appliedTags(): readonly string[] | null {
|
||||
return Array.isArray(this[kData].applied_tags) ? this[kData].applied_tags : null;
|
||||
}
|
||||
}
|
||||
14
packages/structures/src/channels/mixins/ChannelOwnerMixin.ts
Normal file
14
packages/structures/src/channels/mixins/ChannelOwnerMixin.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { ChannelType, ThreadChannelType } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Channel } from '../Channel.js';
|
||||
|
||||
export interface ChannelOwnerMixin<Type extends ChannelType.GroupDM | ThreadChannelType> extends Channel<Type> {}
|
||||
|
||||
export class ChannelOwnerMixin<Type extends ChannelType.GroupDM | ThreadChannelType> {
|
||||
/**
|
||||
* The id of the creator of the group DM or thread
|
||||
*/
|
||||
public get ownerId() {
|
||||
return this[kData].owner_id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { ChannelType, GuildChannelType } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import { GuildChannelMixin } from './GuildChannelMixin.js';
|
||||
|
||||
export class ChannelParentMixin<
|
||||
Type extends Exclude<GuildChannelType, ChannelType.GuildCategory | ChannelType.GuildDirectory>,
|
||||
> extends GuildChannelMixin<Type> {
|
||||
/**
|
||||
* The id of the parent category for a channel (each parent category can contain up to 50 channels) or id of the parent channel for a thread
|
||||
*/
|
||||
public get parentId() {
|
||||
return this[kData].parent_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the channel is nsfw
|
||||
*/
|
||||
public get nsfw() {
|
||||
return this[kData].nsfw;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { ChannelType, GuildChannelType, ThreadChannelType } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Channel } from '../Channel.js';
|
||||
|
||||
export interface ChannelPermissionMixin<
|
||||
Type extends Exclude<GuildChannelType, ChannelType.GuildDirectory | ThreadChannelType> = Exclude<
|
||||
GuildChannelType,
|
||||
ChannelType.GuildDirectory | ThreadChannelType
|
||||
>,
|
||||
> extends Channel<Type> {}
|
||||
|
||||
/**
|
||||
* @remarks has an array of sub-structures {@link PermissionOverwrite} that extending mixins should add to their DataTemplate and _optimizeData
|
||||
*/
|
||||
export class ChannelPermissionMixin<
|
||||
Type extends Exclude<GuildChannelType, ChannelType.GuildDirectory | ThreadChannelType> = Exclude<
|
||||
GuildChannelType,
|
||||
ChannelType.GuildDirectory | ThreadChannelType
|
||||
>,
|
||||
> {
|
||||
/**
|
||||
* The sorting position of the channel
|
||||
*/
|
||||
public get position() {
|
||||
return this[kData].position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this channel can have permission overwrites
|
||||
*/
|
||||
public isPermissionCapable(): this is ChannelPermissionMixin & this {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
62
packages/structures/src/channels/mixins/ChannelPinMixin.ts
Normal file
62
packages/structures/src/channels/mixins/ChannelPinMixin.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import type { ChannelType, ThreadChannelType } from 'discord-api-types/v10';
|
||||
import { kLastPinTimestamp, kMixinConstruct, kMixinToJSON } from '../../utils/symbols.js';
|
||||
import type { Channel, ChannelDataType } from '../Channel.js';
|
||||
|
||||
export interface ChannelPinMixin<
|
||||
Type extends ChannelType.DM | ChannelType.GuildAnnouncement | ChannelType.GuildText | ThreadChannelType,
|
||||
> extends Channel<Type> {}
|
||||
|
||||
export class ChannelPinMixin<
|
||||
Type extends ChannelType.DM | ChannelType.GuildAnnouncement | ChannelType.GuildText | ThreadChannelType,
|
||||
> {
|
||||
/**
|
||||
* The timestamp of when the last pin in the channel happened
|
||||
*/
|
||||
declare protected [kLastPinTimestamp]: number | null;
|
||||
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each Channel.
|
||||
*/
|
||||
public static readonly DataTemplate: Partial<
|
||||
ChannelDataType<ChannelType.DM | ChannelType.GuildAnnouncement | ChannelType.GuildText | ThreadChannelType>
|
||||
> = {
|
||||
set last_pin_timestamp(_: string) {},
|
||||
};
|
||||
|
||||
public [kMixinConstruct]() {
|
||||
this[kLastPinTimestamp] ??= null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.optimizeData}
|
||||
*/
|
||||
protected optimizeData(data: Partial<ChannelDataType<Type>>) {
|
||||
if (data.last_pin_timestamp) {
|
||||
this[kLastPinTimestamp] = Date.parse(data.last_pin_timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The timestamp of when the last pin in the channel happened.
|
||||
*/
|
||||
public get lastPinTimestamp() {
|
||||
return this[kLastPinTimestamp];
|
||||
}
|
||||
|
||||
/**
|
||||
* The Date of when the last pin in the channel happened
|
||||
*/
|
||||
public get lastPinAt() {
|
||||
const lastPinTimestamp = this.lastPinTimestamp;
|
||||
return lastPinTimestamp ? new Date(lastPinTimestamp) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds data from optimized properties omitted from [kData].
|
||||
*
|
||||
* @param data - the result of {@link (Structure:class).toJSON}
|
||||
*/
|
||||
protected [kMixinToJSON](data: Partial<ChannelDataType<Type>>) {
|
||||
data.last_pin_timestamp = this[kLastPinTimestamp] ? new Date(this[kLastPinTimestamp]).toISOString() : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { GuildTextChannelType } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import { TextChannelMixin } from './TextChannelMixin.js';
|
||||
|
||||
export class ChannelSlowmodeMixin<Type extends GuildTextChannelType> extends TextChannelMixin<Type> {
|
||||
/**
|
||||
* The rate limit per user (slowmode) of this channel.
|
||||
*/
|
||||
public get rateLimitPerUser() {
|
||||
return this[kData].rate_limit_per_user;
|
||||
}
|
||||
}
|
||||
33
packages/structures/src/channels/mixins/ChannelTopicMixin.ts
Normal file
33
packages/structures/src/channels/mixins/ChannelTopicMixin.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { ChannelType } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Channel } from '../Channel.js';
|
||||
import { ChannelWebhookMixin } from './ChannelWebhookMixin.js';
|
||||
|
||||
export interface ChannelTopicMixin<
|
||||
Type extends ChannelType.GuildAnnouncement | ChannelType.GuildForum | ChannelType.GuildMedia | ChannelType.GuildText,
|
||||
> extends Channel<Type> {}
|
||||
|
||||
export class ChannelTopicMixin<
|
||||
Type extends ChannelType.GuildAnnouncement | ChannelType.GuildForum | ChannelType.GuildMedia | ChannelType.GuildText,
|
||||
> extends ChannelWebhookMixin<Type> {
|
||||
/**
|
||||
* The topic of this channel.
|
||||
*/
|
||||
public get topic() {
|
||||
return this[kData].topic;
|
||||
}
|
||||
|
||||
/**
|
||||
* The duration after which new threads get archived by default on this channel.
|
||||
*/
|
||||
public get defaultAutoArchiveDuration() {
|
||||
return this[kData].default_auto_archive_duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default value for rate limit per user (slowmode) on new threads in this channel.
|
||||
*/
|
||||
public get defaultThreadRateLimitPerUser() {
|
||||
return this[kData].default_thread_rate_limit_per_user;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { ChannelType, GuildTextChannelType, ThreadChannelType } from 'discord-api-types/v10';
|
||||
import type { Channel } from '../Channel.js';
|
||||
|
||||
export interface ChannelWebhookMixin<
|
||||
Type extends ChannelType.GuildForum | ChannelType.GuildMedia | Exclude<GuildTextChannelType, ThreadChannelType> =
|
||||
| ChannelType.GuildForum
|
||||
| ChannelType.GuildMedia
|
||||
| Exclude<GuildTextChannelType, ThreadChannelType>,
|
||||
> extends Channel<Type> {}
|
||||
|
||||
export class ChannelWebhookMixin<
|
||||
Type extends ChannelType.GuildForum | ChannelType.GuildMedia | Exclude<GuildTextChannelType, ThreadChannelType> =
|
||||
| ChannelType.GuildForum
|
||||
| ChannelType.GuildMedia
|
||||
| Exclude<GuildTextChannelType, ThreadChannelType>,
|
||||
> {
|
||||
/**
|
||||
* Indicates whether this channel can have webhooks
|
||||
*/
|
||||
public isWebhookCapable(): this is ChannelWebhookMixin & this {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
27
packages/structures/src/channels/mixins/DMChannelMixin.ts
Normal file
27
packages/structures/src/channels/mixins/DMChannelMixin.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { channelLink } from '@discordjs/formatters';
|
||||
import type { ChannelType } from 'discord-api-types/v10';
|
||||
import type { User } from '../../users/User.js';
|
||||
import type { Channel } from '../Channel.js';
|
||||
|
||||
export interface DMChannelMixin<
|
||||
Type extends ChannelType.DM | ChannelType.GroupDM = ChannelType.DM | ChannelType.GroupDM,
|
||||
> extends Channel<Type> {}
|
||||
|
||||
/**
|
||||
* @remarks has recipients, an array of sub-structures {@link User} that extending mixins should add to their DataTemplate and _optimizeData
|
||||
*/
|
||||
export class DMChannelMixin<Type extends ChannelType.DM | ChannelType.GroupDM = ChannelType.DM | ChannelType.GroupDM> {
|
||||
/**
|
||||
* The URL to this channel.
|
||||
*/
|
||||
public get url() {
|
||||
return channelLink(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this channel is a DM or DM Group
|
||||
*/
|
||||
public isDMBased(): this is DMChannelMixin & this {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
28
packages/structures/src/channels/mixins/GroupDMMixin.ts
Normal file
28
packages/structures/src/channels/mixins/GroupDMMixin.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { ChannelType } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Channel } from '../Channel.js';
|
||||
|
||||
export interface GroupDMMixin extends Channel<ChannelType.GroupDM> {}
|
||||
|
||||
export class GroupDMMixin {
|
||||
/**
|
||||
* The icon hash of the group DM.
|
||||
*/
|
||||
public get icon() {
|
||||
return this[kData].icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the channel is managed by an application via the `gdm.join` OAuth2 scope.
|
||||
*/
|
||||
public get managed() {
|
||||
return this[kData].managed;
|
||||
}
|
||||
|
||||
/**
|
||||
* The application id of the group DM creator if it is bot-created.
|
||||
*/
|
||||
public get applicationId() {
|
||||
return this[kData].application_id;
|
||||
}
|
||||
}
|
||||
40
packages/structures/src/channels/mixins/GuildChannelMixin.ts
Normal file
40
packages/structures/src/channels/mixins/GuildChannelMixin.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { channelLink } from '@discordjs/formatters';
|
||||
import type { GuildChannelType } from 'discord-api-types/v10';
|
||||
import { ChannelFlagsBitField } from '../../bitfields/ChannelFlagsBitField.js';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Channel } from '../Channel.js';
|
||||
|
||||
export interface GuildChannelMixin<Type extends GuildChannelType = GuildChannelType> extends Channel<Type> {}
|
||||
|
||||
export class GuildChannelMixin<Type extends GuildChannelType = GuildChannelType> {
|
||||
/**
|
||||
* The flags that are applied to the channel.
|
||||
*
|
||||
* @privateRemarks The type of `flags` can be narrowed in Guild Channels and DMChannel to ChannelFlags, and in GroupDM channel
|
||||
* to null, respecting Omit behaviors
|
||||
*/
|
||||
public get flags() {
|
||||
return this[kData].flags ? new ChannelFlagsBitField(this[kData].flags) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* THe id of the guild this channel is in.
|
||||
*/
|
||||
public get guildId() {
|
||||
return this[kData].guild_id!;
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL to this channel.
|
||||
*/
|
||||
public get url() {
|
||||
return channelLink(this.id, this.guildId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this channel is in a guild
|
||||
*/
|
||||
public isGuildBased(): this is GuildChannelMixin & this {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
21
packages/structures/src/channels/mixins/TextChannelMixin.ts
Normal file
21
packages/structures/src/channels/mixins/TextChannelMixin.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { TextChannelType } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Channel } from '../Channel.js';
|
||||
|
||||
export interface TextChannelMixin<Type extends TextChannelType = TextChannelType> extends Channel<Type> {}
|
||||
|
||||
export class TextChannelMixin<Type extends TextChannelType = TextChannelType> {
|
||||
/**
|
||||
* The id of the last message sent in this channel.
|
||||
*/
|
||||
public get lastMessageId() {
|
||||
return this[kData].last_message_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this channel can contain messages
|
||||
*/
|
||||
public isTextBased(): this is TextChannelMixin & this {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import type { ThreadChannelType } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Channel } from '../Channel.js';
|
||||
|
||||
export interface ThreadChannelMixin<Type extends ThreadChannelType = ThreadChannelType> extends Channel<Type> {}
|
||||
|
||||
/**
|
||||
* @remarks has a sub-structure {@link ThreadMetadata} that extending mixins should add to their DataTemplate and _optimizeData
|
||||
*/
|
||||
export class ThreadChannelMixin<Type extends ThreadChannelType = ThreadChannelType> {
|
||||
/**
|
||||
* The approximate count of users in a thread, stops counting at 50
|
||||
*/
|
||||
public get memberCount() {
|
||||
return this[kData].member_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of messages (not including the initial message or deleted messages) in a thread.
|
||||
*/
|
||||
public get messageCount() {
|
||||
return this[kData].message_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of messages ever sent in a thread, it's similar to message_count on message creation,
|
||||
* but will not decrement the number when a message is deleted.
|
||||
*/
|
||||
public get totalMessageSent() {
|
||||
return this[kData].total_message_sent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this channel is a thread channel
|
||||
*/
|
||||
public isThread(): this is ThreadChannelMixin & this {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { ChannelType } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Channel } from '../Channel.js';
|
||||
|
||||
export interface ThreadOnlyChannelMixin<
|
||||
Type extends ChannelType.GuildForum | ChannelType.GuildMedia = ChannelType.GuildForum | ChannelType.GuildMedia,
|
||||
> extends Channel<Type> {}
|
||||
|
||||
/**
|
||||
* @remarks has an array of sub-structures {@link ForumTag} that extending mixins should add to their DataTemplate and _optimizeData
|
||||
*/
|
||||
export class ThreadOnlyChannelMixin<
|
||||
Type extends ChannelType.GuildForum | ChannelType.GuildMedia = ChannelType.GuildForum | ChannelType.GuildMedia,
|
||||
> {
|
||||
/**
|
||||
* The emoji to show in the add reaction button on a thread in this channel.
|
||||
*/
|
||||
public get defaultReactionEmoji() {
|
||||
return this[kData].default_reaction_emoji;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default sort order type used to order posts in this channel.
|
||||
*
|
||||
* @defaultValue `null` – indicates a preferred sort order hasn't been set.
|
||||
*/
|
||||
public get defaultSortOrder() {
|
||||
return this[kData].default_sort_order!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this channel only allows thread creation
|
||||
*/
|
||||
public isThreadOnly(): this is ThreadOnlyChannelMixin & this {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
51
packages/structures/src/channels/mixins/VoiceChannelMixin.ts
Normal file
51
packages/structures/src/channels/mixins/VoiceChannelMixin.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import type { ChannelType } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Channel } from '../Channel.js';
|
||||
import { TextChannelMixin } from './TextChannelMixin.js';
|
||||
|
||||
export interface VoiceChannelMixin<
|
||||
Type extends ChannelType.GuildStageVoice | ChannelType.GuildVoice =
|
||||
| ChannelType.GuildStageVoice
|
||||
| ChannelType.GuildVoice,
|
||||
> extends Channel<Type> {}
|
||||
|
||||
export class VoiceChannelMixin<
|
||||
Type extends ChannelType.GuildStageVoice | ChannelType.GuildVoice =
|
||||
| ChannelType.GuildStageVoice
|
||||
| ChannelType.GuildVoice,
|
||||
> extends TextChannelMixin<Type> {
|
||||
/**
|
||||
* The bitrate (in bits) of the voice channel.
|
||||
*/
|
||||
public get bitrate() {
|
||||
return this[kData].bitrate!;
|
||||
}
|
||||
|
||||
/**
|
||||
* The voice region id for this channel, automatic when set to null.
|
||||
*/
|
||||
public get rtcRegion() {
|
||||
return this[kData].rtc_region!;
|
||||
}
|
||||
|
||||
/**
|
||||
* The camera video quality mode of the voice channel, {@link discord-api-types/v10#(VideoQualityMode:enum) | Auto} when not present.
|
||||
*/
|
||||
public get videoQualityMode() {
|
||||
return this[kData].video_quality_mode!;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user limit of the voice channel.
|
||||
*/
|
||||
public get userLimit() {
|
||||
return this[kData].user_limit!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this channel has voice connection capabilities
|
||||
*/
|
||||
public override isVoiceBased(): this is VoiceChannelMixin & this {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
15
packages/structures/src/channels/mixins/index.ts
Normal file
15
packages/structures/src/channels/mixins/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export * from './AppliedTagsMixin.js';
|
||||
export * from './ChannelOwnerMixin.js';
|
||||
export * from './ChannelParentMixin.js';
|
||||
export * from './ChannelPermissionMixin.js';
|
||||
export * from './ChannelPinMixin.js';
|
||||
export * from './ChannelSlowmodeMixin.js';
|
||||
export * from './ChannelTopicMixin.js';
|
||||
export * from './ChannelWebhookMixin.js';
|
||||
export * from './DMChannelMixin.js';
|
||||
export * from './GroupDMMixin.js';
|
||||
export * from './GuildChannelMixin.js';
|
||||
export * from './TextChannelMixin.js';
|
||||
export * from './ThreadChannelMixin.js';
|
||||
export * from './ThreadOnlyChannelMixin.js';
|
||||
export * from './VoiceChannelMixin.js';
|
||||
Reference in New Issue
Block a user