Skip to content

Commit

Permalink
Merge pull request #328 from Helloyunho/main
Browse files Browse the repository at this point in the history
✨ feat: add guild forum channel support
  • Loading branch information
Helloyunho authored Nov 10, 2022
2 parents 0247454 + 304e9c4 commit 85b0780
Show file tree
Hide file tree
Showing 18 changed files with 706 additions and 262 deletions.
7 changes: 5 additions & 2 deletions mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ export {
GuildIntegration
} from './src/structures/guild.ts'
export { CategoryChannel } from './src/structures/guildCategoryChannel.ts'
export {
GuildForumChannel,
GuildForumTag
} from './src/structures/guildForumChannel.ts'
export { NewsChannel } from './src/structures/guildNewsChannel.ts'
export { VoiceChannel } from './src/structures/guildVoiceChannel.ts'
export { Invite } from './src/structures/invite.ts'
Expand All @@ -87,8 +91,7 @@ export { Snowflake } from './src/utils/snowflake.ts'
export { TextChannel } from './src/structures/textChannel.ts'
export {
GuildTextBasedChannel,
GuildTextChannel,
checkGuildTextBasedChannel
GuildTextChannel
} from './src/structures/guildTextChannel.ts'
export type { AllMessageOptions } from './src/structures/textChannel.ts'
export { MessageReaction } from './src/structures/messageReaction.ts'
Expand Down
12 changes: 6 additions & 6 deletions src/managers/channelThreads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@ import type {
ThreadChannel,
ThreadMember
} from '../structures/threadChannel.ts'
import type { BaseManager, Message } from '../../mod.ts'
import type {
CreateThreadOptions,
GuildTextChannel
} from '../structures/guildTextChannel.ts'
import type { BaseManager, Message } from '../../mod.ts'
GuildThreadAvailableChannel
} from '../structures/guildThreadAvailableChannel.ts'

export class ChannelThreadsManager extends BaseChildManager<
ThreadChannelPayload,
ThreadChannel
> {
channel: GuildTextChannel
channel: GuildThreadAvailableChannel
declare parent: BaseManager<ThreadChannelPayload, ThreadChannel>

constructor(
client: Client,
parent: ThreadsManager,
channel: GuildTextChannel
channel: GuildThreadAvailableChannel
) {
super(
client,
Expand Down Expand Up @@ -60,7 +60,7 @@ export class ChannelThreadsManager extends BaseChildManager<

async start(
options: CreateThreadOptions,
message: string | Message
message?: string | Message
): Promise<ThreadChannel> {
return this.channel.startThread(options, message)
}
Expand Down
17 changes: 17 additions & 0 deletions src/managers/emojis.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Guild } from '../../mod.ts'
import type { Client } from '../client/mod.ts'
import { Emoji } from '../structures/emoji.ts'
import type { EmojiPayload } from '../types/emoji.ts'
Expand Down Expand Up @@ -32,4 +33,20 @@ export class EmojisManager extends BaseManager<EmojiPayload, Emoji> {
.catch((e) => reject(e))
})
}

/** Try to get Emoji from cache, if not found then fetch */
async resolve(
key: string,
guild?: string | Guild
): Promise<Emoji | undefined> {
const cacheValue = await this.get(key)
if (cacheValue !== undefined) return cacheValue
else {
if (guild !== undefined) {
const guildID = typeof guild === 'string' ? guild : guild.id
const fetchValue = await this.fetch(guildID, key).catch(() => undefined)
if (fetchValue !== undefined) return fetchValue
}
}
}
}
33 changes: 30 additions & 3 deletions src/rest/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1366,9 +1366,9 @@ The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding
}

/**
* Creates a new public thread from an existing message. Returns a channel on success, and a 400 BAD REQUEST on invalid parameters. Fires a Thread Create Gateway event.
* Creates a new thread from an existing message. Returns a channel on success, and a 400 BAD REQUEST on invalid parameters. Fires a Thread Create Gateway event.
*/
async startPublicThread(
async startPublicThreadFromMessage(
channelId: string,
messageId: string,
payload: CreateThreadPayload
Expand All @@ -1379,14 +1379,41 @@ The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding
)
}

