diff --git a/.changeset/curvy-jobs-leave.md b/.changeset/curvy-jobs-leave.md new file mode 100644 index 00000000..8007bdd8 --- /dev/null +++ b/.changeset/curvy-jobs-leave.md @@ -0,0 +1,6 @@ +--- +'guilded.ts': minor +'@guildedts/rest': minor +--- + +feat: add forum topic comments diff --git a/packages/guilded.ts/src/collectors/ForumTopicCommentReactionCollector.ts b/packages/guilded.ts/src/collectors/ForumTopicCommentReactionCollector.ts new file mode 100644 index 00000000..00614dd1 --- /dev/null +++ b/packages/guilded.ts/src/collectors/ForumTopicCommentReactionCollector.ts @@ -0,0 +1,35 @@ +import { Collector, CollectorOptions } from './Collector'; +import { ForumTopicCommentReaction } from '../structures/forum/ForumTopicCommentReaction'; +import { ForumTopicCommentReactionManager } from '../managers/forum/ForumTopicCommentReactionManager'; + +/** + * The collector for message reactions + */ +export class ForumTopicCommentReactionCollector extends Collector< + number, + ForumTopicCommentReaction +> { + /** + * @param reactions The manager for message reactions + * @param options The options for the reaction collector + */ + constructor( + public readonly reactions: ForumTopicCommentReactionManager, + options?: CollectorOptions, + ) { + super(reactions.client, options); + this.options.dispose = + options?.dispose ?? this.client.options.disposeCollectedMessageReactions; + this.client.on('forumTopicCommentReactionAdd', this.collectReaction.bind(this)); + this.client.on('forumTopicCommentReactionRemove', this.disposeReaction.bind(this)); + } + + private collectReaction(reaction: ForumTopicCommentReaction) { + if (reaction.forumTopicComment.id !== this.reactions.forumTopicComment.id) return; + this.collect(reaction.emote.id, reaction); + } + + private disposeReaction(reaction: ForumTopicCommentReaction) { + this.dispose(reaction.emote.id); + } +} diff --git a/packages/guilded.ts/src/index.ts b/packages/guilded.ts/src/index.ts index 72905b6f..e57ff877 100644 --- a/packages/guilded.ts/src/index.ts +++ b/packages/guilded.ts/src/index.ts @@ -23,7 +23,7 @@ export * from './managers/server/ServerRoleManager'; export * from './managers/BaseManager'; export * from './managers/DocManager'; export * from './managers/ListItemManager'; -export * from './managers/ForumTopicManager'; +export * from './managers/forum/ForumTopicManager'; export * from './managers/UserManager'; // Structures @@ -49,7 +49,7 @@ export * from './structures/CacheCollection'; export * from './structures/Client'; export * from './structures/Doc'; export * from './structures/Group'; -export * from './structures/ForumTopic'; +export * from './structures/forum/ForumTopic'; export * from './structures/User'; export * from './structures/Webhook'; diff --git a/packages/guilded.ts/src/managers/forum/ForumTopicCommentManager.ts b/packages/guilded.ts/src/managers/forum/ForumTopicCommentManager.ts new file mode 100644 index 00000000..08d41197 --- /dev/null +++ b/packages/guilded.ts/src/managers/forum/ForumTopicCommentManager.ts @@ -0,0 +1,160 @@ +import { BaseManager, FetchManyOptions, FetchOptions } from '../BaseManager'; +import { RESTGetForumTopicCommentsResult, APIForumTopicComment } from 'guilded-api-typings'; +import { ForumTopicComment } from '../../structures/forum/ForumTopicComment'; +import { ForumChannel } from '../../structures/channel/ForumChannel'; +import { + APIForumTopic, + APIForumTopicSummary, + RESTPostForumTopicCommentJSONBody, + RESTPatchForumTopicCommentJSONBody, +} from 'guilded-api-typings'; +import { Collection } from '@discordjs/collection'; + +/** + * The manager for forum topic comments + */ +export class ForumTopicCommentManager extends BaseManager { + /** + * @param forumTopic The forum topic + */ + constructor( + public readonly channel: ForumChannel, + public readonly forumTopic: APIForumTopic | APIForumTopicSummary, + ) { + super(channel.client, channel.client.options.maxForumTopicCommentCache); + } + /** + * Fetch the forum topic comment + * @param forumTopicComment The ID of the forum topic comment + * @param options The options to fetch the forum topic comments with + * @returns The fetched forum topic comments + */ + fetch( + forumTopicComment: number | ForumTopicComment, + options?: FetchOptions, + ): Promise; + /** + * Fetch the forum topic comment + * @param forumTopicComment The forum topic comment + * @param options The options to fetch the forum topic comments with + * @returns The fetched forum topic comments + */ + fetch( + options?: ForumTopicCommentFetchManyOptions, + ): Promise>; + fetch( + arg1?: number | ForumTopicComment | ForumTopicCommentFetchManyOptions, + arg2?: FetchOptions, + ) { + if (typeof arg1 === 'number' || arg1 instanceof ForumTopicComment) + return this.fetchSingle(arg1, arg2); + return this.fetchMany(arg1); + } + + private async fetchSingle( + forumTopicComment: number | ForumTopicComment, + options?: FetchOptions, + ) { + const forumTopic = this.forumTopic.id; + forumTopicComment = + forumTopicComment instanceof ForumTopicComment + ? forumTopicComment.id + : forumTopicComment; + const cached = this.cache.get(forumTopicComment); + if (cached && !options?.force) return cached; + const raw = await this.client.api.forumTopicComments.fetch( + this.channel.id, + forumTopic, + forumTopicComment, + ); + const topic = await this.channel.topics.fetch(forumTopic); + return new ForumTopicComment( + this.channel, + topic, + Array.isArray(raw) ? raw[0] : raw, + options?.cache, + ); + } + + private async fetchMany(options?: ForumTopicCommentFetchManyOptions) { + const forumTopic = this.forumTopic.id; + const raw = (await this.client.api.forumTopicComments.fetch( + this.channel.id, + forumTopic, + )) as unknown as APIForumTopicComment[]; + const topic = await this.channel.topics.fetch(forumTopic); + const forumComments = new Collection(); + for (const data of raw) { + const forumTopicComment = new ForumTopicComment( + this.channel, + topic, + data, + options?.cache, + ); + forumComments.set(forumTopicComment.id, forumTopicComment); + } + return forumComments; + } + + /** + * Create a forum topic comment in the forum topic + * @param options The options to create the forum topic with + * @returns The created forum topic + */ + async create(options: RESTPostForumTopicCommentJSONBody) { + const raw = await this.client.api.forumTopicComments.create( + this.channel.id, + this.forumTopic.id, + options, + ); + const topic = await this.channel.topics.fetch(this.forumTopic.id); + return new ForumTopicComment(this.channel, topic, raw); + } + + /** + * Update a forum topic comment in the forum topic + * @param forumTopicComment The forum topic comment + * @param options The options to update the forum topic with + * @returns The updated forum topic + */ + async update( + forumTopicComment: number | ForumTopicComment, + options: RESTPatchForumTopicCommentJSONBody, + ) { + forumTopicComment = + forumTopicComment instanceof ForumTopicComment + ? forumTopicComment.id + : forumTopicComment; + const raw = await this.client.api.forumTopicComments.edit( + this.channel.id, + this.forumTopic.id, + forumTopicComment, + options, + ); + const topic = await this.channel.topics.fetch(this.forumTopic.id); + return new ForumTopicComment(this.channel, topic, raw); + } + + /** + * Delete a forum topic comment from the channel + * @param forumTopic The forum topic + */ + delete(forumTopicComment: number | ForumTopicComment) { + forumTopicComment = + forumTopicComment instanceof ForumTopicComment + ? forumTopicComment.id + : forumTopicComment; + return this.client.api.forumTopicComments.delete( + this.channel.id, + this.forumTopic.id, + forumTopicComment, + ); + } +} + +/** + * The options for fetching forum topics + */ +export interface ForumTopicCommentFetchManyOptions + extends FetchManyOptions, + RESTGetForumTopicCommentsResult {} diff --git a/packages/guilded.ts/src/managers/forum/ForumTopicCommentReactionManager.ts b/packages/guilded.ts/src/managers/forum/ForumTopicCommentReactionManager.ts new file mode 100644 index 00000000..31043bc9 --- /dev/null +++ b/packages/guilded.ts/src/managers/forum/ForumTopicCommentReactionManager.ts @@ -0,0 +1,83 @@ +import { BaseManager, FetchManyOptions } from '../BaseManager'; +import { RESTGetForumTopicCommentsResult } from 'guilded-api-typings'; +import { ForumTopicComment } from '../../structures/forum/ForumTopicComment'; +import { ForumTopicCommentReaction } from '../../structures/forum/ForumTopicCommentReaction'; +import { ForumTopicCommentReactionCollector } from '../../collectors/ForumTopicCommentReactionCollector'; +import { CollectorOptions } from '../../collectors/Collector'; +import { Collection } from '@discordjs/collection'; + +/** + * The manager for forum topic comment reactions + */ +export class ForumTopicCommentReactionManager extends BaseManager< + number, + ForumTopicCommentReaction +> { + /** + * @param forumTopicComment The forum topic comment + */ + constructor(public readonly forumTopicComment: ForumTopicComment) { + super(forumTopicComment.client, forumTopicComment.client.options.maxMessageReactionCache); + } + + /** + * Add a reaction to the forum topic comment + * @param emojiId The ID of the emoji + */ + add(emojiId: number) { + return this.client.api.forumTopicCommentReactions.create( + this.forumTopicComment.channel.id, + this.forumTopicComment.forumTopic.id, + this.forumTopicComment.id, + emojiId, + ); + } + + /** + * Remove a reaction from the forum topic comment + * @param emojiId The ID of the emoji + */ + remove(emojiId: number) { + return this.client.api.forumTopicCommentReactions.delete( + this.forumTopicComment.channel.id, + this.forumTopicComment.forumTopic.id, + this.forumTopicComment.id, + emojiId, + ); + } + + /** + * Create a reaction collector for the forum topic comment + * @param options The options for the forum topic comment reaction collector + * @returns The created forum topic comment reaction collector + * @example + * const collector = comment.reactions.createCollector({ time: 15 * 1000 }); + * + * collector.on('end', (reactions) => console.log(`Collected ${reactions.size} reactions!`)); + */ + createCollector(options?: CollectorOptions) { + return new ForumTopicCommentReactionCollector(this, options); + } + + /** + * Similar to {@link createCollector} but in promise form + * @param options The options for the forum topic comment reaction collector + * @returns The collected forum topic comment reactions + * @example + * const reactions = await comment.reactions.awaitReactions({ time: 15 * 1000 }); + * + * console.log(`Collected ${reactions.size} reactions!`); + */ + awaitReactions(options?: CollectorOptions) { + return new Promise>((resolve) => + this.createCollector(options).once('end', resolve), + ); + } +} + +/** + * The options for fetching forum topics + */ +export interface ForumTopicCommentFetchManyOptions + extends FetchManyOptions, + RESTGetForumTopicCommentsResult {} diff --git a/packages/guilded.ts/src/managers/ForumTopicManager.ts b/packages/guilded.ts/src/managers/forum/ForumTopicManager.ts similarity index 95% rename from packages/guilded.ts/src/managers/ForumTopicManager.ts rename to packages/guilded.ts/src/managers/forum/ForumTopicManager.ts index d2b0de0d..92c0f2aa 100644 --- a/packages/guilded.ts/src/managers/ForumTopicManager.ts +++ b/packages/guilded.ts/src/managers/forum/ForumTopicManager.ts @@ -1,6 +1,6 @@ -import { BaseManager, FetchManyOptions, FetchOptions } from './BaseManager'; -import { ForumChannel } from '../structures/channel/ForumChannel'; -import { ForumTopic } from '../structures/ForumTopic'; +import { BaseManager, FetchManyOptions, FetchOptions } from '../BaseManager'; +import { ForumChannel } from '../../structures/channel/ForumChannel'; +import { ForumTopic } from '../../structures/forum/ForumTopic'; import { RESTPatchForumTopicJSONBody, RESTGetForumTopicsQuery, diff --git a/packages/guilded.ts/src/managers/message/MessageReactionManager.ts b/packages/guilded.ts/src/managers/message/MessageReactionManager.ts index 7a0d3edb..882c0561 100644 --- a/packages/guilded.ts/src/managers/message/MessageReactionManager.ts +++ b/packages/guilded.ts/src/managers/message/MessageReactionManager.ts @@ -50,7 +50,7 @@ export class MessageReactionManager extends BaseManager * @param options The options for the message reaction collector * @returns The collected message reactions * @example - * const reactions = await message.reactions.await({ time: 15 * 1000 }); + * const reactions = await message.reactions.awaitReactions({ time: 15 * 1000 }); * * console.log(`Collected ${reactions.size} reactions!`); */ diff --git a/packages/guilded.ts/src/structures/Client.ts b/packages/guilded.ts/src/structures/Client.ts index f6bbb748..1a008eb7 100644 --- a/packages/guilded.ts/src/structures/Client.ts +++ b/packages/guilded.ts/src/structures/Client.ts @@ -25,7 +25,9 @@ import { MessageReaction } from './message/MessageReaction'; import { Channel } from './channel/Channel'; import { Collection } from '@discordjs/collection'; import { CalendarEventRsvp } from './calendarEvent/CalendarEventRsvp'; -import { ForumTopic } from './ForumTopic'; +import { ForumTopic } from './forum/ForumTopic'; +import { ForumTopicComment } from './forum/ForumTopicComment'; +import { ForumTopicCommentReaction } from './forum/ForumTopicCommentReaction'; /** * The main hub for interacting with the Guilded API @@ -334,6 +336,29 @@ export interface ClientEvents { * Emitted whenever a forum topic is unlocked */ forumTopicUnlock: [forumTopic: ForumTopic]; + /** + * Emitted whenever a forum topic comment is created + */ + forumTopicCommentCreate: [forumTopicComment: ForumTopicComment]; + /** + * Emitted whenever a forum topic comment is edited + */ + forumTopicCommentEdit: [ + newForumTopicComment: ForumTopicComment, + oldForumTopicComment?: ForumTopicComment, + ]; + /** + * Emitted whenever a forum topic comment is deleted + */ + forumTopicCommentDelete: [forumTopicComment: ForumTopicComment]; + /** + * Emitted whenever a forum topic comment reaction is added + */ + forumTopicCommentReactionAdd: [forumTopicCommentReaction: ForumTopicCommentReaction]; + /** + * Emitted whenever a forum topic comment reaction is removed + */ + forumTopicCommentReactionRemove: [forumTopicCommentReaction: ForumTopicCommentReaction]; /** * Emitted whenever a calendar event RSVP is edited */ @@ -510,6 +535,30 @@ export interface ClientOptions { * Whether to dispose cached forum topics */ disposeCachedForumTopics?: boolean; + /** + * Whether to cache forum topic comments + */ + cacheForumTopicComments?: boolean; + /** + * The max cache size for forum topic comments + */ + maxForumTopicCommentCache?: number; + /** + * Whether to dispose cached forum topic comments + */ + disposeCachedForumTopicComments?: boolean; + /** + * Whether to cache forum topic comment reactions + */ + cacheForumTopicCommentReactions?: boolean; + /** + * The max cache size for forum topic reactions + */ + maxForumTopicCommentReactionsCache?: number; + /** + * Whether to dispose cached forum topic reactions + */ + disposeCachedForumTopicCommentReactions?: boolean; /** * Whether to cache list items */ diff --git a/packages/guilded.ts/src/structures/channel/ForumChannel.ts b/packages/guilded.ts/src/structures/channel/ForumChannel.ts index 4962b5dd..c3e9c5c4 100644 --- a/packages/guilded.ts/src/structures/channel/ForumChannel.ts +++ b/packages/guilded.ts/src/structures/channel/ForumChannel.ts @@ -1,7 +1,7 @@ import { APIChannel } from 'guilded-api-typings'; import { Client } from '../Client'; import { Channel } from './Channel'; -import { ForumTopicManager } from '../../managers/ForumTopicManager'; +import { ForumTopicManager } from '../../managers/forum/ForumTopicManager'; /** * Represents a forum channel on Guilded diff --git a/packages/guilded.ts/src/structures/ForumTopic.ts b/packages/guilded.ts/src/structures/forum/ForumTopic.ts similarity index 74% rename from packages/guilded.ts/src/structures/ForumTopic.ts rename to packages/guilded.ts/src/structures/forum/ForumTopic.ts index f5398cfe..40364d2b 100644 --- a/packages/guilded.ts/src/structures/ForumTopic.ts +++ b/packages/guilded.ts/src/structures/forum/ForumTopic.ts @@ -3,14 +3,24 @@ import { RESTPatchForumTopicJSONBody, APIForumTopicSummary, } from 'guilded-api-typings'; -import { FetchOptions } from '../managers/BaseManager'; -import { Base } from './Base'; -import { ForumChannel } from './channel/ForumChannel'; +import { FetchOptions } from '../../managers/BaseManager'; +import { Base } from '../Base'; +import { ForumChannel } from '../channel/ForumChannel'; +import { ForumTopicComment } from './ForumTopicComment'; +import { + ForumTopicCommentManager, + ForumTopicCommentFetchManyOptions, +} from '../../managers/forum/ForumTopicCommentManager'; /** * Represents a forum topic on Guilded */ export class ForumTopic extends Base { + /** + * The manager for comments + */ + readonly comments: ForumTopicCommentManager; + /** * @param channel The forum channel * @param data The data of the forum topic @@ -22,6 +32,7 @@ export class ForumTopic extends Base { cache = channel.client.options.cacheForumTopics ?? true, ) { super(channel.client); + this.comments = new ForumTopicCommentManager(this.channel, this.data); if (cache) channel.topics.cache.set(this.id, this); } @@ -118,6 +129,29 @@ export class ForumTopic extends Base { return 'mentions' in this.data ? this.data.mentions ?? {} : {}; } + /** + * Fetch a comment of the forum topic + * @param forumTopicComment The comment of the forum topic + * @param options The options to fetch the comment of the forum topic with + * @returns The fetched forum topic comment + */ + fetchComment(forumTopicComment: number | ForumTopicComment, options?: FetchOptions) { + forumTopicComment = + forumTopicComment instanceof ForumTopicComment + ? forumTopicComment.id + : forumTopicComment; + return this.comments.fetch(forumTopicComment, options); + } + + /** + * Fetch the comments of the forum topic + * @param options The options to fetch the comments of the forum topic with + * @returns The fetched forum topic comment + */ + fetchComments(options?: ForumTopicCommentFetchManyOptions) { + return this.comments.fetch(options); + } + /** * Fetch the forum topic * @param options The options to fetch the forum topic with diff --git a/packages/guilded.ts/src/structures/forum/ForumTopicComment.ts b/packages/guilded.ts/src/structures/forum/ForumTopicComment.ts new file mode 100644 index 00000000..35e17eea --- /dev/null +++ b/packages/guilded.ts/src/structures/forum/ForumTopicComment.ts @@ -0,0 +1,130 @@ +import { APIForumTopicComment, RESTPatchForumTopicCommentJSONBody } from 'guilded-api-typings'; +import { FetchOptions } from '../../managers/BaseManager'; +import { Base } from '../Base'; +import { ForumChannel } from '../channel/ForumChannel'; +import { ForumTopic } from './ForumTopic'; +import { ForumTopicCommentReactionManager } from '../../managers/forum/ForumTopicCommentReactionManager'; + +/** + * Represents a forum comment on Guilded + */ +export class ForumTopicComment extends Base { + /** + * The manager for reactions + */ + readonly reactions: ForumTopicCommentReactionManager; + + /** + * @param channel The forum channel + * @param data The data of the forum topic + * @param cache Whether to cache the forum topic + */ + constructor( + public readonly channel: ForumChannel, + public readonly forumTopic: ForumTopic, + public readonly data: APIForumTopicComment, + cache = channel.client.options.cacheForumTopicComments ?? true, + ) { + super(channel.client); + this.reactions = new ForumTopicCommentReactionManager(this); + if (cache) this.forumTopic.comments.cache.set(this.id, this); + } + + /** + * Whether the forum topic comment is cached + */ + get isCached() { + return this.forumTopic.comments.cache.has(this.id); + } + + /** + * The ID of the forum topic comment + */ + get id() { + return this.data.id; + } + + /** + * When the forum topic comment was created + */ + get createdAt() { + return new Date(this.data.createdAt); + } + + /** + * The ID of the user that created the forum topic comment + */ + get creatorId() { + return this.data.createdBy; + } + + /** + * The user that created the forum topic comment + */ + get creator() { + return this.client.users.cache.get(this.creatorId) ?? null; + } + + /** + * Whether the client user created the forum topic comment + */ + get isCreator() { + return this.creatorId == this.client.user?.id; + } + + /** + * When the forum topic comment was updated, if relevant + */ + get updatedAt() { + return this.data.updatedAt ? new Date(this.data.updatedAt) : null; + } + + /** + * The content of the forum topic comment + */ + get content() { + return 'content' in this.data ? this.data.content : null; + } + + /** + * The mentions of the forum topic comment + */ + get mentions() { + return 'mentions' in this.data ? this.data.mentions ?? {} : {}; + } + + /** + * Fetch the forum topic comment + * @param options The options to fetch the forum topic comment with + * @returns The fetched forum topic comment + */ + fetch(options?: FetchOptions) { + return this.forumTopic.comments.fetch(this.data.id, options); + } + + /** + * Fetch the user that created the forum topic comment + * @returns The fetched user + */ + async fetchCreator() { + return this.client.users.fetch(this.channel.serverId, this.data.createdBy); + } + + /** + * Update a forum topic comment's content + * @param options The options to update the forum topic comment with + * @returns The updated forum topic comment + */ + update(options: RESTPatchForumTopicCommentJSONBody) { + return this.forumTopic.comments.update(this, options) as Promise; + } + + /** + * Delete the forum topic comment + * @returns The deleted forum topic comment + */ + async delete() { + await this.forumTopic.comments.delete(this.id); + return this; + } +} diff --git a/packages/guilded.ts/src/structures/forum/ForumTopicCommentReaction.ts b/packages/guilded.ts/src/structures/forum/ForumTopicCommentReaction.ts new file mode 100644 index 00000000..8a500f73 --- /dev/null +++ b/packages/guilded.ts/src/structures/forum/ForumTopicCommentReaction.ts @@ -0,0 +1,78 @@ +import { APIForumTopicCommentReaction } from 'guilded-api-typings'; +import { Base } from '../Base'; +import { ForumChannel } from '../channel/ForumChannel'; +import { ForumTopicComment } from './ForumTopicComment'; +import { ForumTopic } from './ForumTopic'; + +/** + * Represents a forum comment reaction on Guilded + */ +export class ForumTopicCommentReaction extends Base { + /** + * @param channel The forum channel + * @param forumTopic The forum topic + * @param forumTopicComment The forum topic comment + * @param data The data of the forum topic comment reaction + * @param cache Whether to cache the forum topic comment reaction + */ + constructor( + public readonly channel: ForumChannel, + public readonly forumTopic: ForumTopic, + public readonly forumTopicComment: ForumTopicComment, + public readonly data: APIForumTopicCommentReaction, + cache = channel.client.options.cacheForumTopicComments ?? true, + ) { + super(channel.client); + if (cache) this.forumTopicComment.reactions.cache.set(this.emote.id, this); + } + + /** + * Whether the forum topic reaction is cached + */ + get isCached() { + return this.forumTopicComment.reactions.cache.has(this.emote.id); + } + + /** + * The ID of the user that created the forum topic reaction + */ + get creatorId() { + return this.data.createdBy; + } + + /** + * The user that created the forum topic reaction + */ + get creator() { + return this.client.users.cache.get(this.creatorId) ?? null; + } + + /** + * Whether the client user created the forum topic reaction + */ + get isCreator() { + return this.creatorId === this.client.user?.id; + } + + /** + * The emote + */ + get emote() { + return this.data.emote; + } + + /** + * Fetch the user that created the forum topic comment reaction + * @returns The fetched user + */ + async fetchCreator() { + return this.client.users.fetch(this.forumTopic.channel.serverId, this.creatorId); + } + + /** + * Remove the reaction from the forum topic comment + */ + remove() { + return this.forumTopicComment.reactions.remove(this.emote.id); + } +} diff --git a/packages/guilded.ts/src/ws/events/forumTopic.ts b/packages/guilded.ts/src/ws/events/forumTopic.ts index 7a3352ee..33156980 100644 --- a/packages/guilded.ts/src/ws/events/forumTopic.ts +++ b/packages/guilded.ts/src/ws/events/forumTopic.ts @@ -6,10 +6,17 @@ import { WebSocketForumTopicUnlockEventData, WebSocketForumTopicUnpinEventData, WebSocketForumTopicUpdateEventData, + WebSocketForumTopicCommentCreateEventData, + WebSocketForumTopicCommentUpdateEventData, + WebSocketForumTopicCommentDeleteEventData, + WebSocketForumTopicCommentReactionAddEventData, + WebSocketForumTopicCommentReactionRemoveEventData, } from 'guilded-api-typings'; import { ForumChannel } from '../../structures/channel/ForumChannel'; import { Client } from '../../structures/Client'; -import { ForumTopic } from '../../structures/ForumTopic'; +import { ForumTopicComment } from '../../structures/forum/ForumTopicComment'; +import { ForumTopic } from '../../structures/forum/ForumTopic'; +import { ForumTopicCommentReaction } from '../../structures/forum/ForumTopicCommentReaction'; /** * Handle the `ForumTopicCreated` event @@ -89,3 +96,71 @@ export async function unlocked(client: Client, data: WebSocketForumTopicUnlockEv const forumTopic = new ForumTopic(channel, data.forumTopic); client.emit('forumTopicUnlock', forumTopic); } + +/** + * Handle the `ForumTopicCommentCreated` event + */ +export async function commentCreated( + client: Client, + data: WebSocketForumTopicCommentCreateEventData, +) { + const channel = (await client.channels.fetch(data.forumTopicComment.channelId)) as ForumChannel; + const topic = await channel.topics.fetch(data.forumTopicComment.forumTopicId); + const comment = new ForumTopicComment(channel, topic, data.forumTopicComment); + client.emit('forumTopicCommentCreate', comment); +} + +/** + * Handle the `ForumTopicCommentUpdated` event + */ +export async function commentUpdated( + client: Client, + data: WebSocketForumTopicCommentUpdateEventData, +) { + const channel = (await client.channels.fetch(data.forumTopicComment.channelId)) as ForumChannel; + const topic = await channel.topics.fetch(data.forumTopicComment.forumTopicId); + const oldComment = topic.comments.cache.get(data.forumTopicComment.id); + const newComment = new ForumTopicComment(channel, topic, data.forumTopicComment); + client.emit('forumTopicCommentEdit', newComment, oldComment); +} + +/** + * Handle the `ForumTopicCommentUpdated` event + */ +export async function commentDeleted( + client: Client, + data: WebSocketForumTopicCommentDeleteEventData, +) { + const channel = (await client.channels.fetch(data.forumTopicComment.channelId)) as ForumChannel; + const topic = await channel.topics.fetch(data.forumTopicComment.forumTopicId); + const comment = new ForumTopicComment(channel, topic, data.forumTopicComment); + client.emit('forumTopicCommentDelete', comment); +} + +/** + * Handle the `ForumTopicCommentUpdated` event + */ +export async function commentReactionAdd( + client: Client, + data: WebSocketForumTopicCommentReactionAddEventData, +) { + const channel = (await client.channels.fetch(data.reaction.channelId)) as ForumChannel; + const topic = await channel.topics.fetch(data.reaction.forumTopicId); + const comment = await topic.comments.fetch(Number(data.reaction.forumTopicCommentId)); + const reaction = new ForumTopicCommentReaction(channel, topic, comment, data.reaction); + client.emit('forumTopicCommentReactionAdd', reaction); +} + +/** + * Handle the `ForumTopicCommentUpdated` event + */ +export async function commentReactionRemove( + client: Client, + data: WebSocketForumTopicCommentReactionRemoveEventData, +) { + const channel = (await client.channels.fetch(data.reaction.channelId)) as ForumChannel; + const topic = await channel.topics.fetch(data.reaction.forumTopicId); + const comment = await topic.comments.fetch(Number(data.reaction.forumTopicCommentId)); + const reaction = new ForumTopicCommentReaction(channel, topic, comment, data.reaction); + client.emit('forumTopicCommentReactionRemove', reaction); +} diff --git a/packages/guilded.ts/src/ws/index.ts b/packages/guilded.ts/src/ws/index.ts index 7f322760..1f4bff07 100644 --- a/packages/guilded.ts/src/ws/index.ts +++ b/packages/guilded.ts/src/ws/index.ts @@ -47,6 +47,11 @@ const WSEventHandler: { [WebSocketEvent.ForumTopicUnpin]: forumTopic.unpinned, [WebSocketEvent.ForumTopicLock]: forumTopic.locked, [WebSocketEvent.ForumTopicUnlock]: forumTopic.unlocked, + [WebSocketEvent.ForumTopicCommentCreate]: forumTopic.commentCreated, + [WebSocketEvent.ForumTopicCommentUpdate]: forumTopic.commentUpdated, + [WebSocketEvent.ForumTopicCommentDelete]: forumTopic.commentDeleted, + [WebSocketEvent.ForumTopicCommentReactionAdd]: forumTopic.commentReactionAdd, + [WebSocketEvent.ForumTopicCommentReactionRemove]: forumTopic.commentReactionRemove, [WebSocketEvent.CalendarEventRsvpUpdate]: calendarEvent.rsvpUpdated, [WebSocketEvent.CalendarEventRsvpsUpdate]: calendarEvent.rsvpsUpdated, [WebSocketEvent.CalendarEventRsvpDelete]: calendarEvent.rsvpDeleted, diff --git a/packages/rest/src/index.ts b/packages/rest/src/index.ts index c7332fa4..f9d63a92 100644 --- a/packages/rest/src/index.ts +++ b/packages/rest/src/index.ts @@ -9,7 +9,8 @@ export * from './routers/server/ServerRouter'; export * from './routers/BaseRouter'; export * from './routers/ChannelRouter'; export * from './routers/DocRouter'; -export * from './routers/ForumTopicRouter'; +export * from './routers/forum/ForumTopicRouter'; +export * from './routers/forum/ForumTopicCommentRouter'; export * from './routers/GroupRouter'; export * from './routers/ListItemRouter'; export * from './routers/MessageRouter'; diff --git a/packages/rest/src/routers/Router.ts b/packages/rest/src/routers/Router.ts index 1ef7b90a..7f76705d 100644 --- a/packages/rest/src/routers/Router.ts +++ b/packages/rest/src/routers/Router.ts @@ -7,13 +7,15 @@ import { ServerRouter } from './server/ServerRouter'; import { BaseRouter } from './BaseRouter'; import { ChannelRouter } from './ChannelRouter'; import { DocRouter } from './DocRouter'; -import { ForumTopicRouter } from './ForumTopicRouter'; +import { ForumTopicRouter } from './forum/ForumTopicRouter'; import { GroupRouter } from './GroupRouter'; import { ListItemRouter } from './ListItemRouter'; import { MessageRouter } from './MessageRouter'; import { ReactionRouter } from './ReactionRouter'; import { WebhookRouter } from './WebhookRouter'; import { RESTManager } from '../RESTManager'; +import { ForumTopicCommentRouter } from './forum/ForumTopicCommentRouter'; +import { ForumTopicCommentReactionRouter } from './forum/ForumTopicCommentReactionRouter'; /** * The router for the Guilded REST API @@ -39,6 +41,10 @@ export class Router extends BaseRouter { * The forum topic router for the Guilded REST API */ readonly forumTopics: ForumTopicRouter; + /** + * The forum topic comments router for the Guilded REST API + */ + readonly forumTopicComments: ForumTopicCommentRouter; /** * The list item router for the Guilded REST API */ @@ -51,6 +57,10 @@ export class Router extends BaseRouter { * The reaction router for the Guilded REST API */ readonly reactions: ReactionRouter; + /** + * The reaction router for the Guilded REST API + */ + readonly forumTopicCommentReactions: ForumTopicCommentReactionRouter; /** * The server role router for the Guilded REST API */ @@ -86,9 +96,11 @@ export class Router extends BaseRouter { this.serverMembers = new ServerMemberRouter(rest); this.serverBans = new ServerBanRouter(rest); this.forumTopics = new ForumTopicRouter(rest); + this.forumTopicComments = new ForumTopicCommentRouter(rest); this.listItems = new ListItemRouter(rest); this.docs = new DocRouter(rest); this.reactions = new ReactionRouter(rest); + this.forumTopicCommentReactions = new ForumTopicCommentReactionRouter(rest); this.serverRoles = new ServerRoleRouter(rest); this.groups = new GroupRouter(rest); this.webhooks = new WebhookRouter(rest); diff --git a/packages/rest/src/routers/forum/ForumTopicCommentReactionRouter.ts b/packages/rest/src/routers/forum/ForumTopicCommentReactionRouter.ts new file mode 100644 index 00000000..61dcbca9 --- /dev/null +++ b/packages/rest/src/routers/forum/ForumTopicCommentReactionRouter.ts @@ -0,0 +1,33 @@ +import { Routes } from 'guilded-api-typings'; +import { BaseRouter } from '../BaseRouter'; + +/** + * The reaction router for the Guilded REST API + */ +export class ForumTopicCommentReactionRouter extends BaseRouter { + /** + * Create a reaction on Guilded + * @param channelId The ID of the channel + * @param topicId The ID of the topic + * @param commentId The ID of the content + * @param emojiId The ID of the emoji + */ + create(channelId: string, topicId: number, commentId: number, emojiId: number) { + return this.rest.put( + Routes.forumTopicCommentReaction(channelId, topicId, commentId, emojiId), + ); + } + + /** + * Delete a reaction from Guilded + * @param channelId The ID of the channel + * @param topicId The ID of the topic + * @param commentId The ID of the content + * @param emojiId The ID of the emoji + */ + delete(channelId: string, topicId: number, commentId: number, emojiId: number) { + return this.rest.delete( + Routes.forumTopicCommentReaction(channelId, topicId, commentId, emojiId), + ); + } +} diff --git a/packages/rest/src/routers/forum/ForumTopicCommentRouter.ts b/packages/rest/src/routers/forum/ForumTopicCommentRouter.ts new file mode 100644 index 00000000..0b1e2870 --- /dev/null +++ b/packages/rest/src/routers/forum/ForumTopicCommentRouter.ts @@ -0,0 +1,108 @@ +import { + RESTGetForumTopicsQuery, + Routes, + APIForumTopicComment, + RESTPostForumTopicCommentJSONBody, + RESTPatchForumTopicCommentJSONBody, +} from 'guilded-api-typings'; +import { BaseRouter } from '../BaseRouter'; + +/** + * The forum topic router for the Guilded REST API + */ +export class ForumTopicCommentRouter extends BaseRouter { + /** + * Fetch forum topic comments from Guilded + * @param channelId The ID of the channel + * @param forumTopicId The ID of the forum topic + * @returns The fetched forum topic + */ + fetch( + channelId: string, + forumTopicId: number, + forumTopicCommentId?: number, + ): Promise; + /** + * Fetch a forum topic comment from Guilded + * @param channelId The ID of the channel + * @param forumTopicId The ID of the forum topic + * @param forumTopicCommentId The ID of the forum topic comment + * @returns The fetched forum topic + */ + fetch(channelId: string, forumTopicId: number): Promise; + fetch(channelId: string, forumTopicId: number, forumTopicCommentId?: number) { + if (typeof forumTopicCommentId === 'number') + return this.fetchSingle(channelId, forumTopicId, forumTopicCommentId); + else return this.fetchMany(channelId, forumTopicId); + } + + private async fetchSingle( + channelId: string, + forumTopicId: number, + forumTopicCommentId: number, + ) { + const { forumTopicComment } = await this.rest.get<{ + forumTopicComment: APIForumTopicComment; + }>(Routes.forumTopicComment(channelId, forumTopicId, forumTopicCommentId)); + return forumTopicComment; + } + + private async fetchMany(channelId: string, forumTopicId: number) { + const { forumTopicComments } = await this.rest.get< + { forumTopicComments: APIForumTopicComment[] }, + RESTGetForumTopicsQuery + >(Routes.forumTopicComments(channelId, forumTopicId)); + return forumTopicComments; + } + + /** + * Create a forum topic comment on Guilded + * @param channelId The ID of the channel + * @param payload The payload of the forum topic + * @returns The created forum topic + */ + async create( + channelId: string, + forumTopicId: number, + payload: RESTPostForumTopicCommentJSONBody, + ) { + const { forumTopicComment } = await this.rest.post< + { forumTopicComment: APIForumTopicComment }, + RESTPostForumTopicCommentJSONBody + >(Routes.forumTopicComments(channelId, forumTopicId), payload); + return forumTopicComment; + } + + /** + * Edit a forum topic comment on Guilded + * @param channelId The ID of the channel + * @param forumTopicId The ID of the forum topic + * @param forumTopicCommentId The ID of the forum topic comment + * @param payload The payload of the forum topic + * @returns The edited forum topic + */ + async edit( + channelId: string, + forumTopicId: number, + forumTopicCommentId: number, + payload: RESTPatchForumTopicCommentJSONBody, + ) { + const { forumTopicComment } = await this.rest.patch< + { forumTopicComment: APIForumTopicComment }, + RESTPatchForumTopicCommentJSONBody + >(Routes.forumTopicComment(channelId, forumTopicId, forumTopicCommentId), payload); + return forumTopicComment; + } + + /** + * Delete a forum topic from Guilded + * @param channelId The ID of the channel + * @param forumTopicId The ID of the forum topic + * @param forumTopicCommentId The ID of the forum topic comment + */ + delete(channelId: string, forumTopicId: number, forumTopicCommentId: number) { + return this.rest.delete( + Routes.forumTopicComment(channelId, forumTopicId, forumTopicCommentId), + ); + } +} diff --git a/packages/rest/src/routers/ForumTopicRouter.ts b/packages/rest/src/routers/forum/ForumTopicRouter.ts similarity index 98% rename from packages/rest/src/routers/ForumTopicRouter.ts rename to packages/rest/src/routers/forum/ForumTopicRouter.ts index 383bbb19..7381a21a 100644 --- a/packages/rest/src/routers/ForumTopicRouter.ts +++ b/packages/rest/src/routers/forum/ForumTopicRouter.ts @@ -6,7 +6,7 @@ import { Routes, RESTPostForumTopicJSONBody, } from 'guilded-api-typings'; -import { BaseRouter } from './BaseRouter'; +import { BaseRouter } from '../BaseRouter'; /** * The forum topic router for the Guilded REST API diff --git a/packages/ws/src/WebsocketManager.ts b/packages/ws/src/WebsocketManager.ts index 509c6a8e..eef513d4 100644 --- a/packages/ws/src/WebsocketManager.ts +++ b/packages/ws/src/WebsocketManager.ts @@ -134,7 +134,7 @@ export class WebsocketManager extends EventEmitter { } /** - * Handle recieved data + * Handle received data * @param data The data */ private onSocketMessage(data: WebSocketPayload) {