Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: forum topics #171

Merged
merged 4 commits into from
Sep 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/green-countries-promise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@guildedjs/guilded-api-typings": patch
"guilded.js": patch
"@guildedjs/rest": patch
---

feat: forum topics
64 changes: 63 additions & 1 deletion packages/guilded-api-typings/lib/v1/rest/Forum.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ForumTopicPayload } from "../structs/Forum";
import type { ForumTopicPayload, ForumTopicSummaryPayload } from "../structs/Forum";

/**
* POST
Expand All @@ -11,3 +11,65 @@ export interface RESTPostForumTopicBody {
title: string;
content: string;
}

/**
* PUT
* /channels/:channelId/topics/:forumTopicId/pin
*/
export type RESTPutForumTopicPinResult = never;

/**
* DELETE
* /channels/:channelId/topics/:forumTopicId/pin
*/
export type RESTDeleteForumTopicPinResult = never;

/**
* POST
* /channels/:channelId/topics
*/
export interface RESTPostForumTopicsBody {
title: string;
content: string;
}
export interface RESTPostForumTopicsResult {
forumTopic: ForumTopicPayload;
}

/**
* GET
* /channels/:channelId/topics
*/
export interface RESTGetForumTopicsQuery {
before?: string;
limit?: number;
}
export interface RESTGetForumTopicsResult {
forumTopics: ForumTopicSummaryPayload[];
}

/**
* GET
* /channels/:channelId/topics/:forumTopicId
*/
export interface RESTGetForumTopicResult {
forumTopic: ForumTopicPayload;
}

/**
* PATCH
* /channels/:channelId/topics/:forumTopicId
*/
export interface RESTPatchForumTopicBody {
title: string;
content: string;
}
export interface RESTPatchForumTopicResult {
forumTopic: ForumTopicPayload;
}

/**
* DELETE
* /channels/:channelId/topics/:forumTopicId
*/
export type RESTDeleteForumTopicResult = never;
19 changes: 17 additions & 2 deletions packages/guilded-api-typings/lib/v1/structs/Forum.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { MentionsPayload } from "./Message";

