diff --git a/handlers/HandlerManager.ts b/handlers/HandlerManager.ts new file mode 100644 index 0000000..3a95ec7 --- /dev/null +++ b/handlers/HandlerManager.ts @@ -0,0 +1 @@ +export * from "./MessageRelated.ts"; \ No newline at end of file diff --git a/handlers/MessageRelated.ts b/handlers/MessageRelated.ts new file mode 100644 index 0000000..b03f7b5 --- /dev/null +++ b/handlers/MessageRelated.ts @@ -0,0 +1,18 @@ +import type { DiscordMessage, DiscordReady } from "../vendor/external.ts"; +import type { Session } from "../session/Session.ts"; +import { Message } from "../structures/Message.ts"; + +type Handler = (...args: [ Session, number, T ]) => void; + +// TODO: move this lol +export const READY: Handler = (session, shardId, payload) => { + session.emit("ready", shardId, payload); +}; + +export const MESSAGE_CREATE: Handler = (session, _shardId, message) => { + session.emit("messageCreate", new Message(session, message)); +}; + +export const raw: Handler = (session, shardId, data) => { + session.emit("raw", data, shardId); +} \ No newline at end of file diff --git a/handlers/mod.ts b/handlers/mod.ts deleted file mode 100644 index e69de29..0000000 diff --git a/mod.ts b/mod.ts index d0e61e4..f869556 100644 --- a/mod.ts +++ b/mod.ts @@ -1,5 +1,3 @@ export * from "./session/mod.ts"; export * from "./util/mod.ts"; -export * from "./structures/mod.ts"; export * from "./vendor/external.ts"; -export * from "./handlers/mod.ts"; diff --git a/session/Events.ts b/session/Events.ts index e0a7ff9..d024a6c 100644 --- a/session/Events.ts +++ b/session/Events.ts @@ -1,9 +1,10 @@ -import type { DiscordGatewayPayload, DiscordMessage, DiscordReady, Shard } from "../vendor/external.ts"; +import type { DiscordGatewayPayload, DiscordReady, Shard } from "../vendor/external.ts"; +import type { Message } from "../structures/Message.ts"; export type DiscordRawEventHandler = (shard: Shard, data: DiscordGatewayPayload) => unknown; export interface Events { - ready(payload: DiscordReady, shardId: number): unknown; - messageCreate(message: DiscordMessage): unknown; + ready(shardId: number, payload: DiscordReady): unknown; + messageCreate(message: Message): unknown; raw(data: DiscordGatewayPayload, shardId: number): unknown; } diff --git a/session/Session.ts b/session/Session.ts index a189c16..d8e3592 100644 --- a/session/Session.ts +++ b/session/Session.ts @@ -1,12 +1,7 @@ import type { - DiscordGatewayPayload, DiscordGetGatewayBot, - DiscordMessage, - DiscordReady, GatewayBot, - GatewayDispatchEventNames, GatewayIntents, - Shard, } from "../vendor/external.ts"; import { EventEmitter, Routes, Snowflake } from "../util/mod.ts"; @@ -15,6 +10,8 @@ import type { DiscordRawEventHandler, Events } from "./Events.ts"; import { createGatewayManager, createRestManager } from "../vendor/external.ts"; +import * as Actions from "../handlers/HandlerManager.ts"; + export interface RestOptions { secretKey?: string; applicationId?: Snowflake; @@ -48,11 +45,14 @@ export class Session extends EventEmitter { this.options = options; const defHandler: DiscordRawEventHandler = (shard, data) => { - this.emit("raw", data, shard.id); + Actions.raw(this, shard.id, data); - if (!data.t) return; + if (!data.t) { + return; + } - this.emit(data.t as GatewayDispatchEventNames, data, shard.id); + // deno-lint-ignore no-explicit-any + Actions[data.t as keyof typeof Actions]?.(this, shard.id, data.d as any); }; this.rest = createRestManager({ @@ -75,31 +75,25 @@ export class Session extends EventEmitter { // TODO: set botId in Session.botId or something } - /** TODO: move this */ - static #toSnakeCase(str: string) { - // probably not a fast implementation - return str.replace(/[A-Z]/g, (char) => "_" + char.toLowerCase()); - } - override on(event: "ready", func: Events["ready"]): this; override on(event: "messageCreate", func: Events["messageCreate"]): this; override on(event: "raw", func: Events["raw"]): this; override on(event: keyof Events, func: Events[keyof Events]): this { - return super.on(Session.#toSnakeCase(event).toUpperCase(), func); + return super.on(event, func); } override off(event: "ready", func: Events["ready"]): this; override off(event: "messageCreate", func: Events["messageCreate"]): this; override off(event: "raw", func: Events["raw"]): this; override off(event: keyof Events, func: Events[keyof Events]): this { - return super.off(Session.#toSnakeCase(event).toUpperCase(), func); + return super.off(event, func); } override once(event: "ready", func: Events["ready"]): this; override once(event: "messageCreate", func: Events["messageCreate"]): this; override once(event: "raw", func: Events["raw"]): this; override once(event: keyof Events, func: Events[keyof Events]): this { - return super.once(Session.#toSnakeCase(event).toUpperCase(), func); + return super.once(event, func); } async start() { diff --git a/structures/Base.ts b/structures/Base.ts new file mode 100644 index 0000000..031020e --- /dev/null +++ b/structures/Base.ts @@ -0,0 +1,12 @@ +import type { Snowflake } from "../util/mod.ts"; +import type { Session } from "../session/mod.ts"; + +/** + * Represents a Discord data model + * */ +export interface Base { + /** id of the model */ + id: Snowflake; + /** reference to the client that instantiated the model */ + session: Session; +} \ No newline at end of file diff --git a/structures/Message.ts b/structures/Message.ts new file mode 100644 index 0000000..3d4dd67 --- /dev/null +++ b/structures/Message.ts @@ -0,0 +1,94 @@ +import type { Base } from "./Base.ts"; +import type { Snowflake } from "../util/Snowflake.ts"; +import type { Session } from "../session/mod.ts"; +import type { DiscordMessage, AllowedMentionsTypes } from "../vendor/external.ts"; +import { Routes } from "../util/mod.ts"; + +/** + * @link https://discord.com/developers/docs/resources/channel#allowed-mentions-object + * */ +export interface AllowedMentions { + parse?: AllowedMentionsTypes[]; + repliedUser?: boolean; + roles?: Snowflake[]; + users?: Snowflake[]; +} + +/** + * @link https://discord.com/developers/docs/resources/channel#edit-message-json-params + * */ +export interface EditMessage { + content?: string; + allowedMentions?: AllowedMentions; +} + +/** + * @link https://discord.com/developers/docs/resources/channel#create-message-json-params + * */ +export interface CreateMessage { + content?: string; + allowedMentions?: AllowedMentions; +} + +/** + * Represents a message + * @link https://discord.com/developers/docs/resources/channel#message-object + * */ +export class Message implements Base { + constructor(session: Session, data: DiscordMessage) { + this.session = session; + + this.id = data.id; + + this.channelId = data.channel_id; + } + + /** the session that instantiated the message */ + session: Session; + + /** the id of the message */ + id: Snowflake; + + /** the id of the channel where the message was sent */ + channelId: Snowflake; + + /** Edits the current message */ + async edit({ content, allowedMentions }: EditMessage): Promise { + const message = await this.session.rest.runMethod( + this.session.rest, + "POST", + Routes.CHANNEL_MESSAGE(this.id, this.channelId), + { + content, + allowed_mentions: { + parse: allowedMentions?.parse, + roles: allowedMentions?.roles, + users: allowedMentions?.users, + replied_user: allowedMentions?.repliedUser, + }, + } + ); + + return message; + } + + /** Responds directly in the channel the message was sent */ + async respond({ content, allowedMentions }: CreateMessage): Promise { + const message = await this.session.rest.runMethod( + this.session.rest, + "POST", + Routes.CHANNEL_MESSAGES(this.channelId), + { + content, + allowed_mentions: { + parse: allowedMentions?.parse, + roles: allowedMentions?.roles, + users: allowedMentions?.users, + replied_user: allowedMentions?.repliedUser, + }, + } + ); + + return message; + } +} \ No newline at end of file diff --git a/structures/mod.ts b/structures/mod.ts deleted file mode 100644 index e69de29..0000000 diff --git a/util/Routes.ts b/util/Routes.ts index 0a13468..5819ba4 100644 --- a/util/Routes.ts +++ b/util/Routes.ts @@ -1,3 +1,43 @@ +import type { Snowflake } from "./Snowflake.ts"; + export function GATEWAY_BOT() { return "/gateway/bot"; } + +export interface GetMessagesOptions { + limit?: number; +} + +export interface GetMessagesOptions { + around?: Snowflake; + limit?: number; +} + +export interface GetMessagesOptions { + before?: Snowflake; + limit?: number; +} + +export interface GetMessagesOptions { + after?: Snowflake; + limit?: number; +} + +/** used to send messages */ +export function CHANNEL_MESSAGES(channelId: Snowflake, options?: GetMessagesOptions) { + let url = `/channels/${channelId}/messages?`; + + if (options) { + if ("after" in options && options.after) url += `after=${options.after}`; + if ("before" in options && options.before) url += `&before=${options.before}`; + if ("around" in options && options.around) url += `&around=${options.around}`; + if ("limit" in options && options.limit) url += `&limit=${options.limit}`; + } + + return url; +} + +/** used to edit messages */ +export function CHANNEL_MESSAGE(channelId: Snowflake, messageId: Snowflake) { + return `/channels/${channelId}/messages/${messageId}`; +} \ No newline at end of file