// Exist for backwards compatibility
/**
* Creates a new public thread from an existing message. Returns a channel on success, and a 400 BAD REQUEST on invalid parameters. Fires a Thread Create Gateway event.
*/
async startPublicThread(
channelId: string,
messageId: string,
payload: CreateThreadPayload
): Promise<ThreadChannelPayload> {
return await this.startPublicThreadFromMessage(
channelId,
messageId,
payload
)
}

/**
* Creates a new thread from an existing message. Returns a channel on success, and a 400 BAD REQUEST on invalid parameters. Fires a Thread Create Gateway event.
*/
async startThreadWithoutMessage(
channelId: string,
payload: CreateThreadPayload
): Promise<ThreadChannelPayload> {
return this.rest.post(`/channels/${channelId}/threads`, payload)
}

// Exist for backwards compatibility
/**
* Creates a new private thread. Returns a channel on success, and a 400 BAD REQUEST on invalid parameters. Fires a Thread Create Gateway event.
*/
async startPrivateThread(
channelId: string,
payload: CreateThreadPayload
): Promise<ThreadChannelPayload> {
return this.rest.post(`/channels/${channelId}/threads`, payload)
return await this.startThreadWithoutMessage(channelId, payload)
}

/**
Expand Down
13 changes: 13 additions & 0 deletions src/structures/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,23 @@ import type { VoiceChannel } from '../structures/guildVoiceChannel.ts'
import type { StageVoiceChannel } from '../structures/guildVoiceStageChannel.ts'
import type { TextChannel } from '../structures/textChannel.ts'
import type { ThreadChannel } from '../structures/threadChannel.ts'
import { CreateInviteOptions } from '../managers/invites.ts'
import { Invite } from './invite.ts'

export class Channel extends SnowflakeBase {
type!: ChannelTypes
flags!: number

static cacheName = 'channel'

get mention(): string {
return `<#${this.id}>`
}

toString(): string {
return this.mention
}

constructor(client: Client, data: ChannelPayload) {
super(client, data)
this.readFromData(data)
Expand All @@ -61,6 +68,7 @@ export class Channel extends SnowflakeBase {
readFromData(data: ChannelPayload): void {
this.type = data.type ?? this.type
this.id = data.id ?? this.id
this.flags = data.flags ?? this.flags
}

isDM(): this is DMChannel {
Expand Down Expand Up @@ -435,4 +443,9 @@ export class GuildChannel extends Channel {
async setPosition(position: number): Promise<GuildChannels> {
return await this.edit({ position })
}

/** Create an Invite for this Channel */
async createInvite(options?: CreateInviteOptions): Promise<Invite> {
return this.guild.invites.create(this.id, options)
}
}
191 changes: 191 additions & 0 deletions src/structures/guildForumChannel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { Client } from '../client/client.ts'
import type { AllMessageOptions } from '../managers/channels.ts'
import {
CreateThreadInForumPayload,
GuildForumChannelPayload,
GuildForumSortOrderTypes,
GuildForumTagPayload,
ModifyGuildForumChannelOption,
ModifyGuildForumChannelPayload,
ThreadChannelPayload
} from '../types/channel.ts'
import { CHANNEL } from '../types/endpoint.ts'
import { transformComponent } from '../utils/components.ts'
import { Embed } from './embed.ts'
import { Emoji } from './emoji.ts'
import { Guild } from './guild.ts'
import { GuildThreadAvailableChannel } from './guildThreadAvailableChannel.ts'
import { Message } from './message.ts'
import { ThreadChannel } from './threadChannel.ts'

export interface CreateThreadInForumOptions {
/** 2-100 character channel name */
name: string
/** duration in minutes to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 */
autoArchiveDuration?: number
slowmode?: number | null
message: string | AllMessageOptions
appliedTags?: string[] | GuildForumTag[]
}

export class GuildForumTag {
id!: string
name!: string
moderated!: boolean
emojiID!: string
emojiName!: string | null

constructor(data: GuildForumTagPayload) {
this.readFromData(data)
}

readFromData(data: GuildForumTagPayload): void {
this.id = data.id ?? this.id
this.name = data.name ?? this.name
this.moderated = data.moderated ?? this.moderated
this.emojiID = data.emoji_id ?? this.emojiID
this.emojiName = data.emoji_name ?? this.emojiName
}
}

