diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts index b2778bb8..7cdc2dd9 100644 --- a/src/gateway/opcodes/Identify.ts +++ b/src/gateway/opcodes/Identify.ts @@ -16,15 +16,7 @@ along with this program. If not, see . */ -import { - CLOSECODES, - Capabilities, - OPCODES, - Payload, - Send, - WebSocket, - setupListener, -} from "@spacebar/gateway"; +import { CLOSECODES, Capabilities, OPCODES, Payload, Send, WebSocket, setupListener } from "@spacebar/gateway"; import { Application, Config, @@ -104,8 +96,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { const userQueryTime = taskSw.getElapsedAndReset(); // Check intents - if (!identify.intents) - identify.intents = 0b11011111111111111111111111111111111n; // TODO: what is this number? + if (!identify.intents) identify.intents = 0b11011111111111111111111111111111111n; // TODO: what is this number? this.intents = new Intents(identify.intents); // TODO: actually do intent things. @@ -115,17 +106,9 @@ export async function onIdentify(this: WebSocket, data: Payload) { this.shard_id = identify.shard[0]; this.shard_count = identify.shard[1]; - if ( - this.shard_count == null || - this.shard_id == null || - this.shard_id > this.shard_count || - this.shard_id < 0 || - this.shard_count <= 0 - ) { + if (this.shard_count == null || this.shard_id == null || this.shard_id > this.shard_count || this.shard_id < 0 || this.shard_count <= 0) { // TODO: why do we even care about this right now? - console.log( - `[Gateway] Invalid sharding from ${user.id}: ${identify.shard}`, - ); + console.log(`[Gateway] Invalid sharding from ${user.id}: ${identify.shard}`); return this.close(CLOSECODES.Invalid_shard); } } @@ -170,13 +153,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { timePromise(() => ReadState.find({ where: { user_id: this.user_id }, - select: [ - "id", - "channel_id", - "last_message_id", - "last_pin_timestamp", - "mention_count", - ], + select: ["id", "channel_id", "last_message_id", "last_pin_timestamp", "mention_count"], }), ), @@ -185,29 +162,28 @@ export async function onIdentify(this: WebSocket, data: Payload) { where: { id: this.user_id }, select: { // We only want some member props - ...Object.fromEntries( - MemberPrivateProjection.map((x) => [x, true]), - ), + ...Object.fromEntries(MemberPrivateProjection.map((x) => [x, true])), settings: true, // guild settings roles: { id: true }, // the full role is fetched from the `guild` relation + guild: { id: true }, // TODO: we don't really need every property of // guild channels, emoji, roles, stickers // but we do want almost everything from guild. // How do you do that without just enumerating the guild props? - guild: Object.fromEntries( - getDatabase()! - .getMetadata(Guild) - .columns.map((x) => [x.propertyName, true]), - ), + // guild: Object.fromEntries( + // getDatabase()! + // .getMetadata(Guild) + // .columns.map((x) => [x.propertyName, true]), + // ), }, relations: [ - "guild", - "guild.channels", - "guild.emojis", - "guild.roles", - "guild.stickers", - "guild.voice_states", + // "guild", + // "guild.channels", + // "guild.emojis", + // "guild.roles", + // "guild.stickers", + // "guild.voice_states", "roles", // For these entities, `user` is always just the logged in user we fetched above @@ -219,11 +195,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { timePromise(() => Recipient.find({ where: { user_id: this.user_id, closed: false }, - relations: [ - "channel", - "channel.recipients", - "channel.recipients.user", - ], + relations: ["channel", "channel.recipients", "channel.recipients.user"], select: { channel: { id: true, @@ -241,9 +213,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { // at least one column. id: true, // We only want public user data for each dm channel - user: Object.fromEntries( - PublicUserProjection.map((x) => [x, true]), - ), + user: Object.fromEntries(PublicUserProjection.map((x) => [x, true])), }, }, }, @@ -251,6 +221,27 @@ export async function onIdentify(this: WebSocket, data: Payload) { ), ]); + const { result: memberGuilds, elapsed: queryGuildsTime } = await timePromise(() => + Promise.all( + members.map((m) => + Guild.findOneOrFail({ + where: { id: m.guild_id }, + select: Object.fromEntries( + getDatabase()! + .getMetadata(Guild) + .columns.map((x) => [x.propertyName, true]), + ), + relations: ["channels", "emojis", "roles", "stickers", "voice_states"], + }), + ), + ), + ); + + members.forEach((m) => { + const g = memberGuilds.find((mg) => mg.id === m.guild_id); + if (g) m.guild = g; + }); + const totalQueryTime = taskSw.getElapsedAndReset(); // We forgot to migrate user settings from the JSON column of `users` @@ -311,9 +302,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { }) */ .map((channel) => { - channel.position = member.guild.channel_ordering.indexOf( - channel.id, - ); + channel.position = member.guild.channel_ordering.indexOf(channel.id); return channel; }) .sort((a, b) => a.position - b.position); @@ -333,21 +322,18 @@ export async function onIdentify(this: WebSocket, data: Payload) { const generateGuildsListTime = taskSw.getElapsedAndReset(); // Generate user_guild_settings - const user_guild_settings_entries: ReadyUserGuildSettingsEntries[] = - members.map((x) => ({ - ...DefaultUserGuildSettings, - ...x.settings, - guild_id: x.guild_id, - channel_overrides: Object.entries( - x.settings.channel_overrides ?? {}, - ).map((y) => ({ - ...y[1], - channel_id: y[0], - })), - })); + const user_guild_settings_entries: ReadyUserGuildSettingsEntries[] = members.map((x) => ({ + ...DefaultUserGuildSettings, + ...x.settings, + guild_id: x.guild_id, + channel_overrides: Object.entries(x.settings.channel_overrides ?? {}).map((y) => ({ + ...y[1], + channel_id: y[0], + })), + })); const generateUserGuildSettingsTime = taskSw.getElapsedAndReset(); - // Popultaed with users from private channels, relationships. + // Populated with users from private channels, relationships. // Uses a set to dedupe for us. const users: Set = new Set(); @@ -360,16 +346,11 @@ export async function onIdentify(this: WebSocket, data: Payload) { const channel = r.channel as DMChannel; // Remove ourself from the list of other users in dm channel - channel.recipients = channel.recipients.filter( - (recipient) => recipient.user.id !== this.user_id, - ); + channel.recipients = channel.recipients.filter((recipient) => recipient.user.id !== this.user_id); - const channelUsers = channel.recipients?.map((recipient) => - recipient.user.toPublicUser(), - ); + const channelUsers = channel.recipients?.map((recipient) => recipient.user.toPublicUser()); - if (channelUsers && channelUsers.length > 0) - channelUsers.forEach((user) => users.add(user)); + if (channelUsers && channelUsers.length > 0) channelUsers.forEach((user) => users.add(user)); return { id: channel.id, @@ -403,10 +384,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { })); const findAndGenerateSessionReplaceTime = taskSw.getElapsedAndReset(); - const [ - { elapsed: emitSessionsReplaceTime }, - { elapsed: emitPresenceUpdateTime }, - ] = await Promise.all([ + const [{ elapsed: emitSessionsReplaceTime }, { elapsed: emitPresenceUpdateTime }] = await Promise.all([ timePromise(() => emitEvent({ event: "SESSIONS_REPLACE", @@ -437,14 +415,10 @@ export async function onIdentify(this: WebSocket, data: Payload) { const d: ReadyEventData = { v: 9, - application: application - ? { id: application.id, flags: application.flags } - : undefined, + application: application ? { id: application.id, flags: application.flags } : undefined, user: user.toPrivateUser(), user_settings: user.settings, - guilds: this.capabilities.has(Capabilities.FLAGS.CLIENT_STATE_V2) - ? guilds.map((x) => new ReadyGuildDTO(x).toJSON()) - : guilds, + guilds: this.capabilities.has(Capabilities.FLAGS.CLIENT_STATE_V2) ? guilds.map((x) => new ReadyGuildDTO(x).toJSON()) : guilds, relationships: user.relationships.map((x) => x.toPublicRelationship()), read_state: { entries: read_states, @@ -463,16 +437,10 @@ export async function onIdentify(this: WebSocket, data: Payload) { merged_members: merged_members, sessions: allSessions, - resume_gateway_url: - Config.get().gateway.endpointClient || - Config.get().gateway.endpointPublic || - "ws://127.0.0.1:3001", + resume_gateway_url: Config.get().gateway.endpointClient || Config.get().gateway.endpointPublic || "ws://127.0.0.1:3001", // lol hack whatever - required_action: - Config.get().login.requireVerification && !user.verified - ? "REQUIRE_VERIFIED_EMAIL" - : undefined, + required_action: Config.get().login.requireVerification && !user.verified ? "REQUIRE_VERIFIED_EMAIL" : undefined, consents: { personalization: { @@ -534,6 +502,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { read_statesQueryTime, membersQueryTime, recipientsQueryTime, + queryGuildsTime, })) { if (subvalue) { val.calls.push(subkey, { @@ -564,9 +533,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { await Promise.all( pending_guilds.map((x) => { //Even with the GUILD_MEMBERS intent, the bot always receives just itself as the guild members - const botMemberObject = members.find( - (member) => member.guild_id === x.id, - ); + const botMemberObject = members.find((member) => member.guild_id === x.id); return Send(this, { op: OPCODES.Dispatch, @@ -583,21 +550,15 @@ export async function onIdentify(this: WebSocket, data: Payload) { ] : [], }, - })?.catch((e) => - console.error(`[Gateway] error when sending bot guilds`, e), - ); + })?.catch((e) => console.error(`[Gateway] error when sending bot guilds`, e)); }), ); const pendingGuildsTime = Date.now(); - const readySupplementalGuilds = ( - guilds.filter((guild) => !guild.unavailable) as Guild[] - ).map((guild) => { + const readySupplementalGuilds = (guilds.filter((guild) => !guild.unavailable) as Guild[]).map((guild) => { return { - voice_states: guild.voice_states.map((state) => - state.toPublicVoiceState(), - ), + voice_states: guild.voice_states.map((state) => state.toPublicVoiceState()), id: guild.id, embedded_activities: [], }; @@ -631,8 +592,5 @@ export async function onIdentify(this: WebSocket, data: Payload) { const setupListenerTime = Date.now(); - console.log( - `[Gateway] IDENTIFY ${this.user_id} in ${totalSw.elapsed().totalMilliseconds}ms`, - JSON.stringify(d._trace, null, 2), - ); + console.log(`[Gateway] IDENTIFY ${this.user_id} in ${totalSw.elapsed().totalMilliseconds}ms`, JSON.stringify(d._trace, null, 2)); }