export interface ForumTopicPayload {
/** The ID of the forum topic */
id: number;
Expand All @@ -6,13 +8,26 @@ export interface ForumTopicPayload {
/** The ID of the channel this forum topic belongs to */
channelId: string;
/** The title of the forum topic */
title?: string;
title: string;
/** The content of the forum topic */
content?: string;
content: string;
/** The ISO 8601 timestamp that the forum topic was created at */
createdAt: string;
/** The ID of the user who created this forum topic (Note: If this event has createdByWebhookId present, this field will still be populated, but can be ignored. In this case, the value of this field will always be Ann6LewA) */
createdBy: string;
/** The ID of the webhook who created this forum topic, if it was created by a webhook */
createdByWebhookId?: string;
/** When, if at all, this forum topic was updated */
updatedAt?: string;
/** When, if at all, this forum topic was bumped */
bumpedAt?: string;
/** Whether this forum topic is pinned */
isPinned?: boolean;
/** The mentions within this forum topic */
mentions?: MentionsPayload;
}

export type ForumTopicSummaryPayload = Pick<
ForumTopicPayload,
"id" | "serverId" | "channelId" | "title" | "createdAt" | "createdBy" | "createdByWebhookId" | "updatedAt" | "bumpedAt" | "isPinned"
>;
5 changes: 5 additions & 0 deletions packages/guilded-api-typings/lib/v1/ws/Events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ export const WebSocketEvents = {
DocDeleted: "DocDeleted",
ChannelMessageReactionCreated: "ChannelMessageReactionCreated",
ChannelMessageReactionDeleted: "ChannelMessageReactionDeleted",
ForumTopicCreated: "ForumTopicCreated",
ForumTopicUpdated: "ForumTopicUpdated",
ForumTopicDeleted: "ForumTopicDeleted",
ForumTopicPinned: "ForumTopicPinned",
ForumTopicUnpinned: "ForumTopicUnpinned",
} as const;
export type WSEvent = typeof WebSocketEvents;

Expand Down
42 changes: 42 additions & 0 deletions packages/guilded-api-typings/lib/v1/ws/Forum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { ForumTopicPayload } from "../structs";
import type { SkeletonWSPayload, WSEvent } from "./Events";

export interface WSForumTopicCreated extends SkeletonWSPayload {
d: {
serverId: string;
forumTopic: ForumTopicPayload;
};
t: WSEvent["ForumTopicCreated"];
}

export interface WSForumTopicUpdated extends SkeletonWSPayload {
d: {
serverId: string;
forumTopic: ForumTopicPayload;
};
t: WSEvent["ForumTopicUpdated"];
}

export interface WSForumTopicDeleted extends SkeletonWSPayload {
d: {
serverId: string;
forumTopic: ForumTopicPayload;
};
t: WSEvent["ForumTopicDeleted"];
}

export interface WSForumTopicPinned extends SkeletonWSPayload {
d: {
serverId: string;
forumTopic: ForumTopicPayload;
};
t: WSEvent["ForumTopicPinned"];
}

export interface WSForumTopicUnpinned extends SkeletonWSPayload {
d: {
serverId: string;
forumTopic: ForumTopicPayload;
};
t: WSEvent["ForumTopicUnpinned"];
}
1 change: 1 addition & 0 deletions packages/guilded-api-typings/lib/v1/ws/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from "./CalendarEvent";
export * from "./Channel";
export * from "./Doc";
export * from "./Events";
export * from "./Forum";
export * from "./List";
export * from "./Member";
export * from "./Message";
Expand Down
2 changes: 1 addition & 1 deletion packages/guilded.js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ client.login();

<a href="https://npmjs.org/package/guilded.js"><img src="https://nodei.co/npm/guilded.js.png" alt="NPM"></a>

**Recommended that you use node v12+**
**Recommended that you use node v16+**

- `npm install guilded.js`
- `yarn add guilded.js`
Expand Down
5 changes: 5 additions & 0 deletions packages/guilded.js/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ export const constants = {
SERVER_CREATED: "serverCreated",
MESSAGE_REACTION_CREATED: "messageReactionCreated",
MESSAGE_REACTION_DELETED: "messageReactionDeleted",
FORUM_TOPIC_CREATED: "forumTopicCreated",
FORUM_TOPIC_DELETED: "forumTopicDeleted",
FORUM_TOPIC_UPDATED: "forumTopicUpdated",
FORUM_TOPIC_PINNED: "forumTopicPinned",
FORUM_TOPIC_UNPINNED: "forumTopicUnpinned",
},
} as const;
export type ClientEvent = typeof constants.clientEvents;
Expand Down
21 changes: 19 additions & 2 deletions packages/guilded.js/lib/gateway/ClientGatewayHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ import type {
WSCalendarEventUpdated,
WSCalendarEventRsvpUpdated,
WSCalendarEventRsvpManyUpdated,
WSCalendarEventRsvpDeleted
WSCalendarEventRsvpDeleted,
WSForumTopicCreated,
WSForumTopicDeleted,
WSForumTopicUpdated,
WSForumTopicPinned,
WSForumTopicUnpinned
} from "@guildedjs/guilded-api-typings";
import { WebSocketEvents } from "@guildedjs/guilded-api-typings";
import { TeamWebhookEventHandler } from "./handler/TeamWebhookEventHandler";
Expand All @@ -43,6 +48,7 @@ import { TeamChannelEventHandler } from "./handler/TeamChannelEventHandler";
import { DocEventHandler } from "./handler/DocEventHandler";
import { ReactionEventHandler } from "./handler/ReactionEventHandler";
import { CalendarEventHandler, CalendarEventRsvpHandler } from "./handler/CalendarEventHandler";
import { ForumEventHandler } from "./handler/ForumEventHandler";