export class GuildForumChannel extends GuildThreadAvailableChannel {
availableTags!: GuildForumTag[]
defaultReactionEmoji!: Emoji
defaultSortOrder!: GuildForumSortOrderTypes

constructor(client: Client, data: GuildForumChannelPayload, guild: Guild) {
super(client, data, guild)
this.readFromData(data)
}

readFromData(data: GuildForumChannelPayload): void {
super.readFromData(data)
this.availableTags =
data.available_tags?.map((tag) => new GuildForumTag(tag)) ??
this.availableTags
this.defaultReactionEmoji =
data.default_reaction_emoji !== null
? new Emoji(this.client, {
id: data.default_reaction_emoji.emoji_id,
name: data.default_reaction_emoji.emoji_name
})
: this.defaultReactionEmoji
this.defaultSortOrder = data.default_sort_order ?? this.defaultSortOrder
}

async edit(
options?: ModifyGuildForumChannelOption
): Promise<GuildForumChannel> {
if (options?.defaultReactionEmoji !== undefined) {
if (options.defaultReactionEmoji instanceof Emoji) {
options.defaultReactionEmoji = {
emoji_id: options.defaultReactionEmoji.id,
emoji_name: options.defaultReactionEmoji.name
}
}
}
if (options?.availableTags !== undefined) {
options.availableTags = options.availableTags?.map((tag) => {
if (tag instanceof GuildForumTag) {
return {
id: tag.id,
name: tag.name,
moderated: tag.moderated,
emoji_id: tag.emojiID,
emoji_name: tag.emojiName
}
}
return tag
})
}

const body: ModifyGuildForumChannelPayload = {
name: options?.name,
position: options?.position,
permission_overwrites: options?.permissionOverwrites,
parent_id: options?.parentID,
nsfw: options?.nsfw,
topic: options?.topic,
rate_limit_per_user: options?.slowmode,
default_auto_archive_duration: options?.defaultAutoArchiveDuration,
default_thread_rate_limit_per_user: options?.defaultThreadSlowmode,
default_sort_order: options?.defaultSortOrder,
default_reaction_emoji: options?.defaultReactionEmoji,
available_tags: options?.availableTags
}

const resp = await this.client.rest.patch(CHANNEL(this.id), body)

return new GuildForumChannel(this.client, resp, this.guild)
}

override async startThread(
options: CreateThreadInForumOptions,
message?: string | AllMessageOptions | Message
): Promise<ThreadChannel> {
if (options.message !== undefined) {
message = options.message
}
if (message instanceof Message) {
message = {
content: message.content,
embeds: message.embeds.map((embed) => new Embed(embed)),
components: message.components
}
} else if (message instanceof Embed) {
message = {
embed: message
}
} else if (Array.isArray(message)) {
message = {
embeds: message
}
} else if (typeof message === 'string') {
message = {
content: message
}
}

const messageObject = {
content: message?.content,
embed: message?.embed,
embeds: message?.embeds,
file: message?.file,
files: message?.files,
allowed_mentions: message?.allowedMentions,
components:
message?.components !== undefined
? typeof message.components === 'function'
? message.components()
: transformComponent(message.components)
: undefined
}

if (
messageObject.content === undefined &&
messageObject.embed === undefined
) {
messageObject.content = ''
}

const body: CreateThreadInForumPayload = {
name: options.name,
auto_archive_duration: options.autoArchiveDuration,
rate_limit_per_user: options.slowmode,
message: messageObject,
applied_tags: options.appliedTags?.map((tag) => {
if (tag instanceof GuildForumTag) {
return tag.id
}
return tag
})
}

const resp: ThreadChannelPayload = await this.client.rest.api.channels[
this.id
].threads.post(body)
const thread = new ThreadChannel(this.client, resp, this.guild)
this.threads.set(thread.id, resp)
return thread
}
}
7 changes: 6 additions & 1 deletion src/structures/guildNewsChannel.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { Mixin } from '../../deps.ts'
import { GuildTextBasedChannel } from './guildTextChannel.ts'
import { GuildThreadAvailableChannel } from './guildThreadAvailableChannel.ts'

export class NewsChannel extends GuildTextBasedChannel {}
export class NewsChannel extends Mixin(
GuildTextBasedChannel,
GuildThreadAvailableChannel
) {}
Loading

0 comments on commit 85b0780

Please sign in to comment.