From 107374725bd4c5f6b1ddeea61edc98b1e1676771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Susa=C3=B1a?= <66887817+socram03@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:08:40 -0400 Subject: [PATCH] Threads Members Update (#46) * PresenceUpdate * Thread Members Events * fix: fmt * fix: all fmt --- .github/ISSUE_TEMPLATE/bug_report.md | 16 +- .github/ISSUE_TEMPLATE/feature_request.md | 14 +- README.md | 57 ++++--- build.ts | 56 +++---- packages/biscuit/Actions.ts | 30 +++- .../biscuit/structures/MessageReaction.ts | 8 +- packages/biscuit/structures/guilds/Guild.ts | 8 +- packages/cache/mod.ts | 152 ++++++++++-------- packages/discordeno/gateway/shard/connect.ts | 6 +- packages/discordeno/rest/sendRequest.ts | 2 +- packages/discordeno/types/discord.ts | 2 + tests/bun.js | 2 +- 12 files changed, 203 insertions(+), 150 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index e338ae5..5fcc662 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,21 +7,17 @@ assignees: '' --- -**Describe the bug** -A clear and concise description of what the bug is. +**Describe the bug** A clear and concise description of what the bug is. -**Biscuit Version** -eg: 0.1.0-rc7 +**Biscuit Version** eg: 0.1.0-rc7 + +**To Reproduce** Steps to reproduce the behavior: -**To Reproduce** -Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error -**Expected behavior** -A clear and concise description of what you expected to happen. +**Expected behavior** A clear and concise description of what you expected to happen. -**etc** -Whatever +**etc** Whatever diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 82444b8..68ca28b 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,14 +7,12 @@ assignees: '' --- -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] +**Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem +is. Ex. I'm always frustrated when [...] -**Describe the solution you'd like** -A clear and concise description of what you want to happen. +**Describe the solution you'd like** A clear and concise description of what you want to happen. -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. +**Describe alternatives you've considered** A clear and concise description of any alternative solutions or features +you've considered. -**Limitations** -A set of limitations of the API or the library by itself +**Limitations** A set of limitations of the API or the library by itself diff --git a/README.md b/README.md index a5da5b6..63e4bf5 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,44 @@ # biscuit + ## A brand new bleeding edge non bloated Discord library - ### Install (for [node18](https://nodejs.org/en/download/)) + ```sh-session npm install @oasisjs/biscuit pnpm add @oasisjs/biscuit yarn add @oasisjs/biscuit ``` + > or via [cdn](https://nest.land/package/biscuit) -The biscuit Discord library is built ontop of Discordeno and webspec APIs, we aim to provide portability. -Join our [Discord](https://discord.gg/zmuvzzEFz2) +The biscuit Discord library is built ontop of Discordeno and webspec APIs, we aim to provide portability. Join our +[Discord](https://discord.gg/zmuvzzEFz2) ### Most importantly, biscuit is: -* A modular [Discordeno](https://github.com/discordeno/discordeno) fork -* A framework to build Discord bots -* A bleeding edge API to contact Discord -Biscuit is primarly inspired by Discord.js and Discordeno but it does not include a cache layer by default, we believe that you should not make software that does things it is not supposed to do. +- A modular [Discordeno](https://github.com/discordeno/discordeno) fork +- A framework to build Discord bots +- A bleeding edge API to contact Discord + +Biscuit is primarly inspired by Discord.js and Discordeno but it does not include a cache layer by default, we believe +that you should not make software that does things it is not supposed to do. ### Why biscuit?: -* [Minimal](https://en.wikipedia.org/wiki/Unix_philosophy), non feature-rich! -* Crossplatform -* Consistent -* Performant + +- [Minimal](https://en.wikipedia.org/wiki/Unix_philosophy), non feature-rich! +- Crossplatform +- Consistent +- Performant ### Example bot (TS/JS) + ```js import { GatewayIntents, Session } from "biscuit"; const token = "your token goes here"; -const intents = GatewayIntents.MessageContent | GatewayIntents.Guilds | GatewayIntents.GuildMessages; +const intents = GatewayIntents.MessageContent | GatewayIntents.Guilds | GatewayIntents.GuildMessages; const session = new Session({ token, intents }); session.on("ready", ({ user }) => { @@ -49,23 +55,28 @@ session.start(); ``` ### Mininal style guide -* 4 spaces, no tabs -* Semi-colons are mandatory -* Run `deno fmt` -* Avoid circular dependencies + +- 4 spaces, no tabs +- Semi-colons are mandatory +- Run `deno fmt` +- Avoid circular dependencies ### Contrib guide -* Install Deno extension [here](https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno) -* Run `deno check` to make sure the library works -* Avoid sharing state between classes + +- Install Deno extension [here](https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno) +- Run `deno check` to make sure the library works +- Avoid sharing state between classes ### Compatibility #### bun + since bun is unestable I highly recommend running biscuit on node! -* We got the library running on EndeavourOS but it spams the ready event multiple times -* We got the library running on Arch/Artix Linux but breaks when sending fetch requests -* We got the library running on WSL + +- We got the library running on EndeavourOS but it spams the ready event multiple times +- We got the library running on Arch/Artix Linux but breaks when sending fetch requests +- We got the library running on WSL #### node -* all DNT shims work properly + +- all DNT shims work properly diff --git a/build.ts b/build.ts index e940537..a3f11bf 100644 --- a/build.ts +++ b/build.ts @@ -1,67 +1,67 @@ -import { build } from 'https://deno.land/x/dnt@0.23.0/mod.ts'; +import { build } from "https://deno.land/x/dnt@0.23.0/mod.ts"; -await Deno.remove('npm', { recursive: true }).catch((_) => {}); +await Deno.remove("npm", { recursive: true }).catch((_) => {}); await build({ shims: { custom: [ { package: { - name: 'ws', - version: '^8.4.0', + name: "ws", + version: "^8.4.0", }, globalNames: [ { - name: 'WebSocket', - exportName: 'default', + name: "WebSocket", + exportName: "default", }, ], }, ], }, package: { - author: 'Yuzuru', - name: '@oasisjs/biscuit', + author: "Yuzuru", + name: "@oasisjs/biscuit", version: Deno.args[0], - description: 'the Biscuit library', - license: 'Apache License 2.0', + description: "the Biscuit library", + license: "Apache License 2.0", repository: { - type: 'git', - url: 'git+https://github.com/deno-biscuit/biscuit.git', + type: "git", + url: "git+https://github.com/deno-biscuit/biscuit.git", }, bugs: { - url: 'https://github.com/deno-biscuit/biscuit/issues', + url: "https://github.com/deno-biscuit/biscuit/issues", }, typesVersions: { - '*': { - '*': ['./types/mod.d.ts'], - 'biscuit': ['./types/packages/biscuit/mod.d.ts'], - 'discordeno': ['./types/packages/discordeno/mod.d.ts'], - 'cache': ['./types/packages/cache/mod.d.ts'], + "*": { + "*": ["./types/mod.d.ts"], + "biscuit": ["./types/packages/biscuit/mod.d.ts"], + "discordeno": ["./types/packages/discordeno/mod.d.ts"], + "cache": ["./types/packages/cache/mod.d.ts"], }, }, }, entryPoints: [ - './mod.ts', + "./mod.ts", { - name: './biscuit', - path: 'packages/biscuit/mod.ts', + name: "./biscuit", + path: "packages/biscuit/mod.ts", }, { - name: './discordeno', - path: 'packages/discordeno/mod.ts', + name: "./discordeno", + path: "packages/discordeno/mod.ts", }, { - name: './cache', - path: 'packages/cache/mod.ts', + name: "./cache", + path: "packages/cache/mod.ts", }, ], - outDir: './npm', + outDir: "./npm", declaration: true, typeCheck: false, test: false, }); // post build steps -Deno.copyFileSync('LICENSE', 'npm/LICENSE'); -Deno.copyFileSync('README.md', 'npm/README.md'); +Deno.copyFileSync("LICENSE", "npm/LICENSE"); +Deno.copyFileSync("README.md", "npm/README.md"); diff --git a/packages/biscuit/Actions.ts b/packages/biscuit/Actions.ts index 7c2bf62..0c0112c 100644 --- a/packages/biscuit/Actions.ts +++ b/packages/biscuit/Actions.ts @@ -31,9 +31,9 @@ import type { DiscordScheduledEvent, DiscordScheduledEventUserAdd, DiscordScheduledEventUserRemove, - // DiscordThreadMemberUpdate, - // DiscordThreadMembersUpdate, DiscordThreadListSync, + DiscordThreadMembersUpdate, + DiscordThreadMemberUpdate, DiscordTypingStart, DiscordUser, DiscordWebhookUpdate, @@ -192,6 +192,28 @@ export const THREAD_DELETE: RawHandler = (session, _shardId, cha session.emit("threadDelete", new ThreadChannel(session, channel, channel.guild_id)); }; +export const THREAD_MEMBER_UPDATE: RawHandler = (session, _shardId, payload) => { + session.emit("threadMemberUpdate", { + guildId: payload.guild_id, + id: payload.id, + userId: payload.user_id, + joinedAt: payload.joined_at, + flags: payload.flags, + }); +}; + +export const THREAD_MEMBERS_UPDATE: RawHandler = (session, _shardId, payload) => { + session.emit("threadMembersUpdate", { + memberCount: payload.member_count, + addedMembers: payload.added_members + ? payload.added_members.map((tm) => new ThreadMember(session, tm)) + : undefined, + removedMemberIds: payload.removed_member_ids ? payload.removed_member_ids : undefined, + guildId: payload.guild_id, + id: payload.id, + }); +}; + export const THREAD_LIST_SYNC: RawHandler = (session, _shardId, payload) => { session.emit("threadListSync", { guildId: payload.guild_id, @@ -346,7 +368,7 @@ export const GUILD_SCHEDULED_EVENT_USER_REMOVE: RawHandler = (session, shardId, data) => { - session.emit("raw", data as { t: string, d: unknown }, shardId); + session.emit("raw", data as { t: string; d: unknown }, shardId); }; export interface Ready extends Omit { @@ -386,6 +408,8 @@ export interface Events { "threadUpdate": Handler<[ThreadChannel]>; "threadDelete": Handler<[ThreadChannel]>; "threadListSync": Handler<[{ guildId: Snowflake, channelIds: Snowflake[], threads: ThreadChannel[], members: ThreadMember[] }]> + "threadMemberUpdate": Handler<[{id: Snowflake, userId: Snowflake, guildId: Snowflake, joinedAt: string, flags: number }]> + "threadMembersUpdate": Handler<[{id: Snowflake, memberCount: number, addedMembers?: ThreadMember[], guildId: Snowflake, removedMemberIds?: Snowflake[]}]> "interactionCreate": Handler<[Interaction]>; "integrationCreate": Handler<[Integration]>; "integrationUpdate": Handler<[Integration]>; diff --git a/packages/biscuit/structures/MessageReaction.ts b/packages/biscuit/structures/MessageReaction.ts index 9846f5f..c97de09 100644 --- a/packages/biscuit/structures/MessageReaction.ts +++ b/packages/biscuit/structures/MessageReaction.ts @@ -9,7 +9,7 @@ export interface MessageReactionAdd { messageId: string; guildId?: string; member?: Member; - emoji: Partial + emoji: Partial; } export function NewMessageReactionAdd(session: Session, data: DiscordMessageReactionAdd): MessageReactionAdd { @@ -18,9 +18,11 @@ export function NewMessageReactionAdd(session: Session, data: DiscordMessageReac channelId: data.channel_id, messageId: data.message_id, guildId: data.guild_id, - member: data.member ? new Member(session, (data.member as DiscordMemberWithUser), (data.guild_id || "")) : undefined, + member: data.member + ? new Member(session, data.member as DiscordMemberWithUser, data.guild_id || "") + : undefined, emoji: new Emoji(session, data.emoji), - } + }; } /** diff --git a/packages/biscuit/structures/guilds/Guild.ts b/packages/biscuit/structures/guilds/Guild.ts index 86a60f4..ed5de0b 100644 --- a/packages/biscuit/structures/guilds/Guild.ts +++ b/packages/biscuit/structures/guilds/Guild.ts @@ -210,19 +210,19 @@ export class Guild extends BaseGuild implements Model { this.explicitContentFilterLevel = data.explicit_content_filter; this.members = new Map( - data.members?.map((member) => [data.id, new Member(session, { ...member, user: member.user! }, data.id)]) + data.members?.map((member) => [data.id, new Member(session, { ...member, user: member.user! }, data.id)]), ); this.roles = new Map( - data.roles.map((role) => [data.id, new Role(session, role, data.id)]) + data.roles.map((role) => [data.id, new Role(session, role, data.id)]), ); this.emojis = new Map( - data.emojis.map((guildEmoji) => [guildEmoji.id!, new GuildEmoji(session, guildEmoji, data.id)]) + data.emojis.map((guildEmoji) => [guildEmoji.id!, new GuildEmoji(session, guildEmoji, data.id)]), ); this.channels = new Map( - data.channels?.map((guildChannel) => [guildChannel.id, new GuildChannel(session, guildChannel, data.id)]) + data.channels?.map((guildChannel) => [guildChannel.id, new GuildChannel(session, guildChannel, data.id)]), ); } diff --git a/packages/cache/mod.ts b/packages/cache/mod.ts index 6f88117..ac7deb8 100644 --- a/packages/cache/mod.ts +++ b/packages/cache/mod.ts @@ -1,13 +1,13 @@ import type { - DiscordMessage, DiscordChannel, DiscordEmoji, DiscordGuild, DiscordMemberWithUser, + DiscordMessage, DiscordMessageReactionAdd, DiscordMessageReactionRemove, - DiscordMessageReactionRemoveEmoji, DiscordMessageReactionRemoveAll, + DiscordMessageReactionRemoveEmoji, DiscordUser, Session, Snowflake, @@ -15,22 +15,22 @@ import type { } from "./deps.ts"; import { - ChannelTypes, ChannelFactory, + ChannelTypes, DMChannel, Emoji, Guild, + GuildChannel, GuildEmoji, GuildTextChannel, - GuildChannel, Member, Message, MessageReaction, NewsChannel, + textBasedChannels, ThreadChannel, User, VoiceChannel, - textBasedChannels, } from "./deps.ts"; export const cache_sym = Symbol("@cache"); @@ -99,32 +99,32 @@ export default function (session: Session): SessionCache { switch (data.t) { case "MESSAGE_CREATE": messageBootstrapper(cache, raw, false); - break; + break; case "MESSAGE_UPDATE": messageBootstrapper(cache, raw, !raw.edited_timestamp); - break; + break; case "CHANNEL_UPDATE": case "CHANNEL_CREATE": channelBootstrapper(cache, raw); - break; + break; case "GUILD_MEMBER_ADD": memberBootstrapper(cache, raw, raw.guild_id); - break; + break; case "GUILD_CREATE": guildBootstrapper(cache, raw); - break; + break; case "GUILD_DELETE": cache.guilds.delete(raw.id); - break; + break; case "MESSAGE_REACTION_ADD": reactionBootstrapper(cache, raw, false); - break; + break; case "MESSAGE_REACTION_REMOVE": reactionBootstrapper(cache, raw, false); - break; + break; case "MESSAGE_REACTION_REMOVE_ALL": reactionBootstrapperDeletions(cache, raw); - break; + break; } }); @@ -323,8 +323,7 @@ export function reactionBootstrapperDeletions(cache: SessionCache, payload: Disc }); }); }); - } - else { + } else { cache.dms.retrieve(payload.channel_id, (channel) => { channel.messages.retrieve(payload.message_id, (message) => { message.reactions = []; @@ -414,29 +413,34 @@ export function channelBootstrapper(cache: SessionCache, channel: DiscordChannel cache.guilds.retrieve(channel.guild_id, (guild) => { if (textBasedChannels.includes(channel.type)) { // deno-lint-ignore no-explicit-any - guild.channels.set(channel.id, Object.assign( - ChannelFactory.fromGuildChannel(cache.session, channel), - { messages: new StructCache(cache.session) } - )); - } - else { + guild.channels.set( + channel.id, + Object.assign( + ChannelFactory.fromGuildChannel(cache.session, channel), + { messages: new StructCache(cache.session) }, + ), + ); + } else { // deno-lint-ignore no-explicit-any - guild.channels.set(channel.id, ChannelFactory.fromGuildChannel(cache.session, channel)); + guild.channels.set(channel.id, ChannelFactory.fromGuildChannel(cache.session, channel)); } }); } export function memberBootstrapper(cache: SessionCache, member: DiscordMemberWithUser, guildId: Snowflake) { cache.guilds.retrieve(guildId, (guild) => { - guild.members.set(member.user.id, Object.assign( - new Member(cache.session, member, guildId), - { - userId: member.user.id, - get user(): User | undefined { - return cache.users.get(this.userId); - } - } - )); + guild.members.set( + member.user.id, + Object.assign( + new Member(cache.session, member, guildId), + { + userId: member.user.id, + get user(): User | undefined { + return cache.users.get(this.userId); + }, + }, + ), + ); }); } @@ -450,51 +454,67 @@ export function messageBootstrapper(cache: SessionCache, message: DiscordMessage if (cache.dms.has(message.channel_id)) { // is dm cache.dms.retrieve(message.channel_id, (dm) => { - dm.messages[partial ? "updateFields" : "set"](message.id, Object.assign( - new Message(cache.session, message), - { - authorId: message.author.id, - get author(): User | undefined { - return cache.users.get(this.authorId); + dm.messages[partial ? "updateFields" : "set"]( + message.id, + Object.assign( + new Message(cache.session, message), + { + authorId: message.author.id, + get author(): User | undefined { + return cache.users.get(this.authorId); + }, }, - } - )); + ), + ); }); } else { // is not dm - cache.guilds.retrieve(message.guild_id!, (guild) => guild.channels.retrieve(message.channel_id, (dm) => { - dm.messages[partial ? "updateFields" : "set"](message.id, Object.assign( - new Message(cache.session, message), - { - authorId: message.author.id, - get author(): User | undefined { - return cache.users.get(this.authorId); - }, - } - )); - })); + cache.guilds.retrieve(message.guild_id!, (guild) => + guild.channels.retrieve(message.channel_id, (dm) => { + dm.messages[partial ? "updateFields" : "set"]( + message.id, + Object.assign( + new Message(cache.session, message), + { + authorId: message.author.id, + get author(): User | undefined { + return cache.users.get(this.authorId); + }, + }, + ), + ); + })); } } export function guildBootstrapper(cache: SessionCache, guild: DiscordGuild) { - const members = new StructCache(cache.session, guild.members?.map((data) => { - const obj: CachedMember = Object.assign(new Member(cache.session, data as DiscordMemberWithUser, guild.id), { - userId: data.user!.id, - get user(): User | undefined { - return cache.users.get(this.userId); - }, - }); + const members = new StructCache( + cache.session, + guild.members?.map((data) => { + const obj: CachedMember = Object.assign( + new Member(cache.session, data as DiscordMemberWithUser, guild.id), + { + userId: data.user!.id, + get user(): User | undefined { + return cache.users.get(this.userId); + }, + }, + ); - return [data.user!.id, obj as CachedMember]; - })); + return [data.user!.id, obj as CachedMember]; + }), + ); - const channels = new StructCache(cache.session, guild.channels?.map((data) => { - const obj = Object.assign(ChannelFactory.from(cache.session, data), { - messages: new Map(), - }); + const channels = new StructCache( + cache.session, + guild.channels?.map((data) => { + const obj = Object.assign(ChannelFactory.from(cache.session, data), { + messages: new Map(), + }); - return [data.id, obj as CachedGuildChannel]; - })); + return [data.id, obj as CachedGuildChannel]; + }), + ); cache.guilds.set( guild.id, diff --git a/packages/discordeno/gateway/shard/connect.ts b/packages/discordeno/gateway/shard/connect.ts index adf0fbe..3386b2c 100644 --- a/packages/discordeno/gateway/shard/connect.ts +++ b/packages/discordeno/gateway/shard/connect.ts @@ -23,11 +23,11 @@ export async function connect(shard: Shard): Promise { socket.onclose = (event) => shard.handleClose(event); socket.onmessage = (message) => { - // START WSL GATEWAY PATCH + // START WSL GATEWAY PATCH gotHello = true; - // END WSL GATEWAY PATCH + // END WSL GATEWAY PATCH shard.handleMessage(message); - } + }; return new Promise((resolve) => { socket.onopen = () => { diff --git a/packages/discordeno/rest/sendRequest.ts b/packages/discordeno/rest/sendRequest.ts index 81c72bf..5481827 100644 --- a/packages/discordeno/rest/sendRequest.ts +++ b/packages/discordeno/rest/sendRequest.ts @@ -17,7 +17,7 @@ export interface RestSendRequestOptions { } export async function sendRequest(rest: RestManager, options: RestSendRequestOptions): Promise { - try { + try { // CUSTOM HANDLER FOR USER TO LOG OR WHATEVER WHENEVER A FETCH IS MADE rest.debug(`[REST - fetching] URL: ${options.url} | ${JSON.stringify(options)}`); diff --git a/packages/discordeno/types/discord.ts b/packages/discordeno/types/discord.ts index 496e294..f75b9fa 100644 --- a/packages/discordeno/types/discord.ts +++ b/packages/discordeno/types/discord.ts @@ -2268,6 +2268,8 @@ export interface DiscordThreadMembersUpdate { export interface DiscordThreadMemberUpdate { /** The id of the thread */ id: string; + /** The id of the user */ + user_id: string; /** The id of the guild */ guild_id: string; /** The timestamp when the bot joined this thread. */ diff --git a/tests/bun.js b/tests/bun.js index 6f9f186..ab4891b 100644 --- a/tests/bun.js +++ b/tests/bun.js @@ -1,6 +1,6 @@ import { GatewayIntents, Session } from "./deps.ts"; -const token = process.env.TOKEN; +const token = process.env.TOKEN; const intents = GatewayIntents.MessageContent | GatewayIntents.Guilds | GatewayIntents.GuildMessages; const session = new Session({ token, intents });