From f228561f4c2059d3568d4cf7dd8fc98dd0260c2a Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Mon, 13 Mar 2023 19:02:52 +1100 Subject: [PATCH 01/17] Initial identify rewrite --- src/gateway/opcodes/Identify.ts | 500 +++++++++++++++++++------------- src/util/dtos/ReadyGuildDTO.ts | 56 +++- src/util/entities/Channel.ts | 7 + src/util/entities/User.ts | 9 + src/util/interfaces/Event.ts | 31 +- 5 files changed, 387 insertions(+), 216 deletions(-) diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts index 1a632b84..51a6e2e4 100644 --- a/src/gateway/opcodes/Identify.ts +++ b/src/gateway/opcodes/Identify.ts @@ -16,7 +16,7 @@ along with this program. If not, see . */ -import { WebSocket, Payload } from "@fosscord/gateway"; +import { WebSocket, Payload, setupListener } from "@fosscord/gateway"; import { checkToken, Intents, @@ -26,7 +26,6 @@ import { Session, EVENTEnum, Config, - PublicMember, PublicUser, PrivateUserProjection, ReadState, @@ -36,19 +35,19 @@ import { PrivateSessionProjection, MemberPrivateProjection, PresenceUpdateEvent, - UserSettings, IdentifySchema, DefaultUserGuildSettings, - UserGuildSettings, ReadyGuildDTO, Guild, - UserTokenData, + PublicUserProjection, + ReadyUserGuildSettingsEntries, + UserSettings, + Permissions, + DMChannel, + GuildOrUnavailable, } from "@fosscord/util"; import { Send } from "../util/Send"; import { CLOSECODES, OPCODES } from "../util/Constants"; -import { setupListener } from "../listener/listener"; -// import experiments from "./experiments.json"; -const experiments: unknown[] = []; import { check } from "./instanceOf"; import { Recipient } from "@fosscord/util"; @@ -56,49 +55,132 @@ import { Recipient } from "@fosscord/util"; // TODO: check privileged intents, if defined in the config // TODO: check if already identified -// TODO: Refactor identify ( and lazyrequest, tbh ) +const getUserFromToken = async (token: string): Promise => { + try { + const { jwtSecret } = Config.get().security; + const { decoded } = await checkToken(token, jwtSecret); + return decoded.id; + } catch (e) { + console.error(`[Gateway] Invalid token`, e); + return null; + } +}; export async function onIdentify(this: WebSocket, data: Payload) { clearTimeout(this.readyTimeout); - // TODO: is this needed now that we use `json-bigint`? - if (typeof data.d?.client_state?.highest_last_message_id === "number") - data.d.client_state.highest_last_message_id += ""; - check.call(this, IdentifySchema, data.d); + // Check payload matches schema + check.call(this, IdentifySchema, data.d); const identify: IdentifySchema = data.d; - let decoded: UserTokenData["decoded"]; - try { - const { jwtSecret } = Config.get().security; - decoded = (await checkToken(identify.token, jwtSecret)).decoded; // will throw an error if invalid - } catch (error) { - console.error("invalid token", error); - return this.close(CLOSECODES.Authentication_failed); - } - this.user_id = decoded.id; - const session_id = this.session_id; + // Check auth + // TODO: the checkToken call will fetch user, and then we have to refetch with different select + // checkToken should be able to select what we want + const user_id = await getUserFromToken(identify.token); + if (!user_id) return this.close(CLOSECODES.Authentication_failed); + this.user_id = user_id; - const [user, read_states, members, recipients, session, application] = + // Check intents + if (!identify.intents) identify.intents = 30064771071n; // TODO: what is this number? + this.intents = new Intents(identify.intents); + + // TODO: actually do intent things. + + // Validate sharding + if (identify.shard) { + 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 + ) { + // TODO: why do we even care about this? + console.log( + `[Gateway] Invalid sharding from ${user_id}: ${identify.shard}`, + ); + return this.close(CLOSECODES.Invalid_shard); + } + } + + // Generate a new gateway session ( id is already made, just save it in db ) + const session = Session.create({ + user_id: this.user_id, + session_id: this.session_id, + status: identify.presence?.status || "online", + client_info: { + client: identify.properties?.$device, + os: identify.properties?.os, + version: 0, + }, + activities: identify.presence?.activities, // TODO: validation + }); + + // Get from database: + // * the current user, + // * the users read states + // * guild members for this user + // * recipients ( dm channels ) + // * the bot application, if it exists + const [, user, application, read_states, members, recipients] = await Promise.all([ + session.save(), + + // TODO: Refactor checkToken to allow us to skip this additional query User.findOneOrFail({ where: { id: this.user_id }, relations: ["relationships", "relationships.to", "settings"], select: [...PrivateUserProjection, "relationships"], }), - ReadState.find({ where: { user_id: this.user_id } }), + + Application.findOne({ + where: { id: this.user_id }, + select: ["id", "flags"], + }), + + ReadState.find({ + where: { user_id: this.user_id }, + select: [ + "id", + "channel_id", + "last_message_id", + "last_pin_timestamp", + "mention_count", + ], + }), + Member.find({ where: { id: this.user_id }, - select: MemberPrivateProjection, + select: { + // We only want some member props + ...Object.fromEntries( + MemberPrivateProjection.map((x) => [x, true]), + ), + settings: true, // guild settings + roles: { id: true }, // the full role is fetched from the `guild` relation + + // 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: true, + }, relations: [ "guild", "guild.channels", "guild.emojis", "guild.roles", "guild.stickers", - "user", "roles", + + // For these entities, `user` is always just the logged in user we fetched above + // "user", ], }), + Recipient.find({ where: { user_id: this.user_id, closed: false }, relations: [ @@ -106,220 +188,240 @@ export async function onIdentify(this: WebSocket, data: Payload) { "channel.recipients", "channel.recipients.user", ], - // TODO: public user selection - }), - // save the session and delete it when the websocket is closed - Session.create({ - user_id: this.user_id, - session_id: session_id, - // TODO: check if status is only one of: online, dnd, offline, idle - status: identify.presence?.status || "offline", //does the session always start as online? - client_info: { - //TODO read from identity - client: "desktop", - os: identify.properties?.os, - version: 0, + select: { + channel: { + id: true, + flags: true, + // is_spam: true, // TODO + last_message_id: true, + last_pin_timestamp: true, + type: true, + icon: true, + name: true, + owner_id: true, + recipients: { + // we don't actually need this ID or any other information about the recipient info, + // but typeorm does not select anything from the users relation of recipients unless we select + // at least one column. + id: true, + // We only want public user data for each dm channel + user: Object.fromEntries( + PublicUserProjection.map((x) => [x, true]), + ), + }, + }, }, - activities: [], - }).save(), - Application.findOne({ where: { id: this.user_id } }), + }), ]); - if (!user) return this.close(CLOSECODES.Authentication_failed); + // We forgot to migrate user settings from the JSON column of `users` + // to the `user_settings` table theyre in now, + // so for instances that migrated, users may not have a `user_settings` row. if (!user.settings) { user.settings = new UserSettings(); await user.settings.save(); } - if (!identify.intents) identify.intents = BigInt("0x6ffffffff"); - this.intents = new Intents(identify.intents); - if (identify.shard) { - 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 - ) { - console.log(identify.shard); - return this.close(CLOSECODES.Invalid_shard); - } - } - let users: PublicUser[] = []; - - const merged_members = members.map((x: Member) => { + // Generate merged_members + const merged_members = members.map((x) => { return [ { ...x, roles: x.roles.map((x) => x.id), + + // add back user, which we don't fetch from db + // TODO: For guild profiles, this may need to be changed. + // TODO: The only field required in the user prop is `id`, + // but our types are annoying so I didn't bother. + user: user.toPublicUser(), + + guild: { + id: x.guild.id, + }, settings: undefined, - guild: undefined, }, ]; - }) as PublicMember[][]; - // TODO: This type is bad. - let guilds: Partial[] = members.map((x) => ({ - ...x.guild, - joined_at: x.joined_at, - })); - - const pending_guilds: typeof guilds = []; - if (user.bot) - guilds = guilds.map((guild) => { - pending_guilds.push(guild); - return { id: guild.id, unavailable: true }; - }); - - // TODO: Rewrite this. Perhaps a DTO? - const user_guild_settings_entries = 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], - })), - })) as unknown as UserGuildSettings[]; - - const channels = recipients.map((x) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - x.channel.recipients = x.channel.recipients.map((x) => - x.user.toPublicUser(), - ); - //TODO is this needed? check if users in group dm that are not friends are sent in the READY event - users = users.concat(x.channel.recipients as unknown as User[]); - if (x.channel.isDm()) { - x.channel.recipients = x.channel.recipients?.filter( - (x) => x.id !== this.user_id, - ); - } - return x.channel; }); - for (const relation of user.relationships) { - const related_user = relation.to; - const public_related_user = { - username: related_user.username, - discriminator: related_user.discriminator, - id: related_user.id, - public_flags: related_user.public_flags, - avatar: related_user.avatar, - bot: related_user.bot, - bio: related_user.bio, - premium_since: user.premium_since, - premium_type: user.premium_type, - accent_color: related_user.accent_color, - }; - users.push(public_related_user); - } + // Populated with guilds 'unavailable' currently + // Just for bots + const pending_guilds: Guild[] = []; - setImmediate(async () => { - // run in seperate "promise context" because ready payload is not dependent on those events + // Generate guilds list ( make them unavailable if user is bot ) + const guilds: GuildOrUnavailable[] = members.map((member) => { + // Some Discord libraries do `'blah' in object` instead of + // checking if the type is correct + member.guild.roles.forEach((role) => { + for (const key in role) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + if (!role[key]) role[key] = undefined; + } + }); + + // filter guild channels we don't have permission to view + // TODO: check if this causes issues when the user is granted other roles? + member.guild.channels = member.guild.channels.filter((channel) => { + const perms = Permissions.finalPermission({ + user: { id: member.id, roles: member.roles.map((x) => x.id) }, + guild: member.guild, + channel, + }); + + return perms.has("VIEW_CHANNEL"); + }); + + if (user.bot) { + pending_guilds.push(member.guild); + return { id: member.guild.id, unavailable: true }; + } + + return { + ...member.guild.toJSON(), + joined_at: member.joined_at, + }; + }); + + // 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], + })), + })); + + // Popultaed with users from private channels, relationships. + // Uses a set to dedupe for us. + const users: Set = new Set(); + + // Generate dm channels from recipients list. Append recipients to `users` list + const channels = recipients + .filter(({ channel }) => channel.isDm()) + .map((r) => { + // TODO: fix the types of Recipient + // Their channels are only ever private (I think) and thus are always DM channels + 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, + ); + + const channelUsers = channel.recipients?.map((recipient) => + recipient.user.toPublicUser(), + ); + + if (channelUsers && channelUsers.length > 0) + channelUsers.forEach((user) => users.add(user)); + + return { + id: channel.id, + flags: channel.flags, + last_message_id: channel.last_message_id, + type: channel.type, + recipients: channelUsers || [], + is_spam: false, // TODO + }; + }); + + // From user relationships ( friends ), also append to `users` list + user.relationships.forEach((x) => users.add(x.to.toPublicUser())); + + // Send SESSIONS_REPLACE and PRESENCE_UPDATE + const allSessions = ( + await Session.find({ + where: { user_id: this.user_id }, + select: PrivateSessionProjection, + }) + ).map((x) => ({ + // TODO how is active determined? + // in our lazy request impl, we just pick the 'most relevant' session + active: x.session_id == session.session_id, + activities: x.activities, + client_info: x.client_info, + // TODO: what does all mean? + session_id: x.session_id == session.session_id ? "all" : x.session_id, + status: x.status, + })); + + Promise.all([ emitEvent({ event: "SESSIONS_REPLACE", user_id: this.user_id, - data: await Session.find({ - where: { user_id: this.user_id }, - select: PrivateSessionProjection, - }), - } as SessionsReplace); + data: allSessions, + } as SessionsReplace), emitEvent({ event: "PRESENCE_UPDATE", user_id: this.user_id, data: { - user: await User.getPublicUser(this.user_id), + user: user.toPublicUser(), activities: session.activities, - client_status: session?.client_info, + client_status: session.client_info, status: session.status, }, - } as PresenceUpdateEvent); - }); + } as PresenceUpdateEvent), + ]); - read_states.forEach((s: Partial) => { - s.id = s.channel_id; - delete s.user_id; - delete s.channel_id; - }); + // Build READY - const privateUser = { - avatar: user.avatar, - mobile: user.mobile, - desktop: user.desktop, - discriminator: user.discriminator, - email: user.email, - flags: user.flags, - id: user.id, - mfa_enabled: user.mfa_enabled, - nsfw_allowed: user.nsfw_allowed, - phone: user.phone, - premium: user.premium, - premium_type: user.premium_type, - public_flags: user.public_flags, - premium_usage_flags: user.premium_usage_flags, - purchased_flags: user.purchased_flags, - username: user.username, - verified: user.verified, - bot: user.bot, - accent_color: user.accent_color, - banner: user.banner, - bio: user.bio, - premium_since: user.premium_since, - }; + read_states.forEach((x) => { + x.id = x.channel_id; + }); const d: ReadyEventData = { v: 9, - application: { - id: application?.id ?? "", - flags: application?.flags ?? 0, - }, //TODO: check this code! - user: privateUser, + application: application + ? { id: application.id, flags: application.flags } + : undefined, + user: user.toPrivateUser(), user_settings: user.settings, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - guilds: guilds.map((x: Guild & { joined_at: Date }) => { - return { - ...new ReadyGuildDTO(x).toJSON(), - guild_hashes: {}, - joined_at: x.joined_at, - }; - }), - guild_experiments: [], // TODO - geo_ordered_rtc_regions: [], // TODO + guilds: guilds.map((x) => new ReadyGuildDTO(x).toJSON()), relationships: user.relationships.map((x) => x.toPublicRelationship()), read_state: { entries: read_states, partial: false, + // TODO: what is this magic number? + // Isn't `version` referring to the number of changes since this obj was created? + // Why do we send this specific version? version: 304128, }, user_guild_settings: { entries: user_guild_settings_entries, - partial: false, // TODO partial - version: 642, + partial: false, + version: 642, // TODO: see above }, private_channels: channels, - session_id: session_id, - analytics_token: "", // TODO - connected_accounts: [], // TODO + session_id: this.session_id, + country_code: user.settings.locale, // TODO: do ip analysis instead + users: Array.from(users), + merged_members: merged_members, + sessions: allSessions, + consents: { personalization: { consented: false, // TODO }, }, - country_code: user.settings.locale, - friend_suggestion_count: 0, // TODO - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - experiments: experiments, // TODO - guild_join_requests: [], // TODO what is this? - users: users.filter((x) => x).unique(), - merged_members: merged_members, - // shard // TODO: only for user sharding - sessions: [], // TODO: + experiments: [], + guild_join_requests: [], + connected_accounts: [], + guild_experiments: [], + geo_ordered_rtc_regions: [], + api_code_version: 1, + friend_suggestion_count: 0, + analytics_token: "", + tutorial: null, + resume_gateway_url: + Config.get().gateway.endpointClient || + Config.get().gateway.endpointPublic || + "ws://127.0.0.1:3001", + session_type: "normal", // TODO // lol hack whatever required_action: @@ -328,7 +430,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { : undefined, }; - // TODO: send real proper data structure + // Send READY await Send(this, { op: OPCODES.Dispatch, t: EVENTEnum.Ready, @@ -336,23 +438,23 @@ export async function onIdentify(this: WebSocket, data: Payload) { d, }); + // If we're a bot user, send GUILD_CREATE for each unavailable guild await Promise.all( - pending_guilds.map((guild) => + pending_guilds.map((x) => Send(this, { op: OPCODES.Dispatch, t: EVENTEnum.GuildCreate, s: this.sequence++, - d: guild, - })?.catch(console.error), + d: x, + })?.catch((e) => + console.error(`[Gateway] error when sending bot guilds`, e), + ), ), ); //TODO send READY_SUPPLEMENTAL //TODO send GUILD_MEMBER_LIST_UPDATE - //TODO send SESSIONS_REPLACE //TODO send VOICE_STATE_UPDATE to let the client know if another device is already connected to a voice channel await setupListener.call(this); - - // console.log(`${this.ipAddress} identified as ${d.user.id}`); } diff --git a/src/util/dtos/ReadyGuildDTO.ts b/src/util/dtos/ReadyGuildDTO.ts index 97e6931f..e91248d2 100644 --- a/src/util/dtos/ReadyGuildDTO.ts +++ b/src/util/dtos/ReadyGuildDTO.ts @@ -16,7 +16,46 @@ along with this program. If not, see . */ -import { Channel, Emoji, Guild, Member, Role, Sticker } from "../entities"; +import { + Channel, + ChannelOverride, + ChannelType, + Emoji, + Guild, + Member, + PublicUser, + Role, + Sticker, + UserGuildSettings, +} from "../entities"; + +// TODO: this is not the best place for this type +export type ReadyUserGuildSettingsEntries = Omit< + UserGuildSettings, + "channel_overrides" +> & { + channel_overrides: (ChannelOverride & { channel_id: string })[]; +}; + +// TODO: probably should move somewhere else +export interface ReadyPrivateChannel { + id: string; + flags: number; + is_spam: boolean; + last_message_id?: string; + recipients: PublicUser[]; + type: ChannelType.DM | ChannelType.GROUP_DM; +} + +export type GuildOrUnavailable = + | { id: string; unavailable: boolean } + | (Guild & { joined_at?: Date; unavailable: boolean }); + +const guildIsAvailable = ( + guild: GuildOrUnavailable, +): guild is Guild & { joined_at: Date; unavailable: false } => { + return guild.unavailable == false; +}; export interface IReadyGuildDTO { application_command_counts?: { 1: number; 2: number; 3: number }; // ???????????? @@ -64,6 +103,8 @@ export interface IReadyGuildDTO { stickers: Sticker[]; threads: unknown[]; version: string; + guild_hashes: unknown; + unavailable: boolean; } export class ReadyGuildDTO implements IReadyGuildDTO { @@ -112,8 +153,17 @@ export class ReadyGuildDTO implements IReadyGuildDTO { stickers: Sticker[]; threads: unknown[]; version: string; + guild_hashes: unknown; + unavailable: boolean; + joined_at: Date; + + constructor(guild: GuildOrUnavailable) { + if (!guildIsAvailable(guild)) { + this.id = guild.id; + this.unavailable = true; + return; + } - constructor(guild: Guild) { this.application_command_counts = { 1: 5, 2: 2, @@ -163,6 +213,8 @@ export class ReadyGuildDTO implements IReadyGuildDTO { this.stickers = guild.stickers; this.threads = []; this.version = "1"; // ?????? + this.guild_hashes = {}; + this.joined_at = guild.joined_at; } toJSON() { diff --git a/src/util/entities/Channel.ts b/src/util/entities/Channel.ts index 1f128713..7c0828eb 100644 --- a/src/util/entities/Channel.ts +++ b/src/util/entities/Channel.ts @@ -482,3 +482,10 @@ export enum ChannelPermissionOverwriteType { member = 1, group = 2, } + +export interface DMChannel extends Omit { + type: ChannelType.DM | ChannelType.GROUP_DM; + recipients: Recipient[]; + + // TODO: probably more props +} diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts index f99a85e7..0ed88c15 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts @@ -280,6 +280,15 @@ export class User extends BaseClass { return user as PublicUser; } + toPrivateUser() { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const user: any = {}; + PrivateUserProjection.forEach((x) => { + user[x] = this[x]; + }); + return user as UserPrivate; + } + static async getPublicUser(user_id: string, opts?: FindOneOptions) { return await User.findOneOrFail({ where: { id: user_id }, diff --git a/src/util/interfaces/Event.ts b/src/util/interfaces/Event.ts index c3bfbf9b..492821f1 100644 --- a/src/util/interfaces/Event.ts +++ b/src/util/interfaces/Event.ts @@ -40,6 +40,9 @@ import { UserSettings, IReadyGuildDTO, ReadState, + UserPrivate, + ReadyUserGuildSettingsEntries, + ReadyPrivateChannel, } from "@fosscord/util"; export interface Event { @@ -68,20 +71,8 @@ export interface PublicRelationship { export interface ReadyEventData { v: number; - user: PublicUser & { - mobile: boolean; - desktop: boolean; - email: string | undefined; - flags: string; - mfa_enabled: boolean; - nsfw_allowed: boolean; - phone: string | undefined; - premium: boolean; - premium_type: number; - verified: boolean; - bot: boolean; - }; - private_channels: Channel[]; // this will be empty for bots + user: UserPrivate; + private_channels: ReadyPrivateChannel[]; // this will be empty for bots session_id: string; // resuming guilds: IReadyGuildDTO[]; analytics_token?: string; @@ -115,7 +106,7 @@ export interface ReadyEventData { version: number; }; user_guild_settings?: { - entries: UserGuildSettings[]; + entries: ReadyUserGuildSettingsEntries[]; version: number; partial: boolean; }; @@ -127,6 +118,16 @@ export interface ReadyEventData { // probably all users who the user is in contact with users?: PublicUser[]; sessions: unknown[]; + api_code_version: number; + tutorial: number | null; + resume_gateway_url: string; + session_type: string; + required_action?: + | "REQUIRE_VERIFIED_EMAIL" + | "REQUIRE_VERIFIED_PHONE" + | "REQUIRE_CAPTCHA" // TODO: allow these to be triggered + | "TOS_UPDATE_ACKNOWLEDGMENT" + | "AGREEMENTS"; } export interface ReadyEvent extends Event { From ed724472b364ee57288299e8e2bfc2ead1e633de Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Sat, 1 Apr 2023 19:01:34 +1100 Subject: [PATCH 02/17] gatewayresponse?? --- src/api/routes/gateway/index.ts | 4 ---- src/util/schemas/GatewayResponse.ts | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 src/util/schemas/GatewayResponse.ts diff --git a/src/api/routes/gateway/index.ts b/src/api/routes/gateway/index.ts index e6fcf5e3..f5ed4c1f 100644 --- a/src/api/routes/gateway/index.ts +++ b/src/api/routes/gateway/index.ts @@ -22,10 +22,6 @@ import { route, RouteOptions } from "@fosscord/api"; const router = Router(); -export interface GatewayResponse { - url: string; -} - const options: RouteOptions = { test: { response: { diff --git a/src/util/schemas/GatewayResponse.ts b/src/util/schemas/GatewayResponse.ts new file mode 100644 index 00000000..5b771edc --- /dev/null +++ b/src/util/schemas/GatewayResponse.ts @@ -0,0 +1,21 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2023 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +export interface GatewayResponse { + url: string; +} From d944dd4ef42e0991a69e912636b1d60bb0f843ee Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Sat, 1 Apr 2023 19:49:54 +1100 Subject: [PATCH 03/17] allow all role props through ready --- src/gateway/opcodes/Identify.ts | 16 +++++----------- src/util/interfaces/Event.ts | 1 + 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts index 6ae1f9c2..6aa152d3 100644 --- a/src/gateway/opcodes/Identify.ts +++ b/src/gateway/opcodes/Identify.ts @@ -249,21 +249,14 @@ export async function onIdentify(this: WebSocket, data: Payload) { // Generate guilds list ( make them unavailable if user is bot ) const guilds: GuildOrUnavailable[] = members.map((member) => { - // Some Discord libraries do `'blah' in object` instead of - // checking if the type is correct - member.guild.roles.forEach((role) => { - for (const key in role) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - if (!role[key]) role[key] = undefined; - } - }); - // filter guild channels we don't have permission to view // TODO: check if this causes issues when the user is granted other roles? member.guild.channels = member.guild.channels.filter((channel) => { const perms = Permissions.finalPermission({ - user: { id: member.id, roles: member.roles.map((x) => x.id) }, + user: { + id: member.id, + roles: member.roles.map((x) => x.id), + }, guild: member.guild, channel, }); @@ -422,6 +415,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { Config.get().gateway.endpointPublic || "ws://127.0.0.1:3001", session_type: "normal", // TODO + auth_session_id_hash: "", // TODO // lol hack whatever required_action: diff --git a/src/util/interfaces/Event.ts b/src/util/interfaces/Event.ts index cbfa0767..21c34c6c 100644 --- a/src/util/interfaces/Event.ts +++ b/src/util/interfaces/Event.ts @@ -121,6 +121,7 @@ export interface ReadyEventData { tutorial: number | null; resume_gateway_url: string; session_type: string; + auth_session_id_hash: string; required_action?: | "REQUIRE_VERIFIED_EMAIL" | "REQUIRE_VERIFIED_PHONE" From 6ee26082efddfa4f996b4b0424a664a6a592501c Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Mon, 3 Apr 2023 23:50:48 +1000 Subject: [PATCH 04/17] Respect CLIENT_STATE_V2 capability --- src/gateway/opcodes/Identify.ts | 20 +++++++++++++++----- src/gateway/util/Capabilities.ts | 26 ++++++++++++++++++++++++++ src/gateway/util/WebSocket.ts | 2 ++ src/gateway/util/index.ts | 1 + src/util/interfaces/Event.ts | 3 ++- 5 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 src/gateway/util/Capabilities.ts diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts index 6aa152d3..f035fe15 100644 --- a/src/gateway/opcodes/Identify.ts +++ b/src/gateway/opcodes/Identify.ts @@ -16,7 +16,15 @@ along with this program. If not, see . */ -import { WebSocket, Payload, setupListener } from "@spacebar/gateway"; +import { + WebSocket, + Payload, + setupListener, + Capabilities, + CLOSECODES, + OPCODES, + Send, +} from "@spacebar/gateway"; import { checkToken, Intents, @@ -45,11 +53,9 @@ import { Permissions, DMChannel, GuildOrUnavailable, + Recipient, } from "@spacebar/util"; -import { Send } from "../util/Send"; -import { CLOSECODES, OPCODES } from "../util/Constants"; import { check } from "./instanceOf"; -import { Recipient } from "@spacebar/util"; // TODO: user sharding // TODO: check privileged intents, if defined in the config @@ -73,6 +79,8 @@ export async function onIdentify(this: WebSocket, data: Payload) { check.call(this, IdentifySchema, data.d); const identify: IdentifySchema = data.d; + this.capabilities = new Capabilities(identify.capabilities || 0); + // Check auth // TODO: the checkToken call will fetch user, and then we have to refetch with different select // checkToken should be able to select what we want @@ -374,7 +382,9 @@ export async function onIdentify(this: WebSocket, data: Payload) { : undefined, user: user.toPrivateUser(), user_settings: user.settings, - guilds: guilds.map((x) => new ReadyGuildDTO(x).toJSON()), + 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, diff --git a/src/gateway/util/Capabilities.ts b/src/gateway/util/Capabilities.ts new file mode 100644 index 00000000..6c94bb45 --- /dev/null +++ b/src/gateway/util/Capabilities.ts @@ -0,0 +1,26 @@ +import { BitField, BitFieldResolvable, BitFlag } from "@spacebar/util"; + +export type CapabilityResolvable = BitFieldResolvable | CapabilityString; +type CapabilityString = keyof typeof Capabilities.FLAGS; + +export class Capabilities extends BitField { + static FLAGS = { + // Thanks, Opencord! + // https://github.com/MateriiApps/OpenCord/blob/master/app/src/main/java/com/xinto/opencord/gateway/io/Capabilities.kt + LAZY_USER_NOTES: BitFlag(0), + NO_AFFINE_USER_IDS: BitFlag(1), + VERSIONED_READ_STATES: BitFlag(2), + VERSIONED_USER_GUILD_SETTINGS: BitFlag(3), + DEDUPLICATE_USER_OBJECTS: BitFlag(4), + PRIORITIZED_READY_PAYLOAD: BitFlag(5), + MULTIPLE_GUILD_EXPERIMENT_POPULATIONS: BitFlag(6), + NON_CHANNEL_READ_STATES: BitFlag(7), + AUTH_TOKEN_REFRESH: BitFlag(8), + USER_SETTINGS_PROTO: BitFlag(9), + CLIENT_STATE_V2: BitFlag(10), + PASSIVE_GUILD_UPDATE: BitFlag(11), + }; + + any = (capability: CapabilityResolvable) => super.any(capability); + has = (capability: CapabilityResolvable) => super.has(capability); +} diff --git a/src/gateway/util/WebSocket.ts b/src/gateway/util/WebSocket.ts index 972129c7..833756ff 100644 --- a/src/gateway/util/WebSocket.ts +++ b/src/gateway/util/WebSocket.ts @@ -19,6 +19,7 @@ import { Intents, ListenEventOpts, Permissions } from "@spacebar/util"; import WS from "ws"; import { Deflate, Inflate } from "fast-zlib"; +import { Capabilities } from "./Capabilities"; // import { Client } from "@spacebar/webrtc"; export interface WebSocket extends WS { @@ -40,5 +41,6 @@ export interface WebSocket extends WS { events: Record unknown)>; member_events: Record unknown>; listen_options: ListenEventOpts; + capabilities?: Capabilities; // client?: Client; } diff --git a/src/gateway/util/index.ts b/src/gateway/util/index.ts index 627f12b2..6ef694d9 100644 --- a/src/gateway/util/index.ts +++ b/src/gateway/util/index.ts @@ -21,3 +21,4 @@ export * from "./Send"; export * from "./SessionUtils"; export * from "./Heartbeat"; export * from "./WebSocket"; +export * from "./Capabilities"; diff --git a/src/util/interfaces/Event.ts b/src/util/interfaces/Event.ts index 21c34c6c..16df48aa 100644 --- a/src/util/interfaces/Event.ts +++ b/src/util/interfaces/Event.ts @@ -42,6 +42,7 @@ import { UserPrivate, ReadyUserGuildSettingsEntries, ReadyPrivateChannel, + GuildOrUnavailable, } from "@spacebar/util"; export interface Event { @@ -73,7 +74,7 @@ export interface ReadyEventData { user: UserPrivate; private_channels: ReadyPrivateChannel[]; // this will be empty for bots session_id: string; // resuming - guilds: IReadyGuildDTO[]; + guilds: IReadyGuildDTO[] | GuildOrUnavailable[]; // depends on capability analytics_token?: string; connected_accounts?: ConnectedAccount[]; consents?: { From a476319fb419cf2b8e9604f53b075985f2691ea6 Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Tue, 4 Apr 2023 00:15:37 +1000 Subject: [PATCH 05/17] Send d: {} in heartbeat ack --- src/gateway/opcodes/Heartbeat.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gateway/opcodes/Heartbeat.ts b/src/gateway/opcodes/Heartbeat.ts index 7866c3e9..b9b62be3 100644 --- a/src/gateway/opcodes/Heartbeat.ts +++ b/src/gateway/opcodes/Heartbeat.ts @@ -25,5 +25,5 @@ export async function onHeartbeat(this: WebSocket) { setHeartbeat(this); - await Send(this, { op: 11 }); + await Send(this, { op: 11, d: {} }); } From 3e9e8aac3ba0f2890a7eceef9fba13d9b17f6494 Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Tue, 4 Apr 2023 00:16:10 +1000 Subject: [PATCH 06/17] Don't send random read state / guild settings version numbers --- src/gateway/opcodes/Identify.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts index f035fe15..2e661a14 100644 --- a/src/gateway/opcodes/Identify.ts +++ b/src/gateway/opcodes/Identify.ts @@ -389,15 +389,12 @@ export async function onIdentify(this: WebSocket, data: Payload) { read_state: { entries: read_states, partial: false, - // TODO: what is this magic number? - // Isn't `version` referring to the number of changes since this obj was created? - // Why do we send this specific version? - version: 304128, + version: 0, // TODO }, user_guild_settings: { entries: user_guild_settings_entries, partial: false, - version: 642, // TODO: see above + version: 0, // TODO }, private_channels: channels, session_id: this.session_id, From 0095b6505043b5becc2599773253055a9dd6efb7 Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Tue, 4 Apr 2023 00:17:02 +1000 Subject: [PATCH 07/17] Role flags --- src/util/entities/Guild.ts | 1 + src/util/entities/Role.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/util/entities/Guild.ts b/src/util/entities/Guild.ts index e8454986..64dc2420 100644 --- a/src/util/entities/Guild.ts +++ b/src/util/entities/Guild.ts @@ -353,6 +353,7 @@ export class Guild extends BaseClass { position: 0, icon: undefined, unicode_emoji: undefined, + flags: 0, // TODO? }).save(); if (!body.channels || !body.channels.length) diff --git a/src/util/entities/Role.ts b/src/util/entities/Role.ts index 85877c12..3ae5efc1 100644 --- a/src/util/entities/Role.ts +++ b/src/util/entities/Role.ts @@ -66,4 +66,7 @@ export class Role extends BaseClass { integration_id?: string; premium_subscriber?: boolean; }; + + @Column() + flags: number; } From 89c77dc4493909d19d06bc04ff8461f4450df2ba Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Tue, 4 Apr 2023 00:20:13 +1000 Subject: [PATCH 08/17] Missing ready guild properties --- src/util/dtos/ReadyGuildDTO.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/util/dtos/ReadyGuildDTO.ts b/src/util/dtos/ReadyGuildDTO.ts index 5ce49414..72fb5ed8 100644 --- a/src/util/dtos/ReadyGuildDTO.ts +++ b/src/util/dtos/ReadyGuildDTO.ts @@ -97,6 +97,13 @@ export interface IReadyGuildDTO { max_members: number | undefined; nsfw_level: number | undefined; hub_type?: unknown | null; // ???? + + home_header: null; // TODO + latest_onboarding_question_id: null; // TODO + safety_alerts_channel_id: null; // TODO + max_stage_video_channel_users: 50; // TODO + nsfw: boolean; + id: string; }; roles: Role[]; stage_instances: unknown[]; @@ -147,6 +154,13 @@ export class ReadyGuildDTO implements IReadyGuildDTO { max_members: number | undefined; nsfw_level: number | undefined; hub_type?: unknown | null; // ???? + + home_header: null; // TODO + latest_onboarding_question_id: null; // TODO + safety_alerts_channel_id: null; // TODO + max_stage_video_channel_users: 50; // TODO + nsfw: boolean; + id: string; }; roles: Role[]; stage_instances: unknown[]; @@ -207,6 +221,13 @@ export class ReadyGuildDTO implements IReadyGuildDTO { max_members: guild.max_members, nsfw_level: guild.nsfw_level, hub_type: null, + + home_header: null, + id: guild.id, + latest_onboarding_question_id: null, + max_stage_video_channel_users: 50, // TODO + nsfw: guild.nsfw, + safety_alerts_channel_id: null, }; this.roles = guild.roles; this.stage_instances = []; From a9eac479bb7753665c1a9c06a80056e87c1c8b7d Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Wed, 5 Apr 2023 19:42:28 +1000 Subject: [PATCH 09/17] prettier whoops --- src/util/dtos/ReadyGuildDTO.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/util/dtos/ReadyGuildDTO.ts b/src/util/dtos/ReadyGuildDTO.ts index 72fb5ed8..1c1482dd 100644 --- a/src/util/dtos/ReadyGuildDTO.ts +++ b/src/util/dtos/ReadyGuildDTO.ts @@ -98,10 +98,10 @@ export interface IReadyGuildDTO { nsfw_level: number | undefined; hub_type?: unknown | null; // ???? - home_header: null; // TODO - latest_onboarding_question_id: null; // TODO - safety_alerts_channel_id: null; // TODO - max_stage_video_channel_users: 50; // TODO + home_header: null; // TODO + latest_onboarding_question_id: null; // TODO + safety_alerts_channel_id: null; // TODO + max_stage_video_channel_users: 50; // TODO nsfw: boolean; id: string; }; @@ -155,10 +155,10 @@ export class ReadyGuildDTO implements IReadyGuildDTO { nsfw_level: number | undefined; hub_type?: unknown | null; // ???? - home_header: null; // TODO - latest_onboarding_question_id: null; // TODO - safety_alerts_channel_id: null; // TODO - max_stage_video_channel_users: 50; // TODO + home_header: null; // TODO + latest_onboarding_question_id: null; // TODO + safety_alerts_channel_id: null; // TODO + max_stage_video_channel_users: 50; // TODO nsfw: boolean; id: string; }; @@ -225,7 +225,7 @@ export class ReadyGuildDTO implements IReadyGuildDTO { home_header: null, id: guild.id, latest_onboarding_question_id: null, - max_stage_video_channel_users: 50, // TODO + max_stage_video_channel_users: 50, // TODO nsfw: guild.nsfw, safety_alerts_channel_id: null, }; From df696d362a135d483d72f05697d41322bd974ad0 Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Wed, 5 Apr 2023 20:47:47 +1000 Subject: [PATCH 10/17] Close if already identified --- src/gateway/opcodes/Identify.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts index 2e661a14..5816c308 100644 --- a/src/gateway/opcodes/Identify.ts +++ b/src/gateway/opcodes/Identify.ts @@ -59,7 +59,6 @@ import { check } from "./instanceOf"; // TODO: user sharding // TODO: check privileged intents, if defined in the config -// TODO: check if already identified const getUserFromToken = async (token: string): Promise => { try { @@ -73,6 +72,11 @@ const getUserFromToken = async (token: string): Promise => { }; export async function onIdentify(this: WebSocket, data: Payload) { + if (this.user_id) { + // we've already identified + return this.close(CLOSECODES.Already_authenticated); + } + clearTimeout(this.readyTimeout); // Check payload matches schema @@ -106,7 +110,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { this.shard_id < 0 || this.shard_count <= 0 ) { - // TODO: why do we even care about this? + // TODO: why do we even care about this right now? console.log( `[Gateway] Invalid sharding from ${user_id}: ${identify.shard}`, ); From c3f5047e0ce7e0cbde6fab09b7ba647b0b6aeea3 Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Wed, 5 Apr 2023 21:18:31 +1000 Subject: [PATCH 11/17] scripts n shit --- scripts/benchmark/connections.js | 82 ----------------------- scripts/benchmark/index.js | 22 ------ scripts/stress/identify.js | 53 +++++++++++++++ scripts/{benchmark => stress}/users.js | 0 scripts/stresstest/.gitignore | 3 - scripts/stresstest/accounts.json.example | 1 - scripts/stresstest/config.json.example | 5 -- scripts/stresstest/index.js | 58 ---------------- scripts/stresstest/package-lock.json | Bin 29271 -> 0 bytes scripts/stresstest/package.json | 17 ----- scripts/stresstest/src/login/index.js | 38 ----------- scripts/stresstest/src/message/send.js | 44 ------------ scripts/stresstest/src/register/index.js | 58 ---------------- 13 files changed, 53 insertions(+), 328 deletions(-) delete mode 100644 scripts/benchmark/connections.js delete mode 100644 scripts/benchmark/index.js create mode 100644 scripts/stress/identify.js rename scripts/{benchmark => stress}/users.js (100%) delete mode 100644 scripts/stresstest/.gitignore delete mode 100644 scripts/stresstest/accounts.json.example delete mode 100644 scripts/stresstest/config.json.example delete mode 100644 scripts/stresstest/index.js delete mode 100644 scripts/stresstest/package-lock.json delete mode 100644 scripts/stresstest/package.json delete mode 100644 scripts/stresstest/src/login/index.js delete mode 100644 scripts/stresstest/src/message/send.js delete mode 100644 scripts/stresstest/src/register/index.js diff --git a/scripts/benchmark/connections.js b/scripts/benchmark/connections.js deleted file mode 100644 index 4246c646..00000000 --- a/scripts/benchmark/connections.js +++ /dev/null @@ -1,82 +0,0 @@ -/* - Spacebar: A FOSS re-implementation and extension of the Discord.com backend. - Copyright (C) 2023 Spacebar and Spacebar Contributors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -require("dotenv").config(); -const cluster = require("cluster"); -const WebSocket = require("ws"); -const endpoint = process.env.GATEWAY || "ws://localhost:3001"; -const connections = Number(process.env.CONNECTIONS) || 50; -const token = process.env.TOKEN; -var cores = 1; -try { - cores = Number(process.env.THREADS) || os.cpus().length; -} catch { - console.log("[Bundle] Failed to get thread count! Using 1..."); -} - -if (!token) { - console.error("TOKEN env var missing"); - process.exit(); -} - -if (cluster.isMaster) { - for (let i = 0; i < cores; i++) { - cluster.fork(); - } - - cluster.on("exit", (worker, code, signal) => { - console.log(`worker ${worker.process.pid} died`); - }); -} else { - for (let i = 0; i < connections; i++) { - connect(); - } -} - -function connect() { - const client = new WebSocket(endpoint); - client.on("message", (data) => { - data = JSON.parse(data); - - switch (data.op) { - case 10: - client.interval = setInterval(() => { - client.send(JSON.stringify({ op: 1 })); - }, data.d.heartbeat_interval); - - client.send( - JSON.stringify({ - op: 2, - d: { - token, - properties: {}, - }, - }), - ); - - break; - } - }); - client.once("close", (code, reason) => { - clearInterval(client.interval); - connect(); - }); - client.on("error", (err) => { - // console.log(err); - }); -} diff --git a/scripts/benchmark/index.js b/scripts/benchmark/index.js deleted file mode 100644 index ca799a85..00000000 --- a/scripts/benchmark/index.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - Spacebar: A FOSS re-implementation and extension of the Discord.com backend. - Copyright (C) 2023 Spacebar and Spacebar Contributors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -require("dotenv").config(); - -require("./connections"); -require("./messages"); diff --git a/scripts/stress/identify.js b/scripts/stress/identify.js new file mode 100644 index 00000000..7dea5e4d --- /dev/null +++ b/scripts/stress/identify.js @@ -0,0 +1,53 @@ +/* eslint-env node */ + +require("dotenv").config(); +const { OPCODES } = require("../../dist/gateway/util/Constants.js"); +const WebSocket = require("ws"); +const ENDPOINT = `ws://localhost:3002?v=9&encoding=json`; +const TOKEN = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEwOTMxMTgwMjgzNjA1MzYxMDYiLCJpYXQiOjE2ODA2OTE5MDB9.9ByCqDvC4mIutW8nM7WhVCtGuKW08UimPnmBeNw-K0E"; +const TOTAL_ITERATIONS = 500; + +const doTimedIdentify = () => + new Promise((resolve) => { + let start; + const ws = new WebSocket(ENDPOINT); + ws.on("message", (data) => { + const parsed = JSON.parse(data); + + switch (parsed.op) { + case OPCODES.Hello: + // send identify + start = performance.now(); + ws.send( + JSON.stringify({ + op: OPCODES.Identify, + d: { + token: TOKEN, + properties: {}, + }, + }), + ); + break; + case OPCODES.Dispatch: + if (parsed.t == "READY") { + ws.close(); + return resolve(performance.now() - start); + } + + break; + } + }); + }); + +(async () => { + const perfs = []; + while (perfs.length < TOTAL_ITERATIONS) { + const ret = await doTimedIdentify(); + perfs.push(ret); + // console.log(`${perfs.length}/${TOTAL_ITERATIONS} - this: ${Math.floor(ret)}ms`) + } + + const avg = perfs.reduce((prev, curr) => prev + curr) / (perfs.length - 1); + console.log(`Average identify time: ${Math.floor(avg * 100) / 100}ms`); +})(); diff --git a/scripts/benchmark/users.js b/scripts/stress/users.js similarity index 100% rename from scripts/benchmark/users.js rename to scripts/stress/users.js diff --git a/scripts/stresstest/.gitignore b/scripts/stresstest/.gitignore deleted file mode 100644 index bde26fd4..00000000 --- a/scripts/stresstest/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/node_modules -config.json -accounts.json diff --git a/scripts/stresstest/accounts.json.example b/scripts/stresstest/accounts.json.example deleted file mode 100644 index ec1b2a0d..00000000 --- a/scripts/stresstest/accounts.json.example +++ /dev/null @@ -1 +0,0 @@ -[{"email":"org11ncxa8.wi732t9b4o@spacebar.chat","password":"x.ibahfyqwle.ne4hajbzp11.gpc4lcup4"},{"email":"rek6kyprik.i5hldol255@spacebar.chat","password":"1.o3w16haor2y.0e1ey2yk1x.1r0gn5o5h"},{"email":"07o37povsi.uk5q9dtxbp@spacebar.chat","password":"8.6z64gcjavp1n.uar3qqymwfi.g0sfmmbd9m"},{"email":"94di5zaie4.n1vhzdfsj@spacebar.chat","password":"1e.k3ijylxme1u.e9xr9yqbrk.3tir7qnvh"},{"email":"zbrqft4chj.yl73e5puq3@spacebar.chat","password":"5.nkc3g8cvwl15.dmp8ywmkka.m79e9t4wij"},{"email":"br1wknv7k.6hw6yl69e@spacebar.chat","password":"3.gimzx06u7mh.6rjrdflo9j1t.h3d8k2f5t"},{"email":"cdehs12h6h.iexxvg16xf@spacebar.chat","password":"1i.5ab7e9rtwl1n.31qtfv7cz9.e1k313py9"},{"email":"pazx37jpra.mgsb8k50ip@spacebar.chat","password":"d.eg5dwqvd981e.5qobehiyffe.6k5pb4fqm"},{"email":"vs6k62ak2o.xo1v4w0rj@spacebar.chat","password":"c.hrkcrnlxlg1d.w18ztd39d1p.eycgehb49"},{"email":"u5d27rbewm.3p0wa7s899@spacebar.chat","password":"s.1r7o1ur8o9k.puzbm1uuta9.an5m8bhh0a"},{"email":"vyp6x66fr.yv74eftomo@spacebar.chat","password":"m.w0c7h21asf.pq2lj3uot6a.xnhv9ftqii"},{"email":"da0k6sra2n.qts4gs9ufg@spacebar.chat","password":"h.8e42ud5f6r5.896sp1t8y6e.shwe0d8no9"},{"email":"l093koc05n.81vt1v8tsx@spacebar.chat","password":"11.zhkv1jbhdf.0ub2po3mnu.no4lq8l4"},{"email":"115tfo7ct.muvy04u0tf@spacebar.chat","password":"1c.4bpk2a17z1p.gw2h6qmvhez.57drs0quz"},{"email":"dq6bk1hjch.huw092gkhr@spacebar.chat","password":"1l.kp28mclrtee.5i4dmacbpc1j.hcqgemsni"},{"email":"8g8q9v3wmk.l2frwpuds8@spacebar.chat","password":"1n.i0wwg0njmv.teaiqjqalt1g.ib6551nh4"},{"email":"5b3y3neqxa.mmi0ex2hxd@spacebar.chat","password":"8.tvz7q9uw0hm.6ufz6fu65c1a.88vp9di6e"},{"email":"mg28g3krsp.35h1akxrqj@spacebar.chat","password":"s.y8j2n19iffr.qyecyrgo6ig.6hgrc5vy9"},{"email":"ehtumcok2j.2oozlhiq97@spacebar.chat","password":"11.uq0up8g8h1q.ofvjsx29yd.pfwen3kr38"},{"email":"le98rah1uc.au4ug9tpnt@spacebar.chat","password":"t.q15zsc0q2mt.2nj3jsdxhfc.leb9ba1xku"},{"email":"hrroex9f5n.6cl98h3jsi@spacebar.chat","password":"17.qnqqhg2us4.kh92v74atg19.49ufgil7g"},{"email":"w95wrrn48.6gfnue7dcq@spacebar.chat","password":"b.jnqgbi89oj1k.8rn0llovbll.kcblui80th"},{"email":"rqo4n0il5w.4gl1u8hlyc@spacebar.chat","password":"17.41d1lpjmi0.d8ijhslby11w.sjn7sqhi9"},{"email":"6dv3yp4kon.pk7ye6q1r6@spacebar.chat","password":"0.j70py6yysjz.sf56ebpp2gp.z68yo9hiim"},{"email":"knmi9qkige.5v1bg6h09w@spacebar.chat","password":"1d.7n58xntwg1s.umnglex7h13.c5xrsfkosm"},{"email":"cefymgc7te.dd81jabws4@spacebar.chat","password":"1u.73ea7dde1o.0i1fhyaird.sjk30nky1e"},{"email":"33xcwiqf73.r6khs46a7j@spacebar.chat","password":"b.5p5gdmh1891f.11g4590n5a.vfoek6qjb9"},{"email":"9zcgmr84s6.utnlygoubi@spacebar.chat","password":"8.g4v53t7kcl16.wgaiufzgg1u.pusdfdneb"},{"email":"26vpzekrdw.3bwq27wla@spacebar.chat","password":"11.yxey8293lj1d.nxhkju2eke.hl86mcvswd"},{"email":"vvq6w36r84.lr1auhpfc@spacebar.chat","password":"y.7vlqbpftom.6xfrtozd11k.ycf9ifi7o"},{"email":"6ejeiq64yo.zorve5saw4@spacebar.chat","password":"o.eue50qp1frq.qi0rwphg3dv.psph7va2fb"},{"email":"6s0hu88ro8.hsckrmud1i@spacebar.chat","password":"16.fc836nhb91a.ul37503ppg.l62wgser4i"},{"email":"h8qwchz2x9.5br1kcw1iv@spacebar.chat","password":"l.iw1041wgy.6azyc9h6vb.br9cr0dmn8"},{"email":"yx13rst2hu.ybisfdwgv@spacebar.chat","password":"8.5yasf5ba619.ir0toxu251p.tbgwjd18f"},{"email":"1j7vrr1trh.wqj0ozl357@spacebar.chat","password":"1m.iucscoe7b0.6ca1jfaag361.c2trc36mnvk"},{"email":"9w2w572pzr.fv1rk360pp@spacebar.chat","password":"t.labzw6qw8t3.33k42uvhgd1s.e1gj71h14"},{"email":"yf5e43ol4.6509owbcxa@spacebar.chat","password":"12.jewy0uvx1m1m.ce28kht6dk.v2p0bzlvz"},{"email":"gzny2o1re.1xrl0ua7yd@spacebar.chat","password":"h.3valf7r8jh.6bzfr4ions.r4b2mt0l0g"},{"email":"bc77a5kw9v.hu5barps6q@spacebar.chat","password":"1a.jt11azsa81j.4v70jvm9d1o.hflrb1tigk"},{"email":"ltoezpefev.hrvnxmq9ee@spacebar.chat","password":"b.v4f5lqrlc912.dx4dd4xq91v.zj345to03"},{"email":"svcpsuoenk.b8mfqxpbzg@spacebar.chat","password":"1a.7aobev8b4r.xqqfybkcs1t.cjuswku0a"},{"email":"n0mroewqq.svq5iq57pe@spacebar.chat","password":"1l.zxm1xhlavp.65rp7bz57x.01vjajdsc"},{"email":"trly6yupd.dt37kh07dn@spacebar.chat","password":"1i.o2ieg72fz1j.er031tzerx.2ngg4dcvlh"},{"email":"ickkf14cqv.9pu2pnmx7n@spacebar.chat","password":"18.pyhd9ruatl1k.erfchcjc95.wfd67r5e8x"},{"email":"5o4ornfwy9.yabymb8e2k@spacebar.chat","password":"1g.117kmei8df10.cedozr4vee.08te5d44nb"},{"email":"p0ulegfi3.dgmar6qc2v@spacebar.chat","password":"1h.tle7s3ed82.un20o5nv3dk.wnz4w802h8"},{"email":"58gejpvr6v.jolxrsl83p@spacebar.chat","password":"e.ksw14117hbo.f0pgufr3na.ssrjys23al"},{"email":"vf349zeoja.r8bjel59kd@spacebar.chat","password":"1o.79kh6e6glm9.d76d86g1jp16.u37p4jhf7"},{"email":"uc786nn0go.n9ygun6owj@spacebar.chat","password":"m.xo4bwhct5be.lpokbj59w8p.z4l52dzv1r"},{"email":"5jgx24s87u.odlx0bfo0r@spacebar.chat","password":"1k.ni9jyfol7h1g.vczzsa8dbg.r4bhoh5op"},{"email":"2v44408x8l.unfspunnnm@spacebar.chat","password":"16.63njhji5b4.r4xkcf672f1a.x389dr603g"},{"email":"ityj8kcvrm.9djzannsll@spacebar.chat","password":"1p.6jdbhaxiqc.nfnpw7e09g8.967dtt2dy3"},{"email":"8csbvl9qot.28etdf4pf@spacebar.chat","password":"1b.52rdo5qmj3.ta9jw1wm3k9.m96fe27tp"},{"email":"dqndi38hsq.yv77wk3mov@spacebar.chat","password":"k.zpjwpwxmlr1f.tbj03rxayn17.9x451qclu"},{"email":"ohwmvag9j.w6t8ngs4t@spacebar.chat","password":"b.h1ta0mly991q.wzu1ssffyk1h.kc10wt8i2"},{"email":"2mmors2h0w.jwukibc7oi@spacebar.chat","password":"y.xo4kgepqa1t.b77zwt1in5.3um79fx22r"},{"email":"ux0q6gvwnr.gnywxxrsn@spacebar.chat","password":"g.52userbsonu.ny8omqaduf.rvhtwq4jer"},{"email":"0q12b4zet.y87zc04r8r@spacebar.chat","password":"1t.79mg1a9q85.k66wagu67j14.ad0gw3caw"},{"email":"gatbconrvq.dsopxa8fkk@spacebar.chat","password":"1n.fycl7y9roh1p.4yg37pst4k16.votnvabrf"},{"email":"mmp9g1b1v.xz1w4qzxee@spacebar.chat","password":"1k.cjmz3huosl.jh502yz5jf1e.hyce7qc67"},{"email":"5s90s1hbns.b027pfiv3s@spacebar.chat","password":"1l.86ipkmi6fg.scabtvproj4.yw4nb9qui9"},{"email":"l4zrvtrbpb.1r627sllk@spacebar.chat","password":"1l.zzm1dunzzek.10sr7mp01ly.yyrjj1hsli"},{"email":"xih9rwk90i.rmdifv40g@spacebar.chat","password":"15.db9k0pxci1v.hs6l033urm.5a1zv42fhl"},{"email":"55mq93jdq.2dhr1ps4f5@spacebar.chat","password":"i.v5hpg2qez1u.xhs32cwes1h.n10pexmfff"},{"email":"5c0vb38rul.5su27w4pn8@spacebar.chat","password":"12.y87q6jxq41m.qgiji2j0hm.gmy2wuavc"},{"email":"qjk2eoqeqq.ljq4dig10o@spacebar.chat","password":"0.lpu8eio3hra12.mq8qcehpe1e.77p7zilh4"},{"email":"b45ltbf5d.o4oouuik1e@spacebar.chat","password":"1u.wb7hn2b1x1k.jys5p3ri4j.9ew9jab3ll"},{"email":"1mw205tjri.gpi2h76eps@spacebar.chat","password":"1g.kyh53pnamd13.5yufexmyv1h.r56pmhm7i"},{"email":"8y0psdjq2s.ifqyimhnkj@spacebar.chat","password":"1d.fi03hlwk41u.b89w0vrd712.ljudzvdo6"},{"email":"ls73glp0q9.3rtqyb262@spacebar.chat","password":"1.z70c4ef5hfi.fes9zmue2it.5cobkz3ah"},{"email":"ipe2um46bi.in93oau1l@spacebar.chat","password":"5.a5he7keuru1n.l05ivx4n24.piohqdy51w"},{"email":"mt16ta8diq.krypy2t9cv@spacebar.chat","password":"n.zk4goctn5p3.r1fhllqy1m1p.ni2q3y68w"},{"email":"qehwflm0ja.x5uvmxgfle@spacebar.chat","password":"1e.r2sj0uimq1f.nmtozr8qd1s.xgvz4d62b"},{"email":"0ppn1iwd6.ivrqbvn17i@spacebar.chat","password":"1n.fr6x1pbzjl.c8xwipgo6c.m1me2h2g58"},{"email":"xiiq47ofev.u9z0gndxs6@spacebar.chat","password":"1t.7tfe0181ij.jbznx5eebs.ytm50kp5qf"},{"email":"kqhk3lt2mo.o4y7u23zbu@spacebar.chat","password":"1b.bkoqmxjcf1l.c5q9oneuz1u.00x93z7l4"},{"email":"ri64c5o5zn.o429slph64@spacebar.chat","password":"1r.mre2hu1gpu.401xyxa6eu.j98cetaplg"},{"email":"j5jpukoktw.q5bseyjfu@spacebar.chat","password":"h.k1ar11fpx1m.n50t8tz4k4.9oj17rtdjw"},{"email":"cg8gyuhu16.jezv2bo8n@spacebar.chat","password":"1c.vyfo117pd1b.hxlc7e9zve.j6ej7ho2rk"},{"email":"7ngysyss7w.yjy0whd5fh@spacebar.chat","password":"12.pl4jjp66wi1r.xx7s13gsgy.v2slv2vyx"},{"email":"7uhylwdaiq.w557htx0x@spacebar.chat","password":"1j.icm6w8m4mh.4qyoql77m8.ar8kliax0s"},{"email":"y6yn1ckm1e.7xxizerecm@spacebar.chat","password":"1e.om7n18zisn1w.usblhxf4p1m.r9ke41xox"},{"email":"uwdsktqhuw.4vmh5gmg7d@spacebar.chat","password":"6.cdte4bk24b16.cf1sbtxlx1o.n62w4weh9"},{"email":"8v1nt755y.w0y1jgfcgm@spacebar.chat","password":"f.ozxpvznxj41o.bs5s5dhua1l.ffayy0gsy"},{"email":"rmy9b61cij.qir0bjorcm@spacebar.chat","password":"1h.bxjxpx0u6f13.e97yh8g761c.j8zog74iql"},{"email":"93ir0yiyi9.1f7bfzt3fb@spacebar.chat","password":"18.vky28kwlw14.w1wsoyu6c15.yhxbr725xe"},{"email":"g0kqw9plr.v2zcovhyg6@spacebar.chat","password":"1r.3txq1jt4zl1d.ha0ejtekjh.xhjl9e6vqg"},{"email":"xmk2q5zxa.v1ka9gm3a8@spacebar.chat","password":"1l.ryvykh3ihm16.rxea04ifq0.h14sz83yisv"},{"email":"mqt2bmltj9.53o16bc6xn@spacebar.chat","password":"i.vt66ajtme1f.lllyzaprk16.yb0yh0o1z"},{"email":"4kvjyddsv.7u7lmex2df@spacebar.chat","password":"1i.axaegtd0qz.2yvfr5n261g.8s8fprsd8"},{"email":"yigntcopcc.8bchnlmclm@spacebar.chat","password":"n.b5yn5xried1d.siep9e4fb1t.h6s6erw5t"},{"email":"meubr1b03t.t97015wih6@spacebar.chat","password":"5.wu3izi2gyqi.iurx5qpp7l.znq1htzuel"},{"email":"xz3gta0hi.1x5o83xyee@spacebar.chat","password":"14.uafjiryde3.oin9k24w3510.vkjmjleb4i"},{"email":"9jkrkk9r6o.6ossrgj919@spacebar.chat","password":"v.u9531wtw2o11.151eg145bf.bk57nd0s6u"},{"email":"kf9fdmnacv.67shfcubvn@spacebar.chat","password":"1i.7f1olv2hkt.v2cso7zxlfw.8ylhl33g1"},{"email":"k8zuiett0r.0w299k0t9j@spacebar.chat","password":"t.1mrpwsil15.999lbrfvz1h.7od0kjlxo"},{"email":"8m9rt3vgg.vkpf6apx9@spacebar.chat","password":"1n.2ohz11tk412.5ezp8ujcwn8.rvqqrozh9s"},{"email":"rfavhpnhc.6xwy7o3ulm@spacebar.chat","password":"11.ikd54271zj.vq3brjark7.h1ryvz7ap8"},{"email":"6zmju5azrd.4bes4a3cq@spacebar.chat","password":"5.litb6taajto.ownyp3uhjkh.f543o47uc9"},{"email":"ml5pst7t3g.kbvn8b1vg@spacebar.chat","password":"1q.co2aumj6fw.fa18frro5e.vnpotfg209"},{"email":"kaa1r6srjs.wjriguic5e@spacebar.chat","password":"y.y635jqxai9.s4hcd1weni5.51i7z3c26r"},{"email":"n09uhfkuc5.9aqau9qyk@spacebar.chat","password":"1f.wtjqoqzdwg6.mfvvtcwtx91t.8ujt3pwx4"},{"email":"6y5y3px9oa.4183pg5aq6@spacebar.chat","password":"11.8a00uh75g1i.d462wzpqv1t.dnd8sdvr"},{"email":"aqdzadem03.f8uv1m4zv4@spacebar.chat","password":"4.4ndx89thn53.afcjfzjqe51o.ivaemdp5hf"},{"email":"oqv3944yav.31ccatif3r@spacebar.chat","password":"1w.9cstqu9o21f.p40uqca3vl19.iqnn79lqde"},{"email":"akzyzmigv.9c6w5aj4o@spacebar.chat","password":"m.m382wa8nznr.szvso4c03ke.ttw2jhnwh8"},{"email":"13dqfm57jo.e05e711ggt@spacebar.chat","password":"1b.t1b51jt7lf.rhi4j32rw91u.0foqthilf"},{"email":"3derfs5v66.s2kbedbm3o@spacebar.chat","password":"t.e153si8xso1m.9rv9il857fd.e3i0di3ope"},{"email":"92k9vmws7.dt9mvv6ijh@spacebar.chat","password":"1r.r8oy0su9c1e.irtwz9gdna.3fddwt8k4n"},{"email":"w1huzvblr.q9qp44japt@spacebar.chat","password":"1v.dfdr92srfs.3x2wd25frh15.z73xb3vol"},{"email":"vne3an2fif.32eq9woyl@spacebar.chat","password":"7.lurd6n689ek.sf3gedrf711.5xclyfsn3"},{"email":"298zj4dvxf.5sfh7f2e2m@spacebar.chat","password":"n.1rbv0z54wr1n.nt2041ujks.0gwbe80zyl"},{"email":"ywp1ssr2zh.gl97epixxu@spacebar.chat","password":"1w.gfpvze8vq1p.is7b2795819.4hilzah3"},{"email":"kqzujy4m5j.ocydwl4yyh@spacebar.chat","password":"1c.sqxzxuareez.fgczf2qh3en.yi5vo23phn"},{"email":"ck8n5p7d6d.2vu4cdm6iw@spacebar.chat","password":"1o.g3lq6grnm1t.otf44zgiw1c.jfdgqubfjl"},{"email":"s3vqe9bzj.muec34461t@spacebar.chat","password":"1u.i4u3eidof19.tl8hf5fpdv.mvbij0fdgc"},{"email":"7hedrsktw.oqe4hym4us@spacebar.chat","password":"1j.orlptqc2h.hs6661zehh1r.ngepsoldvf"},{"email":"44tm2rsu6j.oxrw5ib1np@spacebar.chat","password":"1o.n71dxtllrf9.htwjv6fsi81l.5w9pyr8eee"},{"email":"o28saa2e4t.m49530ir45@spacebar.chat","password":"16.z31xrcp6li12.uaklzxvskl.nqyq23zqb"},{"email":"aaz3kkwx2q.u42rdyacy6@spacebar.chat","password":"1k.aohk44bxqkq.6lec7k6yfa14.geiq4ok3b"},{"email":"ntw1oc87mh.js3q1iqxrh@spacebar.chat","password":"15.li45vduoy15.h90fv4ytl1t.3v78qdvcq"},{"email":"cpkgoh313.lkdhhl039a@spacebar.chat","password":"3.fdw00uv0dn1v.qz6frlgeh0.3g0c7xnn9le"},{"email":"wznnajnyww.3f5s5cf0lt@spacebar.chat","password":"4.3d2ro1uvag2.9yum8m4gd5t.yd1zriwovn"},{"email":"odwdxlk49g.m113aywba@spacebar.chat","password":"y.24hnap1ckh.n0q1dtobf717.0tzaopasse"},{"email":"0xt66uuwbs.24qfa4w82q@spacebar.chat","password":"e.3cfcd0usw57.oydvjpl5wm1b.sxnf38ihh"},{"email":"4pxgasro0t.xifcrlp26f@spacebar.chat","password":"1d.oxpqgh8jbgb.6epjawtwga1u.o5d33jm"},{"email":"l202g9q8xo.t4ck4xu44v@spacebar.chat","password":"d.gyul2yhu7h1g.163rzn4kqik.e5qlstdwp"},{"email":"8mwzma33ko.b9on13ypjl@spacebar.chat","password":"b.0sdy90whqr1o.rruwt57r8l14.hjejwclmr"},{"email":"h8dm19fu77.hzpnw8famh@spacebar.chat","password":"s.q49kg1uq8gc.046rudurb1o.2lqegjfds"},{"email":"exkp3ve6z7.mdydbk9jy@spacebar.chat","password":"16.bq8o0d13sd8.tri7wtdjpro.2ebtbyqgtt"},{"email":"n8fe02yphv.huwi91ywha@spacebar.chat","password":"1d.8qp5wkq541k.ulwk4bzjsm19.q3qbxorto"},{"email":"lsslgvrdyb.u86qng3p7o@spacebar.chat","password":"11.9q1m8gwavd.9z3kflcg5k1e.lrux8aqm8"},{"email":"0jur86ya2p.gb26btuz7@spacebar.chat","password":"n.fflp1f1yksg.10rh6etc61.yld8y7u9hi"},{"email":"raseda2c45.vl9resp89r@spacebar.chat","password":"b.6gd8az3ljg.es1yjenqskk.i4i8m466p"},{"email":"jmam7ha069.b96jzg1bkl@spacebar.chat","password":"1f.2z41vc92bo.84f3d3j3gra.5yev9enzv"},{"email":"pp2rki7hjd.a037bg6u6@spacebar.chat","password":"3.nktq53a97c19.khsapwl0wd.ej16kksime"},{"email":"c35l8m3ikr.e7vx8nmbil@spacebar.chat","password":"2.oryjofui8mu.7jes36sirs1u.oclq1geaf"},{"email":"ufhsl7tn5u.j4ey0abswv@spacebar.chat","password":"14.uctn73o6h1n.t75arwloxgf.nvgdr4l41o"},{"email":"8ru4fr2ed.kf8ffg9ko8@spacebar.chat","password":"n.hqxwr2ypwd1l.vu23byfp3c.nzgszptoqk"},{"email":"6gmjeij67o.ep5256bmf@spacebar.chat","password":"1i.237gs5pk5j1w.yvuhvp9ho15.l4qibsw5i"},{"email":"4wrhgqel0w.e0sz7l0zki@spacebar.chat","password":"w.g09qtor0p1g.a5uzjl6u3g17.v3z6rhb9h"},{"email":"3860ixs8g.6pha1slnur@spacebar.chat","password":"m.o7o62cqw3g.wkkaak7zz8.h82m4nenbf"},{"email":"wnnpg8stto.zwsxqfp38i@spacebar.chat","password":"c.k2b6jn1b3r14.ojpvlbxil1r.rpkncuyqp"},{"email":"t04ss33dlw.98dpq7j8rg@spacebar.chat","password":"5.7zgfmumai7.iphztcsjfw1h.sq2kp3j9j"},{"email":"hy53et7kw6.vsku4tebj9@spacebar.chat","password":"3.ayjddj0roe1m.ngz1qajzlgu.xue35w1d1d"},{"email":"252ueajele.j4euv8la1d@spacebar.chat","password":"h.tw1utyw7mh9.ydii1rkvp4.8xafwfxrqd"},{"email":"ye2mi1d86.uqa7ig7qxb@spacebar.chat","password":"j.pn3eoar1ft1e.k8febwch91o.fzau5lnbx"},{"email":"4cq5y22mm8.q33hk612wu@spacebar.chat","password":"1l.7tx03ihc9e1a.3i2l76ur5.28yffumat8"},{"email":"d9op87vvj7.vbu23p4mnq@spacebar.chat","password":"10.z7pgyokesip.0i0axexmwpa.0p5xrlag9k"},{"email":"tnhgsqizxh.14ulf4jinl@spacebar.chat","password":"j.8p0jucy5xk10.creosnkf2o.vzznt05x"},{"email":"8h2h3w3ex5.8ogl7f027n@spacebar.chat","password":"1b.08wkhdm03g3.8hdklh1zj41t.fq57w9raf"},{"email":"ommn4ocwtn.1fkdjbz2v8@spacebar.chat","password":"1e.i8k15b9uk1p.70n34lxbzf.4inv63cwt8"},{"email":"fnxg92zeqn.ljg5uumt3e@spacebar.chat","password":"w.ltho2dsgveu.d1ome2w0x8j.7wr2hq1wk8"},{"email":"lzi8aurosp.mck9i974of@spacebar.chat","password":"u.zezf4qdz2p0.l2g634tak98.ql0n1tg6sq"},{"email":"fdhv4fccm5.o9x209i94g@spacebar.chat","password":"5.2o84u6v43619.4c0c71a9gk7.n9cmjegefv"},{"email":"tuedrm1ajt.bxjgzsyj4s@spacebar.chat","password":"1m.ng2h807gvu.rhd056e6bbb.lkvewwp2tg"},{"email":"488bryb32a.x928qzsf8g@spacebar.chat","password":"y.n2c3x3irffa.fz9xwiimno8.nnpvm280oc"},{"email":"n928oorjaa.kj35rf9p35@spacebar.chat","password":"1l.flvmvopcj16.pmx6n9hi7hi.v9odjzq3at"},{"email":"emzd8qz0f8.b972dvhf0m@spacebar.chat","password":"3.7umgbd5apm18.0n6yi8ol9g.m4607npuc"},{"email":"g7jzdulwv5.0a2wzws2ua@spacebar.chat","password":"10.1u1sac19wkf.lvi2qwfhtq16.8wbdddpms"},{"email":"giuivahumo.7iqapfnbfr@spacebar.chat","password":"q.pzck5qtbype.llhl9ypv6b1e.3dz8gsv0pg"},{"email":"0t84mm2pj9.kycgvqkuag@spacebar.chat","password":"1t.kpvjmvipo14.kwv0np3ordv.ustw31ifu"},{"email":"vx5t6yurg7.pocn2c069m@spacebar.chat","password":"f.gdwgrk0wia1u.7m1ozam0b0.d5y62kwyih5"},{"email":"p7arvq1hha.7wryrvhvl@spacebar.chat","password":"1v.eb62r3rx71h.d3fhbfdxa1l.4gzcu184s"},{"email":"adp64dkhdd.q2nc2qvy3@spacebar.chat","password":"q.96rt5rc517h.3f0foodom4.h1wee4z428"},{"email":"pfhrq2kv8.92dq9bxy8a@spacebar.chat","password":"1g.nfaha2xx7hi.vtng22emxs1l.fpbra2wo6h"},{"email":"jjnysdssoc.eqr6v2pqeo@spacebar.chat","password":"1.112arb9m3cb.w3yfq6ekz1d.keb15ptd5"},{"email":"m5rab6dhrc.p1tnxv9feg@spacebar.chat","password":"1.3guc8m7j0uj4.xphgg3121714.7ii7ah6g7f"},{"email":"zgq1iount.blsiqtyvc6@spacebar.chat","password":"16.dzt6188au1w.ilwc3p3ds17.7j7lcsqur"},{"email":"ix7nx1ce4a.3wj4gs8b7p@spacebar.chat","password":"13.p2v1p2nwa1t.2yarcqsmzk.ay7w9u0p1r"},{"email":"5jh7wm63ug.feyytgy11g@spacebar.chat","password":"10.otpp2mz0smb.uv94hcp26c8.a3nlz16n14"},{"email":"9cd7yy1ps4.jet2fn1fdb@spacebar.chat","password":"9.bjtfocm7zk12.sushyeb1yg.lhtmj6a70t"},{"email":"20u20f6dlk.l8n5tvh2re@spacebar.chat","password":"19.qfrr25rarj.4tzf063a9n4.3i5s3vm30b"},{"email":"b1hnmmwcb.q21mrflg1h@spacebar.chat","password":"3.ysh10ultyjz.nz8azt84216.lxn1kgvly"},{"email":"j7nhj8s2d.aqaeidbi8m@spacebar.chat","password":"g.nlnw7ejuqbz.41exhwj2wiv.1yr0njmd"}] \ No newline at end of file diff --git a/scripts/stresstest/config.json.example b/scripts/stresstest/config.json.example deleted file mode 100644 index 73f52f05..00000000 --- a/scripts/stresstest/config.json.example +++ /dev/null @@ -1,5 +0,0 @@ -{ - "url": "", - "text-channel": "", - "invite": "" -} diff --git a/scripts/stresstest/index.js b/scripts/stresstest/index.js deleted file mode 100644 index d3f2d2ba..00000000 --- a/scripts/stresstest/index.js +++ /dev/null @@ -1,58 +0,0 @@ -/* - Spacebar: A FOSS re-implementation and extension of the Discord.com backend. - Copyright (C) 2023 Spacebar and Spacebar Contributors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -/* eslint-env node */ - -const register = require("./src/register"); -const login = require("./src/login/index"); -const config = require("./config.json"); -const figlet = require("figlet"); -const sendMessage = require("./src/message/send"); -const fs = require("fs"); -figlet("Spacebar Stress Test :)", function (err, data) { - if (err) { - console.log("Something went wrong..."); - console.dir(err); - return; - } - console.log("\x1b[32m", data); -}); -setInterval(() => { - generate(); -}, 1000 * 5); -setInterval(() => { - getUsers(); -}, 60 * 1000); -async function generate() { - var accounts = await JSON.parse(fs.readFileSync("accounts.json")); - console.log(accounts); - var account = await register(); - accounts.push(account); - fs.writeFileSync("accounts.json", JSON.stringify(accounts)); - console.log(accounts.length); - var y = await login(account); - sendMessage(y); -} -async function getUsers() { - var accounts = await JSON.parse(fs.readFileSync("accounts.json")); - accounts.forEach(async (x) => { - var y = await login(x); - console.log(y); - sendMessage(y); - }); -} diff --git a/scripts/stresstest/package-lock.json b/scripts/stresstest/package-lock.json deleted file mode 100644 index 81c9b817abcc9af86421cec78987cefed923e50b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29271 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 L9FY0xA3yys%|TJC diff --git a/scripts/stresstest/package.json b/scripts/stresstest/package.json deleted file mode 100644 index 8d94d05b..00000000 --- a/scripts/stresstest/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "stresstest", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "node ." - }, - "author": "", - "license": "ISC", - "dependencies": { - "figlet": "^1.5.2", - "node-fetch": "^2.6.6", - "request": "^2.88.2" - } -} diff --git a/scripts/stresstest/src/login/index.js b/scripts/stresstest/src/login/index.js deleted file mode 100644 index 128a477b..00000000 --- a/scripts/stresstest/src/login/index.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - Spacebar: A FOSS re-implementation and extension of the Discord.com backend. - Copyright (C) 2023 Spacebar and Spacebar Contributors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -const fetch = require("node-fetch"); -const fs = require("fs"); -var config = require("../../config.json"); -module.exports = login; -async function login(account) { - var body = { - fingerprint: "805826570869932034.wR8vi8lGlFBJerErO9LG5NViJFw", - login: account.email, - password: account.password, - }; - var x = await fetch(config.url + "/auth/login", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(body), - }); - console.log(x); - x = await x.json(); - console.log(x); - return x; -} diff --git a/scripts/stresstest/src/message/send.js b/scripts/stresstest/src/message/send.js deleted file mode 100644 index ca6af62c..00000000 --- a/scripts/stresstest/src/message/send.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - Spacebar: A FOSS re-implementation and extension of the Discord.com backend. - Copyright (C) 2023 Spacebar and Spacebar Contributors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -const fetch = require("node-fetch"); -const fs = require("fs"); -var config = require("../../config.json"); -module.exports = sendMessage; -async function sendMessage(account) { - var body = { - fingerprint: "805826570869932034.wR8vi8lGlFBJerErO9LG5NViJFw", - content: "Test", - tts: false, - }; - var x = await fetch( - config.url + "/channels/" + config["text-channel"] + "/messages", - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: account.token, - }, - body: JSON.stringify(body), - }, - ); - console.log(x); - x = await x.json(); - console.log(x); - return x; -} diff --git a/scripts/stresstest/src/register/index.js b/scripts/stresstest/src/register/index.js deleted file mode 100644 index f085386f..00000000 --- a/scripts/stresstest/src/register/index.js +++ /dev/null @@ -1,58 +0,0 @@ -/* - Spacebar: A FOSS re-implementation and extension of the Discord.com backend. - Copyright (C) 2023 Spacebar and Spacebar Contributors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -/* eslint-env node */ - -const fetch = require("node-fetch"); -const fs = require("fs"); -var config = require("../../config.json"); -module.exports = generate; -async function generate() { - var mail = (Math.random() + 10).toString(36).substring(2); - mail = - mail + - "." + - (Math.random() + 10).toString(36).substring(2) + - "@stresstest.com"; - var password = - (Math.random() * 69).toString(36).substring(-7) + - (Math.random() * 69).toString(36).substring(-7) + - (Math.random() * 69).toString(36).substring(-8); - console.log(mail); - console.log(password); - var body = { - fingerprint: "805826570869932034.wR8vi8lGlFBJerErO9LG5NViJFw", - email: mail, - username: "Spacebar Stress Test", - password: password, - invite: config.invite, - consent: true, - date_of_birth: "2000-04-04", - gift_code_sku_id: null, - captcha_key: null, - }; - var x = await fetch(config.url + "/auth/register", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(body), - }); - console.log(x); - x = await x.json(); - console.log(x); - return { email: mail, password: password }; -} From f1f68c3d314c1b8bf42641a165ef6c9a8ebd348e Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Fri, 28 Jul 2023 09:26:18 +1000 Subject: [PATCH 12/17] refactor checkToken --- assets/openapi.json | Bin 571228 -> 572156 bytes assets/schemas.json | Bin 18199664 -> 18293788 bytes src/api/middlewares/Authentication.ts | 7 +- src/api/routes/auth/reset.ts | 4 +- src/api/routes/auth/verify/index.ts | 3 +- src/gateway/opcodes/Identify.ts | 62 +++++-------- src/util/schemas/RegisterSchema.ts | 4 + src/util/util/Token.ts | 120 ++++++++++---------------- 8 files changed, 75 insertions(+), 125 deletions(-) diff --git a/assets/openapi.json b/assets/openapi.json index 9d4f0a1ecd58b826406fa36aa3ea4c3052f20449..cf262c2cdd77bcb3c247df8b22585a2e5046f4ae 100644 GIT binary patch delta 759 zcmb7>T}YE*6vuha^S-Z}jp>L=#gDbt$L6w}n5apwdZ84FH^mZdZn{lT)A>Px7p^WU zglzd}M~Y|>7*@#A>LDz;DkzQVMHflrFtaZDc%g(!skTW7-F0!E!~e(O9RC03^=HG% zlEHVEgo{xbhfJ1uDi_1WWF)F2rmfh7${3MQf0*R)rzPah6|A(8hfI6b$ZFT`#lUys zWcooX1`d#&Z1<3A{K(nsfI&B{#zVi!Y1USn!nZ7vJPG}mp#x=&REYkdn1$zo=5TqQ zyd=2qJ#FG|7Dy>U`waE4=iRf+HxP$?PVl09i?~p(ic{k{H#o4u0aGm4(}JOH5Sh=H z6}yc35-M8LML)8lVUhETG1BW+=cB z18isdu`>K(iv;)e!bY4fpaRaBBdXJ)!^$$6flU_B@*XpMD#TG6$wuWTl`&WiN!a2B zJCjo-Ugd$?4e0wolQ%SayIl6J4p;jzM~lne?(#Htbhe|epJrm<63LD8G`Bh(&2~IF z1AYA9G^7~&onF@SLcH;8320=S8r#0X0$27E4Z*-HY~{;8U{}QZIM`SJC~PG;7|@Wq zNJM+M9Hg%VmO7Ci3zaLwbcgCch7IEV2|Azf_ppUkX7{3g2x|DRN%|x|#$`$HvP8_l z#CS+zzOo_=Ow!G3s=`XPMU^%Bs9Erl>0-U0M*RuMK>rjqMzJnxKW&Yd=aATjf4N>G Vh-X47AyUyUMJkSmRH}2Me*jyR9)}Jk<7IK9 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 { const { password, token } = req.body as PasswordResetSchema; - const { jwtSecret } = Config.get().security; - let user; try { - const userTokenData = await checkToken(token, jwtSecret, true); + const userTokenData = await checkToken(token); user = userTokenData.user; } catch { throw FieldErrors({ diff --git a/src/api/routes/auth/verify/index.ts b/src/api/routes/auth/verify/index.ts index a98c17fa..49f74277 100644 --- a/src/api/routes/auth/verify/index.ts +++ b/src/api/routes/auth/verify/index.ts @@ -78,11 +78,10 @@ router.post( } } - const { jwtSecret } = Config.get().security; let user; try { - const userTokenData = await checkToken(token, jwtSecret, true); + const userTokenData = await checkToken(token); user = userTokenData.user; } catch { throw FieldErrors({ diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts index 5816c308..837ae351 100644 --- a/src/gateway/opcodes/Identify.ts +++ b/src/gateway/opcodes/Identify.ts @@ -30,7 +30,6 @@ import { Intents, Member, ReadyEventData, - User, Session, EVENTEnum, Config, @@ -60,17 +59,6 @@ import { check } from "./instanceOf"; // TODO: user sharding // TODO: check privileged intents, if defined in the config -const getUserFromToken = async (token: string): Promise => { - try { - const { jwtSecret } = Config.get().security; - const { decoded } = await checkToken(token, jwtSecret); - return decoded.id; - } catch (e) { - console.error(`[Gateway] Invalid token`, e); - return null; - } -}; - export async function onIdentify(this: WebSocket, data: Payload) { if (this.user_id) { // we've already identified @@ -85,12 +73,12 @@ export async function onIdentify(this: WebSocket, data: Payload) { this.capabilities = new Capabilities(identify.capabilities || 0); - // Check auth - // TODO: the checkToken call will fetch user, and then we have to refetch with different select - // checkToken should be able to select what we want - const user_id = await getUserFromToken(identify.token); - if (!user_id) return this.close(CLOSECODES.Authentication_failed); - this.user_id = user_id; + const { user } = await checkToken(identify.token, { + relations: ["relationships", "relationships.to", "settings"], + select: [...PrivateUserProjection, "relationships"], + }); + if (!user) return this.close(CLOSECODES.Authentication_failed); + this.user_id = user.id; // Check intents if (!identify.intents) identify.intents = 30064771071n; // TODO: what is this number? @@ -112,7 +100,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { ) { // TODO: why do we even care about this right now? console.log( - `[Gateway] Invalid sharding from ${user_id}: ${identify.shard}`, + `[Gateway] Invalid sharding from ${user.id}: ${identify.shard}`, ); return this.close(CLOSECODES.Invalid_shard); } @@ -132,22 +120,14 @@ export async function onIdentify(this: WebSocket, data: Payload) { }); // Get from database: - // * the current user, // * the users read states // * guild members for this user // * recipients ( dm channels ) // * the bot application, if it exists - const [, user, application, read_states, members, recipients] = - await Promise.all([ + const [, application, read_states, members, recipients] = await Promise.all( + [ session.save(), - // TODO: Refactor checkToken to allow us to skip this additional query - User.findOneOrFail({ - where: { id: this.user_id }, - relations: ["relationships", "relationships.to", "settings"], - select: [...PrivateUserProjection, "relationships"], - }), - Application.findOne({ where: { id: this.user_id }, select: ["id", "flags"], @@ -224,7 +204,8 @@ export async function onIdentify(this: WebSocket, data: Payload) { }, }, }), - ]); + ], + ); // We forgot to migrate user settings from the JSON column of `users` // to the `user_settings` table theyre in now, @@ -407,6 +388,17 @@ 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", + + // lol hack whatever + required_action: + Config.get().login.requireVerification && !user.verified + ? "REQUIRE_VERIFIED_EMAIL" + : undefined, + consents: { personalization: { consented: false, // TODO @@ -421,18 +413,8 @@ export async function onIdentify(this: WebSocket, data: Payload) { friend_suggestion_count: 0, analytics_token: "", tutorial: null, - resume_gateway_url: - Config.get().gateway.endpointClient || - Config.get().gateway.endpointPublic || - "ws://127.0.0.1:3001", session_type: "normal", // TODO auth_session_id_hash: "", // TODO - - // lol hack whatever - required_action: - Config.get().login.requireVerification && !user.verified - ? "REQUIRE_VERIFIED_EMAIL" - : undefined, }; // Send READY diff --git a/src/util/schemas/RegisterSchema.ts b/src/util/schemas/RegisterSchema.ts index f6c99b18..7b7de9c7 100644 --- a/src/util/schemas/RegisterSchema.ts +++ b/src/util/schemas/RegisterSchema.ts @@ -42,4 +42,8 @@ export interface RegisterSchema { captcha_key?: string; promotional_email_opt_in?: boolean; + + // part of pomelo + unique_username_registration?: boolean; + global_name?: string; } diff --git a/src/util/util/Token.ts b/src/util/util/Token.ts index 90310176..eec72522 100644 --- a/src/util/util/Token.ts +++ b/src/util/util/Token.ts @@ -19,94 +19,66 @@ import jwt, { VerifyOptions } from "jsonwebtoken"; import { Config } from "./Config"; import { User } from "../entities"; +// TODO: dont use deprecated APIs lol +import { + FindOptionsRelationByString, + FindOptionsSelectByString, +} from "typeorm"; export const JWTOptions: VerifyOptions = { algorithms: ["HS256"] }; export type UserTokenData = { user: User; - decoded: { id: string; iat: number }; + decoded: { id: string; iat: number; email?: string }; }; -async function checkEmailToken( - decoded: jwt.JwtPayload, -): Promise { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (res, rej) => { - if (!decoded.iat) return rej("Invalid Token"); // will never happen, just for typings. - - const user = await User.findOne({ - where: { - email: decoded.email, - }, - select: [ - "email", - "id", - "verified", - "deleted", - "disabled", - "username", - "data", - ], - }); - - if (!user) return rej("Invalid Token"); - - if (new Date().getTime() > decoded.iat * 1000 + 86400 * 1000) - return rej("Invalid Token"); - - // Using as here because we assert `id` and `iat` are in decoded. - // TS just doesn't want to assume its there, though. - return res({ decoded, user } as UserTokenData); - }); -} - -export function checkToken( +export const checkToken = ( token: string, - jwtSecret: string, - isEmailVerification = false, -): Promise { - return new Promise((res, rej) => { - token = token.replace("Bot ", ""); - token = token.replace("Bearer ", ""); - /** - in spacebar, even with instances that have bot distinction; we won't enforce "Bot" prefix, - as we don't really have separate pathways for bots - **/ + opts?: { + select?: FindOptionsSelectByString; + relations?: FindOptionsRelationByString; + }, +): Promise => + new Promise((resolve, reject) => { + jwt.verify( + token, + Config.get().security.jwtSecret, + JWTOptions, + async (err, out) => { + const decoded = out as UserTokenData["decoded"]; + if (err || !decoded) return reject("Invalid Token"); - jwt.verify(token, jwtSecret, JWTOptions, async (err, decoded) => { - if (err || !decoded) return rej("Invalid Token"); - if ( - typeof decoded == "string" || - !("id" in decoded) || - !decoded.iat - ) - return rej("Invalid Token"); // will never happen, just for typings. + const user = await User.findOne({ + where: decoded.email + ? { email: decoded.email } + : { id: decoded.id }, + select: [ + ...(opts?.select || []), + "bot", + "disabled", + "deleted", + "rights", + "data", + ], + relations: opts?.relations, + }); - if (isEmailVerification) return res(checkEmailToken(decoded)); + if (!user) return reject("User not found"); - const user = await User.findOne({ - where: { id: decoded.id }, - select: ["data", "bot", "disabled", "deleted", "rights"], - }); + // we need to round it to seconds as it saved as seconds in jwt iat and valid_tokens_since is stored in milliseconds + if ( + decoded.iat * 1000 < + new Date(user.data.valid_tokens_since).setSeconds(0, 0) + ) + return reject("Invalid Token"); - if (!user) return rej("Invalid Token"); + if (user.disabled) return reject("User disabled"); + if (user.deleted) return reject("User not found"); - // we need to round it to seconds as it saved as seconds in jwt iat and valid_tokens_since is stored in milliseconds - if ( - decoded.iat * 1000 < - new Date(user.data.valid_tokens_since).setSeconds(0, 0) - ) - return rej("Invalid Token"); - - if (user.disabled) return rej("User disabled"); - if (user.deleted) return rej("User not found"); - - // Using as here because we assert `id` and `iat` are in decoded. - // TS just doesn't want to assume its there, though. - return res({ decoded, user } as UserTokenData); - }); + return resolve({ decoded, user }); + }, + ); }); -} export async function generateToken(id: string, email?: string) { const iat = Math.floor(Date.now() / 1000); From a0d93fb252803c5fded8723d092ae0f394d1b40b Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Sat, 29 Jul 2023 16:59:21 +1000 Subject: [PATCH 13/17] * call toJSON of keys in gateway when using erlpack * dont send bitrate/etc as null when should be undefined * set user flags to number instead of string * send empty 'threads' in identify when not using new state v2 --- src/api/routes/guilds/#guild_id/index.ts | 2 +- src/gateway/opcodes/Identify.ts | 2 ++ src/util/dtos/ReadyGuildDTO.ts | 4 ++-- src/util/entities/Channel.ts | 12 ++++++++++++ src/util/entities/Guild.ts | 7 +++++++ src/util/entities/User.ts | 2 +- src/util/util/JSON.ts | 10 ++++++++++ 7 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/api/routes/guilds/#guild_id/index.ts b/src/api/routes/guilds/#guild_id/index.ts index afe60614..86777b36 100644 --- a/src/api/routes/guilds/#guild_id/index.ts +++ b/src/api/routes/guilds/#guild_id/index.ts @@ -161,7 +161,7 @@ router.patch( const data = guild.toJSON(); // TODO: guild hashes // TODO: fix vanity_url_code, template_id - delete data.vanity_url_code; + // delete data.vanity_url_code; delete data.template_id; await Promise.all([ diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts index 837ae351..0f91df3e 100644 --- a/src/gateway/opcodes/Identify.ts +++ b/src/gateway/opcodes/Identify.ts @@ -265,6 +265,8 @@ export async function onIdentify(this: WebSocket, data: Payload) { return { ...member.guild.toJSON(), joined_at: member.joined_at, + + threads: [], }; }); diff --git a/src/util/dtos/ReadyGuildDTO.ts b/src/util/dtos/ReadyGuildDTO.ts index 7ca268a0..905ede74 100644 --- a/src/util/dtos/ReadyGuildDTO.ts +++ b/src/util/dtos/ReadyGuildDTO.ts @@ -49,12 +49,12 @@ export interface ReadyPrivateChannel { export type GuildOrUnavailable = | { id: string; unavailable: boolean } - | (Guild & { joined_at?: Date; unavailable: boolean }); + | (Guild & { joined_at?: Date; unavailable: undefined }); const guildIsAvailable = ( guild: GuildOrUnavailable, ): guild is Guild & { joined_at: Date; unavailable: false } => { - return guild.unavailable == false; + return guild.unavailable != true; }; export interface IReadyGuildDTO { diff --git a/src/util/entities/Channel.ts b/src/util/entities/Channel.ts index 19a7a41a..38627c39 100644 --- a/src/util/entities/Channel.ts +++ b/src/util/entities/Channel.ts @@ -468,6 +468,18 @@ export class Channel extends BaseClass { ]; return disallowedChannelTypes.indexOf(this.type) == -1; } + + toJSON() { + return { + ...this, + + // these fields are not returned depending on the type of channel + bitrate: this.bitrate || undefined, + user_limit: this.user_limit || undefined, + rate_limit_per_user: this.rate_limit_per_user || undefined, + owner_id: this.owner_id || undefined, + }; + } } export interface ChannelPermissionOverwrite { diff --git a/src/util/entities/Guild.ts b/src/util/entities/Guild.ts index 4c2949a3..e364ed98 100644 --- a/src/util/entities/Guild.ts +++ b/src/util/entities/Guild.ts @@ -390,4 +390,11 @@ export class Guild extends BaseClass { return guild; } + + toJSON() { + return { + ...this, + unavailable: this.unavailable == false ? undefined : true, + }; + } } diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts index 68d7b5e8..3f1bda05 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts @@ -175,7 +175,7 @@ export class User extends BaseClass { email?: string; // email of the user @Column() - flags: string = "0"; // UserFlags // TODO: generate + flags: number = 0; // UserFlags // TODO: generate @Column() public_flags: number = 0; diff --git a/src/util/util/JSON.ts b/src/util/util/JSON.ts index 1c39b66e..c7dcf47e 100644 --- a/src/util/util/JSON.ts +++ b/src/util/util/JSON.ts @@ -27,6 +27,16 @@ const JSONReplacer = function ( return (this[key] as Date).toISOString().replace("Z", "+00:00"); } + // erlpack encoding doesn't call json.stringify, + // so our toJSON functions don't get called. + // manually call it here + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + if (this?.[key]?.toJSON) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + this[key] = this[key].toJSON(); + return value; }; From 4c0b1391b19cd55127350aa8605362e2799d5407 Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Mon, 31 Jul 2023 19:42:27 +1000 Subject: [PATCH 14/17] send empty READY_SUPPLEMENTAL for client compatibility --- src/gateway/opcodes/Identify.ts | 21 ++++++++++++++++++++- src/util/interfaces/Event.ts | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts index 0f91df3e..bab52279 100644 --- a/src/gateway/opcodes/Identify.ts +++ b/src/gateway/opcodes/Identify.ts @@ -53,6 +53,7 @@ import { DMChannel, GuildOrUnavailable, Recipient, + OPCodes, } from "@spacebar/util"; import { check } from "./instanceOf"; @@ -441,7 +442,25 @@ export async function onIdentify(this: WebSocket, data: Payload) { ), ); - //TODO send READY_SUPPLEMENTAL + // TODO: ready supplemental + await Send(this, { + op: OPCodes.DISPATCH, + t: EVENTEnum.ReadySupplemental, + s: this.sequence++, + d: { + merged_presences: { + guilds: [], + friends: [], + }, + // these merged members seem to be all users currently in vc in your guilds + merged_members: [], + lazy_private_channels: [], + guilds: [], // { voice_states: [], id: string, embedded_activities: [] } + // embedded_activities are users currently in an activity? + disclose: ["pomelo"], + }, + }); + //TODO send GUILD_MEMBER_LIST_UPDATE //TODO send VOICE_STATE_UPDATE to let the client know if another device is already connected to a voice channel diff --git a/src/util/interfaces/Event.ts b/src/util/interfaces/Event.ts index 7ddb763d..deb54428 100644 --- a/src/util/interfaces/Event.ts +++ b/src/util/interfaces/Event.ts @@ -583,6 +583,7 @@ export type EventData = export enum EVENTEnum { Ready = "READY", + ReadySupplemental = "READY_SUPPLEMENTAL", ChannelCreate = "CHANNEL_CREATE", ChannelUpdate = "CHANNEL_UPDATE", ChannelDelete = "CHANNEL_DELETE", From 45e7d763cf149e582d4394880a8788fe689064a6 Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Mon, 31 Jul 2023 20:59:06 +1000 Subject: [PATCH 15/17] lazyrequest dont crash when no members can be found in lazyrequest --- src/gateway/opcodes/LazyRequest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gateway/opcodes/LazyRequest.ts b/src/gateway/opcodes/LazyRequest.ts index 77e1a25a..4ad1ae7b 100644 --- a/src/gateway/opcodes/LazyRequest.ts +++ b/src/gateway/opcodes/LazyRequest.ts @@ -95,7 +95,7 @@ async function getMembers(guild_id: string, range: [number, number]) { console.error(`LazyRequest`, e); } - if (!members) { + if (!members || !members.length) { return { items: [], groups: [], From 496cfc8ea19ac430cfeaf9d7b8d3b3bc6843b852 Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Mon, 31 Jul 2023 21:00:03 +1000 Subject: [PATCH 16/17] some abaddon compat --- .../channels/#channel_id/messages/index.ts | 70 ++++++++++--------- src/util/entities/Message.ts | 26 ++++++- src/util/schemas/MessageCreateSchema.ts | 2 +- 3 files changed, 63 insertions(+), 35 deletions(-) diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts index 75fb7b83..edc0321c 100644 --- a/src/api/routes/channels/#channel_id/messages/index.ts +++ b/src/api/routes/channels/#channel_id/messages/index.ts @@ -144,44 +144,46 @@ router.get( const endpoint = Config.get().cdn.endpointPublic; - return res.json( - messages.map((x: Partial) => { - (x.reactions || []).forEach((y: Partial) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - if ((y.user_ids || []).includes(req.user_id)) y.me = true; - delete y.user_ids; - }); - if (!x.author) - x.author = User.create({ - id: "4", - discriminator: "0000", - username: "Spacebar Ghost", - public_flags: 0, - }); - x.attachments?.forEach((y: Attachment) => { - // dynamically set attachment proxy_url in case the endpoint changed - const uri = y.proxy_url.startsWith("http") - ? y.proxy_url - : `https://example.org${y.proxy_url}`; - y.proxy_url = `${endpoint == null ? "" : endpoint}${ - new URL(uri).pathname - }`; - }); + const ret = messages.map((x: Message) => { + x = x.toJSON(); - /** + (x.reactions || []).forEach((y: Partial) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + if ((y.user_ids || []).includes(req.user_id)) y.me = true; + delete y.user_ids; + }); + if (!x.author) + x.author = User.create({ + id: "4", + discriminator: "0000", + username: "Spacebar Ghost", + public_flags: 0, + }); + x.attachments?.forEach((y: Attachment) => { + // dynamically set attachment proxy_url in case the endpoint changed + const uri = y.proxy_url.startsWith("http") + ? y.proxy_url + : `https://example.org${y.proxy_url}`; + y.proxy_url = `${endpoint == null ? "" : endpoint}${ + new URL(uri).pathname + }`; + }); + + /** Some clients ( discord.js ) only check if a property exists within the response, which causes errors when, say, the `application` property is `null`. **/ - // for (var curr in x) { - // if (x[curr] === null) - // delete x[curr]; - // } + // for (var curr in x) { + // if (x[curr] === null) + // delete x[curr]; + // } - return x; - }), - ); + return x; + }); + + return res.json(ret); }, ); @@ -307,9 +309,11 @@ router.post( embeds, channel_id, attachments, - edited_timestamp: undefined, timestamp: new Date(), }); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore dont care2 + message.edited_timestamp = null; channel.last_message_id = message.id; diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index 519c431e..e5390300 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts @@ -193,7 +193,7 @@ export class Message extends BaseClass { }; @Column({ nullable: true }) - flags?: string; + flags?: number; @Column({ type: "simple-json", nullable: true }) message_reference?: { @@ -217,6 +217,30 @@ export class Message extends BaseClass { @Column({ type: "simple-json", nullable: true }) components?: MessageComponent[]; + + toJSON(): Message { + return { + ...this, + author_id: undefined, + member_id: undefined, + guild_id: undefined, + webhook_id: undefined, + application_id: undefined, + nonce: undefined, + + tts: this.tts ?? false, + guild: this.guild ?? undefined, + webhook: this.webhook ?? undefined, + interaction: this.interaction ?? undefined, + reactions: this.reactions ?? undefined, + sticker_items: this.sticker_items ?? undefined, + message_reference: this.message_reference ?? undefined, + author: this.author?.toPublicUser() ?? undefined, + activity: this.activity ?? undefined, + application: this.application ?? undefined, + components: this.components ?? undefined, + }; + } } export interface MessageComponent { diff --git a/src/util/schemas/MessageCreateSchema.ts b/src/util/schemas/MessageCreateSchema.ts index 45cd735e..7e130751 100644 --- a/src/util/schemas/MessageCreateSchema.ts +++ b/src/util/schemas/MessageCreateSchema.ts @@ -29,7 +29,7 @@ export interface MessageCreateSchema { nonce?: string; channel_id?: string; tts?: boolean; - flags?: string; + flags?: number; embeds?: Embed[]; embed?: Embed; // TODO: ^ embed is deprecated in favor of embeds (https://discord.com/developers/docs/resources/channel#message-object) From 53aed186526bad8a92393f5506df3f20a2470a80 Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Tue, 1 Aug 2023 20:23:00 +1000 Subject: [PATCH 17/17] remove pomelo disclose, to be added in pomelo impl pr --- src/gateway/opcodes/Identify.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts index bab52279..7610901a 100644 --- a/src/gateway/opcodes/Identify.ts +++ b/src/gateway/opcodes/Identify.ts @@ -457,7 +457,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { lazy_private_channels: [], guilds: [], // { voice_states: [], id: string, embedded_activities: [] } // embedded_activities are users currently in an activity? - disclose: ["pomelo"], + disclose: [], // Config.get().general.uniqueUsernames ? ["pomelo"] : [] }, });