export class ClientGatewayHandler {
calendarEventHandler = new CalendarEventHandler(this.client);
Expand All @@ -55,6 +61,7 @@ export class ClientGatewayHandler {
teamChannelHandler = new TeamChannelEventHandler(this.client);
docHandler = new DocEventHandler(this.client);
reactionHandler = new ReactionEventHandler(this.client);
forumHandler = new ForumEventHandler(this.client);

readonly eventToHandlerMap: Record<keyof WSEvent, (data: SkeletonWSPayload) => boolean> = {
[WebSocketEvents.CalendarEventCreated]: (data) => this.calendarEventHandler.calendarEventCreated(data as WSCalendarEventCreated),
Expand Down Expand Up @@ -89,8 +96,18 @@ export class ClientGatewayHandler {
this.reactionHandler.messageReactionCreated(data as WSChannelMessageReactionCreatedPayload),
[WebSocketEvents.ChannelMessageReactionDeleted]: (data) =>
this.reactionHandler.messageReactionDeleted(data as WSChannelMessageReactionDeletedPayload),
[WebSocketEvents.ForumTopicCreated]: (data) =>
this.forumHandler.forumTopicCreated(data as WSForumTopicCreated),
[WebSocketEvents.ForumTopicDeleted]: (data) =>
this.forumHandler.forumTopicDeleted(data as WSForumTopicDeleted),
[WebSocketEvents.ForumTopicUpdated]: (data) =>
this.forumHandler.forumTopicUpdated(data as WSForumTopicUpdated),
[WebSocketEvents.ForumTopicPinned]: (data) =>
this.forumHandler.forumTopicPinned(data as WSForumTopicPinned),
[WebSocketEvents.ForumTopicUnpinned]: (data) =>
this.forumHandler.forumTopicUnpinned(data as WSForumTopicUnpinned)
};

constructor(public readonly client: Client) {}
handleWSMessage(event: keyof WSEvent, data: SkeletonWSPayload): void {
this.eventToHandlerMap[event]?.(data) ?? this.client.emit("unknownGatewayEvent", data);
Expand Down
53 changes: 53 additions & 0 deletions packages/guilded.js/lib/gateway/handler/ForumEventHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type {
WSListItemUncompleted,
WSListItemCompleted,
WSListItemCreated,
WSListItemUpdated,
WSListItemDeleted,
WSForumTopicCreated,
WSForumTopicUpdated,
WSForumTopicDeleted,
WSForumTopicPinned,
WSForumTopicUnpinned,
} from "@guildedjs/guilded-api-typings";
import { constants } from "../../constants";
import type { ListChannel } from "../../structures";
import { ForumTopic } from "../../structures/Forum";
import { GatewayEventHandler } from "./GatewayEventHandler";

export class ForumEventHandler extends GatewayEventHandler {
forumTopicCreated(data: WSForumTopicCreated) {
// This is in the case that a REST request beats us to adding the topic in the cache.
const existingTopic = this.client.topics.cache.get(data.d.forumTopic.id);
if (existingTopic) return this.client.emit(constants.clientEvents.FORUM_TOPIC_CREATED, existingTopic);

const newTopic = new ForumTopic(this.client, data.d.forumTopic);
if(this.client.topics.shouldCacheForumTopic) this.client.topics.cache.set(newTopic.id, newTopic);
return this.client.emit(constants.clientEvents.FORUM_TOPIC_CREATED, newTopic);
}
forumTopicUpdated(data: WSForumTopicUpdated) {
const getCachedtopic = this.client.topics.cache.get(data.d.forumTopic.id);
if (!getCachedtopic) {
const newtopic = new ForumTopic(this.client, data.d.forumTopic);
return this.client.emit(constants.clientEvents.FORUM_TOPIC_UPDATED, newtopic, null);
}
const frozenOldtopic = Object.freeze(getCachedtopic._clone());
getCachedtopic._update(data.d.forumTopic);
return this.client.emit(constants.clientEvents.FORUM_TOPIC_UPDATED, getCachedtopic, frozenOldtopic);
}
forumTopicDeleted(data: WSForumTopicDeleted) {
const getCachedtopic = this.client.topics.cache.get(data.d.forumTopic.id);
getCachedtopic?._update({ _deletedAt: new Date() });
return this.client.emit(constants.clientEvents.FORUM_TOPIC_DELETED, getCachedtopic ?? new ForumTopic(this.client, data.d.forumTopic));
}
forumTopicPinned(data: WSForumTopicPinned) {
const getCachedtopic = this.client.topics.cache.get(data.d.forumTopic.id);
getCachedtopic?._update({ isPinned: true });
return this.client.emit(constants.clientEvents.FORUM_TOPIC_PINNED, getCachedtopic ?? new ForumTopic(this.client, data.d.forumTopic));
}
forumTopicUnpinned(data: WSForumTopicUnpinned) {
const getCachedtopic = this.client.topics.cache.get(data.d.forumTopic.id);
getCachedtopic?._update({ isPinned: false });
return this.client.emit(constants.clientEvents.FORUM_TOPIC_UNPINNED, getCachedtopic ?? new ForumTopic(this.client, data.d.forumTopic));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class MessageEventHandler extends GatewayEventHandler {
if (existingMessage) return this.client.emit(constants.clientEvents.MESSAGE_CREATED, existingMessage);

const newMessage = new Message(this.client, data.d.message);
this.client.messages.cache.set(newMessage.id, newMessage);
if (this.client.messages.shouldCacheMessage) this.client.messages.cache.set(newMessage.id, newMessage);
return this.client.emit(constants.clientEvents.MESSAGE_CREATED, newMessage);
}
messageUpdated(data: WSChatMessageUpdatedPayload): boolean {
Expand All @@ -26,6 +26,6 @@ export class MessageEventHandler extends GatewayEventHandler {
messageDeleted(data: WSChatMessageDeletedPayload): boolean {
const getCachedMessage = this.client.messages.cache.get(data.d.message.id);
getCachedMessage?._update({ deletedAt: data.d.message.deletedAt });
return this.client.emit(constants.clientEvents.MESSAGE_DELETED, getCachedMessage || data.d);
return this.client.emit(constants.clientEvents.MESSAGE_DELETED, getCachedMessage ?? data.d);
}
}
42 changes: 39 additions & 3 deletions packages/guilded.js/lib/managers/global/ForumManager.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,45 @@
import type { ForumTopicPayload, RESTPostForumTopicBody } from "@guildedjs/guilded-api-typings";
import type { ForumTopicPayload, RESTGetForumTopicsQuery, RESTPatchForumTopicBody, RESTPostForumTopicBody } from "@guildedjs/guilded-api-typings";
import type { ForumTopic } from "../../structures/Forum";
import { CacheableStructManager } from "./CacheableStructManager";
import { GlobalManager } from "./GlobalManager";

export class GlobalForumManager extends GlobalManager {
export class GlobalForumTopicManager extends CacheableStructManager<number, ForumTopic> {
get shouldCacheForumTopic() {
return this.client.options.cache?.cacheForumTopics !== false;
}

/** Create a topic in a forum */
createTopic(channelId: string, options: RESTPostForumTopicBody): Promise<ForumTopicPayload> {
createForumTopic(channelId: string, options: RESTPostForumTopicBody): Promise<ForumTopicPayload> {
return this.client.rest.router.createForumTopic(channelId, options).then((data) => data.forumTopic);
}

/** Get all topics in a forum */
getForumTopics(channelId: string, options: RESTGetForumTopicsQuery) {
return this.client.rest.router.getForumTopics(channelId, options);
}

/** Get a topic in a forum */
getForumTopic(channelId: string, forumThreadId: string) {
return this.client.rest.router.getForumTopic(channelId, forumThreadId);
}

/** Update a topic in a forum */
updateForumTopic(channelId: string, forumThreadId: string, options: RESTPatchForumTopicBody) {
return this.client.rest.router.updateForumTopic(channelId, forumThreadId, options);
}

/** Delete a topic in a forum */
deleteForumTopic(channelId: string, forumThreadId: string) {
return this.client.rest.router.deleteForumTopic(channelId, forumThreadId);
}

/** Pin a topic in a forum */
pinForumTopic(channelId: string, forumThreadId: string) {
return this.client.rest.router.pinForumTopic(channelId, forumThreadId);
}

/** Unpin a topic in a forum */
unpinForumTopic(channelId: string, forumThreadId: string) {
return this.client.rest.router.unpinForumTopic(channelId, forumThreadId);
}
}
4 changes: 4 additions & 0 deletions packages/guilded.js/lib/managers/global/MessageManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import type { MessageContent } from "../../typings";
import { CollectorOptions, MessageCollector } from "../../structures";

export class GlobalMessageManager extends CacheableStructManager<string, Message> {
get shouldCacheMessage() {
return this.client.options.cache?.cacheMessages !== false;
}

/** Get a list of the latest 50 messages from a channel. */
fetchMany(channelId: string, options: RESTGetChannelMessagesQuery): Promise<Collection<string, Message>> {
return this.client.rest.router.getChannelMessages(channelId, options).then((data) => {
Expand Down
Loading