summary refs log tree commit diff
path: root/src/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/util')
-rw-r--r--src/util/config/types/GeneralConfiguration.ts1
-rw-r--r--src/util/config/types/SecurityConfiguration.ts2
-rw-r--r--src/util/config/types/subconfigurations/limits/RateLimits.ts4
-rw-r--r--src/util/connections/Connection.ts2
-rw-r--r--src/util/connections/ConnectionLoader.ts8
-rw-r--r--src/util/connections/ConnectionStore.ts4
-rw-r--r--src/util/connections/RefreshableConnection.ts5
-rw-r--r--src/util/dtos/ConnectedAccountDTO.ts2
-rw-r--r--src/util/dtos/ReadyGuildDTO.ts70
-rw-r--r--src/util/entities/Channel.ts68
-rw-r--r--src/util/entities/ConnectedAccount.ts1
-rw-r--r--src/util/entities/Guild.ts52
-rw-r--r--src/util/entities/Member.ts10
-rw-r--r--src/util/entities/Message.ts25
-rw-r--r--src/util/entities/Role.ts3
-rw-r--r--src/util/entities/User.ts35
-rw-r--r--src/util/interfaces/Activity.ts2
-rw-r--r--src/util/interfaces/Event.ts37
-rw-r--r--src/util/interfaces/GuildWelcomeScreen.ts10
-rw-r--r--src/util/interfaces/index.ts1
-rw-r--r--src/util/schemas/AckBulkSchema.ts12
-rw-r--r--src/util/schemas/ConnectedAccountSchema.ts2
-rw-r--r--src/util/schemas/ConnectionCallbackSchema.ts2
-rw-r--r--src/util/schemas/IdentifySchema.ts10
-rw-r--r--src/util/schemas/LazyRequestSchema.ts9
-rw-r--r--src/util/schemas/LoginResponse.ts14
-rw-r--r--src/util/schemas/MemberChangeProfileSchema.ts3
-rw-r--r--src/util/schemas/MessageCreateSchema.ts2
-rw-r--r--src/util/schemas/RegisterSchema.ts4
-rw-r--r--src/util/schemas/UserGuildSettingsSchema.ts4
-rw-r--r--src/util/schemas/UserNoteUpdateSchema.ts3
-rw-r--r--src/util/schemas/UserProfileModifySchema.ts3
-rw-r--r--src/util/schemas/UserProfileResponse.ts26
-rw-r--r--src/util/schemas/UserRelationsResponse.ts27
-rw-r--r--src/util/schemas/VoiceStateUpdateSchema.ts2
-rw-r--r--src/util/schemas/WebAuthnSchema.ts6
-rw-r--r--src/util/schemas/index.ts6
-rw-r--r--src/util/schemas/responses/APIErrorOrCaptchaResponse.ts6
-rw-r--r--src/util/schemas/responses/APIErrorResponse.ts12
-rw-r--r--src/util/schemas/responses/BackupCodesChallengeResponse.ts4
-rw-r--r--src/util/schemas/responses/CaptchaRequiredResponse.ts5
-rw-r--r--src/util/schemas/responses/DiscoverableGuildsResponse.ts8
-rw-r--r--src/util/schemas/responses/GatewayBotResponse.ts10
-rw-r--r--src/util/schemas/responses/GatewayResponse.ts3
-rw-r--r--src/util/schemas/responses/GenerateRegistrationTokensResponse.ts3
-rw-r--r--src/util/schemas/responses/GuildBansResponse.ts10
-rw-r--r--src/util/schemas/responses/GuildCreateResponse.ts3
-rw-r--r--src/util/schemas/responses/GuildDiscoveryRequirements.ts23
-rw-r--r--src/util/schemas/responses/GuildMessagesSearchResponse.ts32
-rw-r--r--src/util/schemas/responses/GuildPruneResponse.ts7
-rw-r--r--src/util/schemas/responses/GuildRecommendationsResponse.ts6
-rw-r--r--src/util/schemas/responses/GuildVanityUrl.ts17
-rw-r--r--src/util/schemas/responses/GuildVoiceRegionsResponse.ts7
-rw-r--r--src/util/schemas/responses/GuildWidgetJsonResponse.ts21
-rw-r--r--src/util/schemas/responses/GuildWidgetSettingsResponse.ts6
-rw-r--r--src/util/schemas/responses/InstanceDomainsResponse.ts6
-rw-r--r--src/util/schemas/responses/InstancePingResponse.ts13
-rw-r--r--src/util/schemas/responses/InstanceStatsResponse.ts8
-rw-r--r--src/util/schemas/responses/LocationMetadataResponse.ts5
-rw-r--r--src/util/schemas/responses/MemberJoinGuildResponse.ts8
-rw-r--r--src/util/schemas/responses/OAuthAuthorizeResponse.ts3
-rw-r--r--src/util/schemas/responses/Tenor.ts72
-rw-r--r--src/util/schemas/responses/TokenResponse.ts15
-rw-r--r--src/util/schemas/responses/TypedResponses.ts88
-rw-r--r--src/util/schemas/responses/UpdatesResponse.ts6
-rw-r--r--src/util/schemas/responses/UserNoteResponse.ts5
-rw-r--r--src/util/schemas/responses/UserProfileResponse.ts37
-rw-r--r--src/util/schemas/responses/UserRelationsResponse.ts7
-rw-r--r--src/util/schemas/responses/UserRelationshipsResponse.ts8
-rw-r--r--src/util/schemas/responses/WebAuthnCreateResponse.ts4
-rw-r--r--src/util/schemas/responses/WebhookCreateResponse.ts6
-rw-r--r--src/util/schemas/responses/index.ts34
-rw-r--r--src/util/util/Application.ts24
-rw-r--r--src/util/util/AutoUpdate.ts2
-rw-r--r--src/util/util/Gifs.ts25
-rw-r--r--src/util/util/JSON.ts10
-rw-r--r--src/util/util/Token.ts133
-rw-r--r--src/util/util/index.ts2
78 files changed, 939 insertions, 242 deletions
diff --git a/src/util/config/types/GeneralConfiguration.ts b/src/util/config/types/GeneralConfiguration.ts

index c20fe9a7..cff8c527 100644 --- a/src/util/config/types/GeneralConfiguration.ts +++ b/src/util/config/types/GeneralConfiguration.ts
@@ -28,4 +28,5 @@ export class GeneralConfiguration { correspondenceUserID: string | null = null; image: string | null = null; instanceId: string = Snowflake.generate(); + autoCreateBotUsers: boolean = false; } diff --git a/src/util/config/types/SecurityConfiguration.ts b/src/util/config/types/SecurityConfiguration.ts
index 5e971cfe..35776642 100644 --- a/src/util/config/types/SecurityConfiguration.ts +++ b/src/util/config/types/SecurityConfiguration.ts
@@ -28,7 +28,7 @@ export class SecurityConfiguration { // header to get the real user ip address // X-Forwarded-For for nginx/reverse proxies // CF-Connecting-IP for cloudflare - forwadedFor: string | null = null; + forwardedFor: string | null = null; ipdataApiKey: string | null = "eca677b284b3bac29eb72f5e496aa9047f26543605efe99ff2ce35c9"; mfaBackupCodeCount: number = 10; diff --git a/src/util/config/types/subconfigurations/limits/RateLimits.ts b/src/util/config/types/subconfigurations/limits/RateLimits.ts
index caba740b..0ce0827c 100644 --- a/src/util/config/types/subconfigurations/limits/RateLimits.ts +++ b/src/util/config/types/subconfigurations/limits/RateLimits.ts
@@ -16,11 +16,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { RouteRateLimit, RateLimitOptions } from "."; +import { RateLimitOptions, RouteRateLimit } from "."; export class RateLimits { enabled: boolean = false; - ip: Omit<RateLimitOptions, "bot_count"> = { + ip: RateLimitOptions = { count: 500, window: 5, }; diff --git a/src/util/connections/Connection.ts b/src/util/connections/Connection.ts
index becee589..5bdebd47 100644 --- a/src/util/connections/Connection.ts +++ b/src/util/connections/Connection.ts
@@ -24,7 +24,7 @@ import { Config, DiscordApiErrors } from "../util"; /** * A connection that can be used to connect to an external service. */ -export default abstract class Connection { +export abstract class Connection { id: string; settings: { enabled: boolean }; states: Map<string, string> = new Map(); diff --git a/src/util/connections/ConnectionLoader.ts b/src/util/connections/ConnectionLoader.ts
index 28f1a202..e9dc6973 100644 --- a/src/util/connections/ConnectionLoader.ts +++ b/src/util/connections/ConnectionLoader.ts
@@ -16,9 +16,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ +import { Connection } from "@spacebar/util"; import fs from "fs"; import path from "path"; -import Connection from "./Connection"; import { ConnectionConfig } from "./ConnectionConfig"; import { ConnectionStore } from "./ConnectionStore"; @@ -48,8 +48,7 @@ export class ConnectionLoader { }); } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public static getConnectionConfig(id: string, defaults?: any): any { + public static getConnectionConfig<T>(id: string, defaults?: unknown): T { let cfg = ConnectionConfig.get()[id]; if (defaults) { if (cfg) cfg = Object.assign({}, defaults, cfg); @@ -70,8 +69,7 @@ export class ConnectionLoader { public static async setConnectionConfig( id: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - config: Partial<any>, + config: Partial<unknown>, ): Promise<void> { if (!config) console.warn(`[Connections/WARN] ${id} tried to set config=null!`); diff --git a/src/util/connections/ConnectionStore.ts b/src/util/connections/ConnectionStore.ts
index 39abfea6..95e54fd9 100644 --- a/src/util/connections/ConnectionStore.ts +++ b/src/util/connections/ConnectionStore.ts
@@ -16,8 +16,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import Connection from "./Connection"; -import RefreshableConnection from "./RefreshableConnection"; +import { Connection } from "./Connection"; +import { RefreshableConnection } from "./RefreshableConnection"; export class ConnectionStore { public static connections: Map<string, Connection | RefreshableConnection> = diff --git a/src/util/connections/RefreshableConnection.ts b/src/util/connections/RefreshableConnection.ts
index fd93adfa..88ad8dab 100644 --- a/src/util/connections/RefreshableConnection.ts +++ b/src/util/connections/RefreshableConnection.ts
@@ -18,13 +18,14 @@ import { ConnectedAccount } from "../entities"; import { ConnectedAccountCommonOAuthTokenResponse } from "../interfaces"; -import Connection from "./Connection"; +import { Connection } from "./Connection"; /** * A connection that can refresh its token. */ -export default abstract class RefreshableConnection extends Connection { +export abstract class RefreshableConnection extends Connection { refreshEnabled = true; + /** * Refreshes the token for a connected account. * @param connectedAccount The connected account to refresh diff --git a/src/util/dtos/ConnectedAccountDTO.ts b/src/util/dtos/ConnectedAccountDTO.ts
index 0a3604d5..f9efd980 100644 --- a/src/util/dtos/ConnectedAccountDTO.ts +++ b/src/util/dtos/ConnectedAccountDTO.ts
@@ -30,7 +30,7 @@ export class ConnectedAccountDTO { verified?: boolean; visibility?: number; integrations?: string[]; - metadata_?: any; + metadata_?: unknown; metadata_visibility?: number; two_way_link?: boolean; diff --git a/src/util/dtos/ReadyGuildDTO.ts b/src/util/dtos/ReadyGuildDTO.ts
index b21afe74..905ede74 100644 --- a/src/util/dtos/ReadyGuildDTO.ts +++ b/src/util/dtos/ReadyGuildDTO.ts
@@ -18,13 +18,45 @@ import { Channel, + ChannelOverride, + ChannelType, Emoji, Guild, - PublicMember, + PublicUser, Role, Sticker, + UserGuildSettings, + PublicMember, } 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: undefined }); + +const guildIsAvailable = ( + guild: GuildOrUnavailable, +): guild is Guild & { joined_at: Date; unavailable: false } => { + return guild.unavailable != true; +}; + export interface IReadyGuildDTO { application_command_counts?: { 1: number; 2: number; 3: number }; // ???????????? channels: Channel[]; @@ -65,12 +97,21 @@ 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[]; stickers: Sticker[]; threads: unknown[]; version: string; + guild_hashes: unknown; + unavailable: boolean; } export class ReadyGuildDTO implements IReadyGuildDTO { @@ -113,14 +154,30 @@ 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[]; 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, @@ -164,12 +221,21 @@ 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 = []; 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 9ce04848..19952bc2 100644 --- a/src/util/entities/Channel.ts +++ b/src/util/entities/Channel.ts
@@ -16,6 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ +import { HTTPError } from "lambert-server"; import { Column, Entity, @@ -24,26 +25,25 @@ import { OneToMany, RelationId, } from "typeorm"; -import { BaseClass } from "./BaseClass"; -import { Guild } from "./Guild"; -import { PublicUserProjection, User } from "./User"; -import { HTTPError } from "lambert-server"; +import { DmChannelDTO } from "../dtos"; +import { ChannelCreateEvent, ChannelRecipientRemoveEvent } from "../interfaces"; import { + InvisibleCharacters, + Snowflake, containsAll, emitEvent, getPermission, - Snowflake, trimSpecial, - InvisibleCharacters, } from "../util"; -import { ChannelCreateEvent, ChannelRecipientRemoveEvent } from "../interfaces"; -import { Recipient } from "./Recipient"; +import { BaseClass } from "./BaseClass"; +import { Guild } from "./Guild"; +import { Invite } from "./Invite"; import { Message } from "./Message"; import { ReadState } from "./ReadState"; -import { Invite } from "./Invite"; +import { Recipient } from "./Recipient"; +import { PublicUserProjection, User } from "./User"; import { VoiceState } from "./VoiceState"; import { Webhook } from "./Webhook"; -import { DmChannelDTO } from "../dtos"; export enum ChannelType { GUILD_TEXT = 0, // a text channel within a guild @@ -302,8 +302,10 @@ export class Channel extends BaseClass { : channel.position) || 0, }; + const ret = Channel.create(channel); + await Promise.all([ - Channel.create(channel).save(), + ret.save(), !opts?.skipEventEmit ? emitEvent({ event: "CHANNEL_CREATE", @@ -313,7 +315,7 @@ export class Channel extends BaseClass { : Promise.resolve(), ]); - return channel; + return ret; } static async createDMChannel( @@ -468,6 +470,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 { @@ -482,3 +496,33 @@ export enum ChannelPermissionOverwriteType { member = 1, group = 2, } + +export interface DMChannel extends Omit<Channel, "type" | "recipients"> { + type: ChannelType.DM | ChannelType.GROUP_DM; + recipients: Recipient[]; +} + +// TODO: probably more props +export function isTextChannel(type: ChannelType): boolean { + switch (type) { + case ChannelType.GUILD_STORE: + case ChannelType.GUILD_VOICE: + case ChannelType.GUILD_STAGE_VOICE: + case ChannelType.GUILD_CATEGORY: + case ChannelType.GUILD_FORUM: + case ChannelType.DIRECTORY: + throw new HTTPError("not a text channel", 400); + case ChannelType.DM: + case ChannelType.GROUP_DM: + case ChannelType.GUILD_NEWS: + case ChannelType.GUILD_NEWS_THREAD: + case ChannelType.GUILD_PUBLIC_THREAD: + case ChannelType.GUILD_PRIVATE_THREAD: + case ChannelType.GUILD_TEXT: + case ChannelType.ENCRYPTED: + case ChannelType.ENCRYPTED_THREAD: + return true; + default: + throw new HTTPError("unimplemented", 400); + } +} diff --git a/src/util/entities/ConnectedAccount.ts b/src/util/entities/ConnectedAccount.ts
index 5dd21250..6e089de1 100644 --- a/src/util/entities/ConnectedAccount.ts +++ b/src/util/entities/ConnectedAccount.ts
@@ -66,6 +66,7 @@ export class ConnectedAccount extends BaseClass { integrations?: string[] = []; @Column({ type: "simple-json", name: "metadata", nullable: true }) + // eslint-disable-next-line @typescript-eslint/no-explicit-any metadata_?: any; @Column() diff --git a/src/util/entities/Guild.ts b/src/util/entities/Guild.ts
index e8454986..e364ed98 100644 --- a/src/util/entities/Guild.ts +++ b/src/util/entities/Guild.ts
@@ -24,7 +24,7 @@ import { OneToMany, RelationId, } from "typeorm"; -import { Config, handleFile, Snowflake } from ".."; +import { Config, GuildWelcomeScreen, handleFile, Snowflake } from ".."; import { Ban } from "./Ban"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; @@ -77,7 +77,7 @@ export class Guild extends BaseClass { afk_channel?: Channel; @Column({ nullable: true }) - afk_timeout?: number = Config.get().defaults.guild.afkTimeout; + afk_timeout?: number; // * commented out -> use owner instead // application id of the guild creator if it is bot-created @@ -95,8 +95,7 @@ export class Guild extends BaseClass { banner?: string; @Column({ nullable: true }) - default_message_notifications?: number = - Config.get().defaults.guild.defaultMessageNotifications; + default_message_notifications?: number; @Column({ nullable: true }) description?: string; @@ -105,11 +104,10 @@ export class Guild extends BaseClass { discovery_splash?: string; @Column({ nullable: true }) - explicit_content_filter?: number = - Config.get().defaults.guild.explicitContentFilter; + explicit_content_filter?: number; @Column({ type: "simple-array" }) - features: string[] = Config.get().guild.defaultFeatures || []; //TODO use enum + features: string[] = []; //TODO use enum //TODO: https://discord.com/developers/docs/resources/guild#guild-object-guild-features @Column({ nullable: true }) @@ -122,14 +120,13 @@ export class Guild extends BaseClass { large?: boolean = false; @Column({ nullable: true }) - max_members?: number = Config.get().limits.guild.maxMembers; + max_members?: number; @Column({ nullable: true }) - max_presences?: number = Config.get().defaults.guild.maxPresences; + max_presences?: number; @Column({ nullable: true }) - max_video_channel_users?: number = - Config.get().defaults.guild.maxVideoChannelUsers; + max_video_channel_users?: number; @Column({ nullable: true }) member_count?: number; @@ -247,7 +244,7 @@ export class Guild extends BaseClass { rules_channel?: string; @Column({ nullable: true }) - region?: string = Config.get().regions.default; + region?: string; @Column({ nullable: true }) splash?: string; @@ -270,16 +267,7 @@ export class Guild extends BaseClass { verification_level?: number; @Column({ type: "simple-json" }) - welcome_screen: { - enabled: boolean; - description: string; - welcome_channels: { - description: string; - emoji_id?: string; - emoji_name?: string; - channel_id: string; - }[]; - }; + welcome_screen: GuildWelcomeScreen; @Column({ nullable: true }) @RelationId((guild: Guild) => guild.widget_channel) @@ -336,6 +324,18 @@ export class Guild extends BaseClass { description: "Fill in your description", welcome_channels: [], }, + + afk_timeout: Config.get().defaults.guild.afkTimeout, + default_message_notifications: + Config.get().defaults.guild.defaultMessageNotifications, + explicit_content_filter: + Config.get().defaults.guild.explicitContentFilter, + features: Config.get().guild.defaultFeatures, + max_members: Config.get().limits.guild.maxMembers, + max_presences: Config.get().defaults.guild.maxPresences, + max_video_channel_users: + Config.get().defaults.guild.maxVideoChannelUsers, + region: Config.get().regions.default, }).save(); // we have to create the role _after_ the guild because else we would get a "SQLITE_CONSTRAINT: FOREIGN KEY constraint failed" error @@ -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) @@ -389,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/Member.ts b/src/util/entities/Member.ts
index 8c208202..8be6eae1 100644 --- a/src/util/entities/Member.ts +++ b/src/util/entities/Member.ts
@@ -344,11 +344,7 @@ export class Member extends BaseClassWithoutId { relations: ["user", "roles"], take: 10, }) - ).map((member) => ({ - ...member.toPublicMember(), - user: member.user.toPublicUser(), - roles: member.roles.map((x) => x.id), - })); + ).map((member) => member.toPublicMember()); if ( await Member.count({ @@ -455,6 +451,10 @@ export class Member extends BaseClassWithoutId { PublicMemberProjection.forEach((x) => { member[x] = this[x]; }); + + if (member.roles) member.roles = member.roles.map((x: Role) => x.id); + if (member.user) member.user = member.user.toPublicUser(); + return member as PublicMember; } } diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts
index 519c431e..3598d29f 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,29 @@ export class Message extends BaseClass { @Column({ type: "simple-json", nullable: true }) components?: MessageComponent[]; + + toJSON(): Message { + return { + ...this, + author_id: undefined, + member_id: undefined, + webhook_id: undefined, + application_id: undefined, + + nonce: this.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/entities/Role.ts b/src/util/entities/Role.ts
index 85877c12..9a601f31 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({ default: 0 }) + flags: number; } diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts
index df9af328..3f1bda05 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts
@@ -26,11 +26,11 @@ import { OneToOne, } from "typeorm"; import { - adjustEmail, Config, Email, FieldErrors, Snowflake, + adjustEmail, trimSpecial, } from ".."; import { BitField } from "../util/BitField"; @@ -86,8 +86,7 @@ export const PrivateUserProjection = [ // Private user data that should never get sent to the client export type PublicUser = Pick<User, PublicUserKeys>; - -export type UserPublic = Pick<User, PublicUserKeys>; +export type PrivateUser = Pick<User, PrivateUserKeys>; export interface UserPrivate extends Pick<User, PrivateUserKeys> { locale: string; @@ -110,8 +109,10 @@ export class User extends BaseClass { @Column({ nullable: true }) banner?: string; // hash of the user banner + // TODO: Separate `User` and `UserProfile` models + // puyo: changed from [number, number] because it breaks openapi @Column({ nullable: true, type: "simple-array" }) - theme_colors?: [number, number]; // TODO: Separate `User` and `UserProfile` models + theme_colors?: number[]; @Column({ nullable: true }) pronouns?: string; @@ -126,10 +127,10 @@ export class User extends BaseClass { mobile: boolean = false; // if the user has mobile app installed @Column() - premium: boolean = Config.get().defaults.user.premium ?? false; // if user bought individual premium + premium: boolean; // if user bought individual premium @Column() - premium_type: number = Config.get().defaults.user.premiumType ?? 0; // individual premium level + premium_type: number; // individual premium level @Column() bot: boolean = false; // if user is bot @@ -156,13 +157,13 @@ export class User extends BaseClass { totp_last_ticket?: string = ""; @Column() - created_at: Date = new Date(); // registration date + created_at: Date; // registration date @Column({ nullable: true }) premium_since: Date; // premium date @Column({ select: false }) - verified: boolean = Config.get().defaults.user.verified ?? true; // email is verified + verified: boolean; // email is verified @Column() disabled: boolean = false; // if the account is disabled @@ -174,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; @@ -280,6 +281,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<User>) { return await User.findOneOrFail({ where: { id: user_id }, @@ -381,11 +391,16 @@ export class User extends BaseClass { valid_tokens_since: new Date(), }, extended_settings: "{}", + settings: settings, + premium_since: Config.get().defaults.user.premium ? new Date() : undefined, - settings: settings, rights: Config.get().register.defaultRights, + premium: Config.get().defaults.user.premium ?? false, + premium_type: Config.get().defaults.user.premiumType ?? 0, + verified: Config.get().defaults.user.verified ?? true, + created_at: new Date(), }); user.validate(); diff --git a/src/util/interfaces/Activity.ts b/src/util/interfaces/Activity.ts
index 7654ba90..0227f242 100644 --- a/src/util/interfaces/Activity.ts +++ b/src/util/interfaces/Activity.ts
@@ -36,7 +36,7 @@ export interface Activity { }; party?: { id?: string; - size?: [number]; // used to show the party's current and maximum size // TODO: array length 2 + size?: number[]; // used to show the party's current and maximum size // TODO: array length 2 }; assets?: { large_image?: string; // the id for a large asset of the activity, usually a snowflake diff --git a/src/util/interfaces/Event.ts b/src/util/interfaces/Event.ts
index 76a5f8d0..deb54428 100644 --- a/src/util/interfaces/Event.ts +++ b/src/util/interfaces/Event.ts
@@ -28,7 +28,6 @@ import { Role, Emoji, PublicMember, - UserGuildSettings, Guild, Channel, PublicUser, @@ -40,6 +39,10 @@ import { UserSettings, IReadyGuildDTO, ReadState, + UserPrivate, + ReadyUserGuildSettingsEntries, + ReadyPrivateChannel, + GuildOrUnavailable, } from "@spacebar/util"; export interface Event { @@ -68,22 +71,10 @@ 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[]; + guilds: IReadyGuildDTO[] | GuildOrUnavailable[]; // depends on capability analytics_token?: string; connected_accounts?: ConnectedAccount[]; consents?: { @@ -115,7 +106,7 @@ export interface ReadyEventData { version: number; }; user_guild_settings?: { - entries: UserGuildSettings[]; + entries: ReadyUserGuildSettingsEntries[]; version: number; partial: boolean; }; @@ -127,6 +118,17 @@ 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; + auth_session_id_hash: 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 { @@ -581,6 +583,7 @@ export type EventData = export enum EVENTEnum { Ready = "READY", + ReadySupplemental = "READY_SUPPLEMENTAL", ChannelCreate = "CHANNEL_CREATE", ChannelUpdate = "CHANNEL_UPDATE", ChannelDelete = "CHANNEL_DELETE", diff --git a/src/util/interfaces/GuildWelcomeScreen.ts b/src/util/interfaces/GuildWelcomeScreen.ts new file mode 100644
index 00000000..38b6061b --- /dev/null +++ b/src/util/interfaces/GuildWelcomeScreen.ts
@@ -0,0 +1,10 @@ +export interface GuildWelcomeScreen { + enabled: boolean; + description: string; + welcome_channels: { + description: string; + emoji_id?: string; + emoji_name?: string; + channel_id: string; + }[]; +} diff --git a/src/util/interfaces/index.ts b/src/util/interfaces/index.ts
index c6a00458..6620ba32 100644 --- a/src/util/interfaces/index.ts +++ b/src/util/interfaces/index.ts
@@ -19,6 +19,7 @@ export * from "./Activity"; export * from "./ConnectedAccount"; export * from "./Event"; +export * from "./GuildWelcomeScreen"; export * from "./Interaction"; export * from "./Presence"; export * from "./Status"; diff --git a/src/util/schemas/AckBulkSchema.ts b/src/util/schemas/AckBulkSchema.ts
index cf6dc597..5604c2fc 100644 --- a/src/util/schemas/AckBulkSchema.ts +++ b/src/util/schemas/AckBulkSchema.ts
@@ -17,11 +17,9 @@ */ export interface AckBulkSchema { - read_states: [ - { - channel_id: string; - message_id: string; - read_state_type: number; // WHat is this? - }, - ]; + read_states: { + channel_id: string; + message_id: string; + read_state_type: number; // WHat is this? + }[]; } diff --git a/src/util/schemas/ConnectedAccountSchema.ts b/src/util/schemas/ConnectedAccountSchema.ts
index fe808a35..5fd05b71 100644 --- a/src/util/schemas/ConnectedAccountSchema.ts +++ b/src/util/schemas/ConnectedAccountSchema.ts
@@ -30,7 +30,7 @@ export interface ConnectedAccountSchema { verified?: boolean; visibility?: number; integrations?: string[]; - metadata_?: any; + metadata_?: unknown; metadata_visibility?: number; two_way_link?: boolean; } diff --git a/src/util/schemas/ConnectionCallbackSchema.ts b/src/util/schemas/ConnectionCallbackSchema.ts
index eb86c087..b66bfe20 100644 --- a/src/util/schemas/ConnectionCallbackSchema.ts +++ b/src/util/schemas/ConnectionCallbackSchema.ts
@@ -21,5 +21,5 @@ export interface ConnectionCallbackSchema { state: string; insecure: boolean; friend_sync: boolean; - openid_params?: any; // TODO: types + openid_params?: unknown; // TODO: types } diff --git a/src/util/schemas/IdentifySchema.ts b/src/util/schemas/IdentifySchema.ts
index fb48c2a4..0619dacc 100644 --- a/src/util/schemas/IdentifySchema.ts +++ b/src/util/schemas/IdentifySchema.ts
@@ -66,6 +66,7 @@ export const IdentifySchema = { $private_channels_version: Number, $guild_versions: Object, $api_code_version: Number, + $initial_guild_id: String, }, $clientState: { $guildHashes: Object, @@ -75,6 +76,7 @@ export const IdentifySchema = { $userGuildSettingsVersion: undefined, $guildVersions: Object, $apiCodeVersion: Number, + $initialGuildId: String, }, $v: Number, $version: Number, @@ -109,7 +111,11 @@ export interface IdentifySchema { compress?: boolean; large_threshold?: number; largeThreshold?: number; - shard?: [bigint, bigint]; + /** + * @minItems 2 + * @maxItems 2 + */ + shard?: bigint[]; // puyo: changed from [bigint, bigint] because it breaks openapi guild_subscriptions?: boolean; capabilities?: number; client_state?: { @@ -122,6 +128,7 @@ export interface IdentifySchema { private_channels_version?: number; guild_versions?: unknown; api_code_version?: number; + initial_guild_id?: string; }; clientState?: { guildHashes?: unknown; @@ -131,6 +138,7 @@ export interface IdentifySchema { useruserGuildSettingsVersion?: number; guildVersions?: unknown; apiCodeVersion?: number; + initialGuildId?: string; }; v?: number; } diff --git a/src/util/schemas/LazyRequestSchema.ts b/src/util/schemas/LazyRequestSchema.ts
index 63e67416..ee52d66c 100644 --- a/src/util/schemas/LazyRequestSchema.ts +++ b/src/util/schemas/LazyRequestSchema.ts
@@ -18,7 +18,14 @@ export interface LazyRequestSchema { guild_id: string; - channels?: Record<string, [number, number][]>; + channels?: { + /** + * @items.type integer + * @minItems 2 + * @maxItems 2 + */ + [key: string]: number[][]; // puyo: changed from [number, number] because it breaks openapi + }; activities?: boolean; threads?: boolean; typing?: true; diff --git a/src/util/schemas/LoginResponse.ts b/src/util/schemas/LoginResponse.ts new file mode 100644
index 00000000..faf3f769 --- /dev/null +++ b/src/util/schemas/LoginResponse.ts
@@ -0,0 +1,14 @@ +import { TokenResponse } from "./responses"; + +export interface MFAResponse { + ticket: string; + mfa: true; + sms: false; // TODO + token: null; +} + +export interface WebAuthnResponse extends MFAResponse { + webauthn: string; +} + +export type LoginResponse = TokenResponse | MFAResponse | WebAuthnResponse; diff --git a/src/util/schemas/MemberChangeProfileSchema.ts b/src/util/schemas/MemberChangeProfileSchema.ts
index e955a0f1..d2d1481d 100644 --- a/src/util/schemas/MemberChangeProfileSchema.ts +++ b/src/util/schemas/MemberChangeProfileSchema.ts
@@ -21,8 +21,7 @@ export interface MemberChangeProfileSchema { nick?: string; bio?: string; pronouns?: string; - - /* + /** * @items.type integer */ theme_colors?: [number, number]; 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) 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/schemas/UserGuildSettingsSchema.ts b/src/util/schemas/UserGuildSettingsSchema.ts
index c295f767..82edae9c 100644 --- a/src/util/schemas/UserGuildSettingsSchema.ts +++ b/src/util/schemas/UserGuildSettingsSchema.ts
@@ -16,12 +16,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { UserGuildSettings, ChannelOverride } from "@spacebar/util"; +import { ChannelOverride, UserGuildSettings } from "@spacebar/util"; // This sucks. I would use a DeepPartial, my own or typeorms, but they both generate inncorect schema export interface UserGuildSettingsSchema extends Partial<Omit<UserGuildSettings, "channel_overrides">> { channel_overrides?: { - [channel_id: string]: Partial<ChannelOverride>; + [channel_id: string]: ChannelOverride; }; } diff --git a/src/util/schemas/UserNoteUpdateSchema.ts b/src/util/schemas/UserNoteUpdateSchema.ts new file mode 100644
index 00000000..0a731279 --- /dev/null +++ b/src/util/schemas/UserNoteUpdateSchema.ts
@@ -0,0 +1,3 @@ +export interface UserNoteUpdateSchema { + note: string; +} diff --git a/src/util/schemas/UserProfileModifySchema.ts b/src/util/schemas/UserProfileModifySchema.ts
index d49fe326..3dea257a 100644 --- a/src/util/schemas/UserProfileModifySchema.ts +++ b/src/util/schemas/UserProfileModifySchema.ts
@@ -21,8 +21,7 @@ export interface UserProfileModifySchema { accent_color?: number | null; banner?: string | null; pronouns?: string; - - /* + /** * @items.type integer */ theme_colors?: [number, number]; diff --git a/src/util/schemas/UserProfileResponse.ts b/src/util/schemas/UserProfileResponse.ts deleted file mode 100644
index 699d6a29..00000000 --- a/src/util/schemas/UserProfileResponse.ts +++ /dev/null
@@ -1,26 +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 <https://www.gnu.org/licenses/>. -*/ - -import { PublicConnectedAccount, UserPublic } from ".."; - -export interface UserProfileResponse { - user: UserPublic; - connected_accounts: PublicConnectedAccount; - premium_guild_since?: Date; - premium_since?: Date; -} diff --git a/src/util/schemas/UserRelationsResponse.ts b/src/util/schemas/UserRelationsResponse.ts deleted file mode 100644
index 38507420..00000000 --- a/src/util/schemas/UserRelationsResponse.ts +++ /dev/null
@@ -1,27 +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 <https://www.gnu.org/licenses/>. -*/ - -export interface UserRelationsResponse { - object: { - id?: string; - username?: string; - avatar?: string; - discriminator?: string; - public_flags?: number; - }; -} diff --git a/src/util/schemas/VoiceStateUpdateSchema.ts b/src/util/schemas/VoiceStateUpdateSchema.ts
index a7d9f9d7..fda073e9 100644 --- a/src/util/schemas/VoiceStateUpdateSchema.ts +++ b/src/util/schemas/VoiceStateUpdateSchema.ts
@@ -26,6 +26,7 @@ export interface VoiceStateUpdateSchema { preferred_region?: string; request_to_speak_timestamp?: Date; suppress?: boolean; + flags?: number; } export const VoiceStateUpdateSchema = { @@ -37,4 +38,5 @@ export const VoiceStateUpdateSchema = { $preferred_region: String, $request_to_speak_timestamp: Date, $suppress: Boolean, + $flags: Number, }; diff --git a/src/util/schemas/WebAuthnSchema.ts b/src/util/schemas/WebAuthnSchema.ts
index 652cda34..3f5e0da7 100644 --- a/src/util/schemas/WebAuthnSchema.ts +++ b/src/util/schemas/WebAuthnSchema.ts
@@ -28,9 +28,9 @@ export interface CreateWebAuthnCredentialSchema { ticket: string; } -export type WebAuthnPostSchema = Partial< - GenerateWebAuthnCredentialsSchema | CreateWebAuthnCredentialSchema ->; +export type WebAuthnPostSchema = + | GenerateWebAuthnCredentialsSchema + | CreateWebAuthnCredentialSchema; export interface WebAuthnTotpSchema { code: string; diff --git a/src/util/schemas/index.ts b/src/util/schemas/index.ts
index 2d254752..44a504cd 100644 --- a/src/util/schemas/index.ts +++ b/src/util/schemas/index.ts
@@ -69,6 +69,7 @@ export * from "./TotpSchema"; export * from "./UserDeleteSchema"; export * from "./UserGuildSettingsSchema"; export * from "./UserModifySchema"; +export * from "./UserNoteUpdateSchema"; export * from "./UserProfileModifySchema"; export * from "./UserSettingsSchema"; export * from "./Validator"; @@ -79,7 +80,4 @@ export * from "./VoiceVideoSchema"; export * from "./WebAuthnSchema"; export * from "./WebhookCreateSchema"; export * from "./WidgetModifySchema"; -export * from "./UserRelationsResponse"; -export * from "./GatewayResponse"; -export * from "./GatewayBotResponse"; -export * from "./UserProfileResponse"; +export * from "./responses"; diff --git a/src/util/schemas/responses/APIErrorOrCaptchaResponse.ts b/src/util/schemas/responses/APIErrorOrCaptchaResponse.ts new file mode 100644
index 00000000..c9a0e5be --- /dev/null +++ b/src/util/schemas/responses/APIErrorOrCaptchaResponse.ts
@@ -0,0 +1,6 @@ +import { APIErrorResponse } from "./APIErrorResponse"; +import { CaptchaRequiredResponse } from "./CaptchaRequiredResponse"; + +export type APIErrorOrCaptchaResponse = + | CaptchaRequiredResponse + | APIErrorResponse; diff --git a/src/util/schemas/responses/APIErrorResponse.ts b/src/util/schemas/responses/APIErrorResponse.ts new file mode 100644
index 00000000..25bb9504 --- /dev/null +++ b/src/util/schemas/responses/APIErrorResponse.ts
@@ -0,0 +1,12 @@ +export interface APIErrorResponse { + code: number; + message: string; + errors: { + [key: string]: { + _errors: { + message: string; + code: string; + }[]; + }; + }; +} diff --git a/src/util/schemas/responses/BackupCodesChallengeResponse.ts b/src/util/schemas/responses/BackupCodesChallengeResponse.ts new file mode 100644
index 00000000..5473ad1f --- /dev/null +++ b/src/util/schemas/responses/BackupCodesChallengeResponse.ts
@@ -0,0 +1,4 @@ +export interface BackupCodesChallengeResponse { + nonce: string; + regenerate_nonce: string; +} diff --git a/src/util/schemas/responses/CaptchaRequiredResponse.ts b/src/util/schemas/responses/CaptchaRequiredResponse.ts new file mode 100644
index 00000000..9f7f02ff --- /dev/null +++ b/src/util/schemas/responses/CaptchaRequiredResponse.ts
@@ -0,0 +1,5 @@ +export interface CaptchaRequiredResponse { + captcha_key: string; + captcha_sitekey: string; + captcha_service: string; +} diff --git a/src/util/schemas/responses/DiscoverableGuildsResponse.ts b/src/util/schemas/responses/DiscoverableGuildsResponse.ts new file mode 100644
index 00000000..2a9fb1bd --- /dev/null +++ b/src/util/schemas/responses/DiscoverableGuildsResponse.ts
@@ -0,0 +1,8 @@ +import { Guild } from "../../entities"; + +export interface DiscoverableGuildsResponse { + total: number; + guilds: Guild[]; + offset: number; + limit: number; +} diff --git a/src/util/schemas/responses/GatewayBotResponse.ts b/src/util/schemas/responses/GatewayBotResponse.ts new file mode 100644
index 00000000..30f1f57f --- /dev/null +++ b/src/util/schemas/responses/GatewayBotResponse.ts
@@ -0,0 +1,10 @@ +export interface GatewayBotResponse { + url: string; + shards: number; + session_start_limit: { + total: number; + remaining: number; + reset_after: number; + max_concurrency: number; + }; +} diff --git a/src/util/schemas/responses/GatewayResponse.ts b/src/util/schemas/responses/GatewayResponse.ts new file mode 100644
index 00000000..e909f7bd --- /dev/null +++ b/src/util/schemas/responses/GatewayResponse.ts
@@ -0,0 +1,3 @@ +export interface GatewayResponse { + url: string; +} diff --git a/src/util/schemas/responses/GenerateRegistrationTokensResponse.ts b/src/util/schemas/responses/GenerateRegistrationTokensResponse.ts new file mode 100644
index 00000000..8816eabf --- /dev/null +++ b/src/util/schemas/responses/GenerateRegistrationTokensResponse.ts
@@ -0,0 +1,3 @@ +export interface GenerateRegistrationTokensResponse { + tokens: string[]; +} diff --git a/src/util/schemas/responses/GuildBansResponse.ts b/src/util/schemas/responses/GuildBansResponse.ts new file mode 100644
index 00000000..876a4bc4 --- /dev/null +++ b/src/util/schemas/responses/GuildBansResponse.ts
@@ -0,0 +1,10 @@ +export interface GuildBansResponse { + reason: string; + user: { + username: string; + discriminator: string; + id: string; + avatar: string | null; + public_flags: number; + }; +} diff --git a/src/util/schemas/responses/GuildCreateResponse.ts b/src/util/schemas/responses/GuildCreateResponse.ts new file mode 100644
index 00000000..8185cb86 --- /dev/null +++ b/src/util/schemas/responses/GuildCreateResponse.ts
@@ -0,0 +1,3 @@ +export interface GuildCreateResponse { + id: string; +} diff --git a/src/util/schemas/responses/GuildDiscoveryRequirements.ts b/src/util/schemas/responses/GuildDiscoveryRequirements.ts new file mode 100644
index 00000000..731976f7 --- /dev/null +++ b/src/util/schemas/responses/GuildDiscoveryRequirements.ts
@@ -0,0 +1,23 @@ +export interface GuildDiscoveryRequirementsResponse { + uild_id: string; + safe_environment: boolean; + healthy: boolean; + health_score_pending: boolean; + size: boolean; + nsfw_properties: unknown; + protected: boolean; + sufficient: boolean; + sufficient_without_grace_period: boolean; + valid_rules_channel: boolean; + retention_healthy: boolean; + engagement_healthy: boolean; + age: boolean; + minimum_age: number; + health_score: { + avg_nonnew_participators: number; + avg_nonnew_communicators: number; + num_intentful_joiners: number; + perc_ret_w1_intentful: number; + }; + minimum_size: number; +} diff --git a/src/util/schemas/responses/GuildMessagesSearchResponse.ts b/src/util/schemas/responses/GuildMessagesSearchResponse.ts new file mode 100644
index 00000000..0b6248b7 --- /dev/null +++ b/src/util/schemas/responses/GuildMessagesSearchResponse.ts
@@ -0,0 +1,32 @@ +import { + Attachment, + Embed, + MessageType, + PublicUser, + Role, +} from "../../entities"; + +export interface GuildMessagesSearchMessage { + id: string; + type: MessageType; + content?: string; + channel_id: string; + author: PublicUser; + attachments: Attachment[]; + embeds: Embed[]; + mentions: PublicUser[]; + mention_roles: Role[]; + pinned: boolean; + mention_everyone?: boolean; + tts: boolean; + timestamp: string; + edited_timestamp: string | null; + flags: number; + components: unknown[]; + hit: true; +} + +export interface GuildMessagesSearchResponse { + messages: GuildMessagesSearchMessage[]; + total_results: number; +} diff --git a/src/util/schemas/responses/GuildPruneResponse.ts b/src/util/schemas/responses/GuildPruneResponse.ts new file mode 100644
index 00000000..fb1abb89 --- /dev/null +++ b/src/util/schemas/responses/GuildPruneResponse.ts
@@ -0,0 +1,7 @@ +export interface GuildPruneResponse { + pruned: number; +} + +export interface GuildPurgeResponse { + purged: number; +} diff --git a/src/util/schemas/responses/GuildRecommendationsResponse.ts b/src/util/schemas/responses/GuildRecommendationsResponse.ts new file mode 100644
index 00000000..211670a6 --- /dev/null +++ b/src/util/schemas/responses/GuildRecommendationsResponse.ts
@@ -0,0 +1,6 @@ +import { Guild } from "../../entities"; + +export interface GuildRecommendationsResponse { + recommended_guilds: Guild[]; + load_id: string; +} diff --git a/src/util/schemas/responses/GuildVanityUrl.ts b/src/util/schemas/responses/GuildVanityUrl.ts new file mode 100644
index 00000000..ff37bf4e --- /dev/null +++ b/src/util/schemas/responses/GuildVanityUrl.ts
@@ -0,0 +1,17 @@ +export interface GuildVanityUrl { + code: string; + uses: number; +} + +export interface GuildVanityUrlNoInvite { + code: null; +} + +export type GuildVanityUrlResponse = + | GuildVanityUrl + | GuildVanityUrl[] + | GuildVanityUrlNoInvite; + +export interface GuildVanityUrlCreateResponse { + code: string; +} diff --git a/src/util/schemas/responses/GuildVoiceRegionsResponse.ts b/src/util/schemas/responses/GuildVoiceRegionsResponse.ts new file mode 100644
index 00000000..8190d5fd --- /dev/null +++ b/src/util/schemas/responses/GuildVoiceRegionsResponse.ts
@@ -0,0 +1,7 @@ +export interface GuildVoiceRegion { + id: string; + name: string; + custom: boolean; + deprecated: boolean; + optimal: boolean; +} diff --git a/src/util/schemas/responses/GuildWidgetJsonResponse.ts b/src/util/schemas/responses/GuildWidgetJsonResponse.ts new file mode 100644
index 00000000..ef85dd08 --- /dev/null +++ b/src/util/schemas/responses/GuildWidgetJsonResponse.ts
@@ -0,0 +1,21 @@ +import { ClientStatus } from "../../interfaces"; + +export interface GuildWidgetJsonResponse { + id: string; + name: string; + instant_invite: string; + channels: { + id: string; + name: string; + position: number; + }[]; + members: { + id: string; + username: string; + discriminator: string; + avatar: string | null; + status: ClientStatus; + avatar_url: string; + }[]; + presence_count: number; +} diff --git a/src/util/schemas/responses/GuildWidgetSettingsResponse.ts b/src/util/schemas/responses/GuildWidgetSettingsResponse.ts new file mode 100644
index 00000000..3c6b45ce --- /dev/null +++ b/src/util/schemas/responses/GuildWidgetSettingsResponse.ts
@@ -0,0 +1,6 @@ +import { Snowflake } from "../../util"; + +export interface GuildWidgetSettingsResponse { + enabled: boolean; + channel_id: Snowflake | null; +} diff --git a/src/util/schemas/responses/InstanceDomainsResponse.ts b/src/util/schemas/responses/InstanceDomainsResponse.ts new file mode 100644
index 00000000..60367492 --- /dev/null +++ b/src/util/schemas/responses/InstanceDomainsResponse.ts
@@ -0,0 +1,6 @@ +export interface InstanceDomainsResponse { + cdn: string; + gateway: string; + defaultApiVersion: string; + apiEndpoint: string; +} diff --git a/src/util/schemas/responses/InstancePingResponse.ts b/src/util/schemas/responses/InstancePingResponse.ts new file mode 100644
index 00000000..5f1a9488 --- /dev/null +++ b/src/util/schemas/responses/InstancePingResponse.ts
@@ -0,0 +1,13 @@ +export interface InstancePingResponse { + ping: "pong!"; + instance: { + id: string; + name: string; + description: string | null; + image: string | null; + correspondenceEmail: string | null; + correspondenceUserID: string | null; + frontPage: string | null; + tosPage: string | null; + }; +} diff --git a/src/util/schemas/responses/InstanceStatsResponse.ts b/src/util/schemas/responses/InstanceStatsResponse.ts new file mode 100644
index 00000000..d24fd434 --- /dev/null +++ b/src/util/schemas/responses/InstanceStatsResponse.ts
@@ -0,0 +1,8 @@ +export interface InstanceStatsResponse { + counts: { + user: number; + guild: number; + message: number; + members: number; + }; +} diff --git a/src/util/schemas/responses/LocationMetadataResponse.ts b/src/util/schemas/responses/LocationMetadataResponse.ts new file mode 100644
index 00000000..55337557 --- /dev/null +++ b/src/util/schemas/responses/LocationMetadataResponse.ts
@@ -0,0 +1,5 @@ +export interface LocationMetadataResponse { + consent_required: boolean; + country_code: string; + promotional_email_opt_in: { required: true; pre_checked: false }; +} diff --git a/src/util/schemas/responses/MemberJoinGuildResponse.ts b/src/util/schemas/responses/MemberJoinGuildResponse.ts new file mode 100644
index 00000000..d7b39d10 --- /dev/null +++ b/src/util/schemas/responses/MemberJoinGuildResponse.ts
@@ -0,0 +1,8 @@ +import { Emoji, Guild, Role, Sticker } from "../../entities"; + +export interface MemberJoinGuildResponse { + guild: Guild; + emojis: Emoji[]; + roles: Role[]; + stickers: Sticker[]; +} diff --git a/src/util/schemas/responses/OAuthAuthorizeResponse.ts b/src/util/schemas/responses/OAuthAuthorizeResponse.ts new file mode 100644
index 00000000..60d6d2e2 --- /dev/null +++ b/src/util/schemas/responses/OAuthAuthorizeResponse.ts
@@ -0,0 +1,3 @@ +export interface OAuthAuthorizeResponse { + location: string; +} diff --git a/src/util/schemas/responses/Tenor.ts b/src/util/schemas/responses/Tenor.ts new file mode 100644
index 00000000..9dddf9d0 --- /dev/null +++ b/src/util/schemas/responses/Tenor.ts
@@ -0,0 +1,72 @@ +export enum TenorMediaTypes { + gif, + mediumgif, + tinygif, + nanogif, + mp4, + loopedmp4, + tinymp4, + nanomp4, + webm, + tinywebm, + nanowebm, +} + +export type TenorMedia = { + preview: string; + url: string; + dims: number[]; + size: number; +}; + +export type TenorGif = { + created: number; + hasaudio: boolean; + id: string; + media: { [type in keyof typeof TenorMediaTypes]: TenorMedia }[]; + tags: string[]; + title: string; + itemurl: string; + hascaption: boolean; + url: string; +}; + +export type TenorCategory = { + searchterm: string; + path: string; + image: string; + name: string; +}; + +export type TenorCategoriesResults = { + tags: TenorCategory[]; +}; + +export type TenorTrendingResults = { + next: string; + results: TenorGif[]; + locale: string; +}; + +export type TenorSearchResults = { + next: string; + results: TenorGif[]; +}; + +export interface TenorGifResponse { + id: string; + title: string; + url: string; + src: string; + gif_src: string; + width: number; + height: number; + preview: string; +} + +export interface TenorTrendingResponse { + categories: TenorCategoriesResults; + gifs: TenorGifResponse[]; +} + +export type TenorGifsResponse = TenorGifResponse[]; diff --git a/src/util/schemas/responses/TokenResponse.ts b/src/util/schemas/responses/TokenResponse.ts new file mode 100644
index 00000000..7e93055a --- /dev/null +++ b/src/util/schemas/responses/TokenResponse.ts
@@ -0,0 +1,15 @@ +import { BackupCode, UserSettings } from "../../entities"; + +export interface TokenResponse { + token: string; + settings: UserSettings; +} + +export interface TokenOnlyResponse { + token: string; +} + +export interface TokenWithBackupCodesResponse { + token: string; + backup_codes: BackupCode[]; +} diff --git a/src/util/schemas/responses/TypedResponses.ts b/src/util/schemas/responses/TypedResponses.ts new file mode 100644
index 00000000..4349b93c --- /dev/null +++ b/src/util/schemas/responses/TypedResponses.ts
@@ -0,0 +1,88 @@ +import { GeneralConfiguration, LimitsConfiguration } from "../../config"; +import { DmChannelDTO } from "../../dtos"; +import { + Application, + BackupCode, + Categories, + Channel, + Emoji, + Guild, + Invite, + Member, + Message, + PrivateUser, + PublicMember, + PublicUser, + Role, + Sticker, + StickerPack, + Template, + Webhook, +} from "../../entities"; +import { GuildVoiceRegion } from "./GuildVoiceRegionsResponse"; + +// removes internal properties from the guild class +export type APIGuild = Omit< + Guild, + | "afk_channel" + | "template" + | "owner" + | "public_updates_channel" + | "rules_channel" + | "system_channel" + | "widget_channel" +>; + +export type APIPublicUser = PublicUser; +export type APIPrivateUser = PrivateUser; + +export type APIGuildArray = APIGuild[]; + +export type APIDMChannelArray = DmChannelDTO[]; + +export type APIBackupCodeArray = BackupCode[]; + +export interface UserUpdateResponse extends APIPrivateUser { + newToken?: string; +} + +export type ApplicationDetectableResponse = unknown[]; + +export type ApplicationEntitlementsResponse = unknown[]; + +export type ApplicationSkusResponse = unknown[]; + +export type APIApplicationArray = Application[]; + +export type APIInviteArray = Invite[]; + +export type APIMessageArray = Message[]; + +export type APIWebhookArray = Webhook[]; + +export type APIDiscoveryCategoryArray = Categories[]; + +export type APIGeneralConfiguration = GeneralConfiguration; + +export type APIChannelArray = Channel[]; + +export type APIEmojiArray = Emoji[]; + +export type APIMemberArray = Member[]; +export type APIPublicMember = PublicMember; + +export interface APIGuildWithJoinedAt extends Guild { + joined_at: string; +} + +export type APIRoleArray = Role[]; + +export type APIStickerArray = Sticker[]; + +export type APITemplateArray = Template[]; + +export type APIGuildVoiceRegion = GuildVoiceRegion[]; + +export type APILimitsConfiguration = LimitsConfiguration; + +export type APIStickerPackArray = StickerPack[]; diff --git a/src/util/schemas/responses/UpdatesResponse.ts b/src/util/schemas/responses/UpdatesResponse.ts new file mode 100644
index 00000000..6b8566f4 --- /dev/null +++ b/src/util/schemas/responses/UpdatesResponse.ts
@@ -0,0 +1,6 @@ +export interface UpdatesResponse { + name: string; + pub_date: string; + url: string; + notes: string | null; +} diff --git a/src/util/schemas/responses/UserNoteResponse.ts b/src/util/schemas/responses/UserNoteResponse.ts new file mode 100644
index 00000000..b142811e --- /dev/null +++ b/src/util/schemas/responses/UserNoteResponse.ts
@@ -0,0 +1,5 @@ +export interface UserNoteResponse { + note: string; + note_user_id: string; + user_id: string; +} diff --git a/src/util/schemas/responses/UserProfileResponse.ts b/src/util/schemas/responses/UserProfileResponse.ts new file mode 100644
index 00000000..eba7cbcc --- /dev/null +++ b/src/util/schemas/responses/UserProfileResponse.ts
@@ -0,0 +1,37 @@ +import { + Member, + PublicConnectedAccount, + PublicMember, + PublicUser, + User, +} from "@spacebar/util"; + +export type MutualGuild = { + id: string; + nick?: string; +}; + +export type PublicMemberProfile = Pick< + Member, + "banner" | "bio" | "guild_id" +> & { + accent_color: null; // TODO +}; + +export type UserProfile = Pick< + User, + "bio" | "accent_color" | "banner" | "pronouns" | "theme_colors" +>; + +export interface UserProfileResponse { + user: PublicUser; + connected_accounts: PublicConnectedAccount; + premium_guild_since?: Date; + premium_since?: Date; + mutual_guilds: MutualGuild[]; + premium_type: number; + profile_themes_experiment_bucket: number; + user_profile: UserProfile; + guild_member?: PublicMember; + guild_member_profile?: PublicMemberProfile; +} diff --git a/src/util/schemas/responses/UserRelationsResponse.ts b/src/util/schemas/responses/UserRelationsResponse.ts new file mode 100644
index 00000000..e784cafb --- /dev/null +++ b/src/util/schemas/responses/UserRelationsResponse.ts
@@ -0,0 +1,7 @@ +import { User } from "@spacebar/util"; + +export type UserRelationsResponse = (Pick<User, "id"> & + Pick<User, "username"> & + Pick<User, "discriminator"> & + Pick<User, "avatar"> & + Pick<User, "public_flags">)[]; diff --git a/src/util/schemas/responses/UserRelationshipsResponse.ts b/src/util/schemas/responses/UserRelationshipsResponse.ts new file mode 100644
index 00000000..dff2f118 --- /dev/null +++ b/src/util/schemas/responses/UserRelationshipsResponse.ts
@@ -0,0 +1,8 @@ +import { PublicUser, RelationshipType } from "../../entities"; + +export interface UserRelationshipsResponse { + id: string; + type: RelationshipType; + nickname: null; + user: PublicUser; +} diff --git a/src/util/schemas/responses/WebAuthnCreateResponse.ts b/src/util/schemas/responses/WebAuthnCreateResponse.ts new file mode 100644
index 00000000..9aa9e206 --- /dev/null +++ b/src/util/schemas/responses/WebAuthnCreateResponse.ts
@@ -0,0 +1,4 @@ +export interface WebAuthnCreateResponse { + name: string; + id: string; +} diff --git a/src/util/schemas/responses/WebhookCreateResponse.ts b/src/util/schemas/responses/WebhookCreateResponse.ts new file mode 100644
index 00000000..ae142632 --- /dev/null +++ b/src/util/schemas/responses/WebhookCreateResponse.ts
@@ -0,0 +1,6 @@ +import { User, Webhook } from "../../entities"; + +export interface WebhookCreateResponse { + user: User; + hook: Webhook; +} diff --git a/src/util/schemas/responses/index.ts b/src/util/schemas/responses/index.ts new file mode 100644
index 00000000..d8b7fd57 --- /dev/null +++ b/src/util/schemas/responses/index.ts
@@ -0,0 +1,34 @@ +export * from "./APIErrorOrCaptchaResponse"; +export * from "./APIErrorResponse"; +export * from "./BackupCodesChallengeResponse"; +export * from "./CaptchaRequiredResponse"; +export * from "./DiscoverableGuildsResponse"; +export * from "./GatewayBotResponse"; +export * from "./GatewayResponse"; +export * from "./GenerateRegistrationTokensResponse"; +export * from "./GuildBansResponse"; +export * from "./GuildCreateResponse"; +export * from "./GuildDiscoveryRequirements"; +export * from "./GuildMessagesSearchResponse"; +export * from "./GuildPruneResponse"; +export * from "./GuildRecommendationsResponse"; +export * from "./GuildVanityUrl"; +export * from "./GuildVoiceRegionsResponse"; +export * from "./GuildWidgetJsonResponse"; +export * from "./GuildWidgetSettingsResponse"; +export * from "./InstanceDomainsResponse"; +export * from "./InstancePingResponse"; +export * from "./InstanceStatsResponse"; +export * from "./LocationMetadataResponse"; +export * from "./MemberJoinGuildResponse"; +export * from "./OAuthAuthorizeResponse"; +export * from "./Tenor"; +export * from "./TokenResponse"; +export * from "./TypedResponses"; +export * from "./UpdatesResponse"; +export * from "./UserNoteResponse"; +export * from "./UserProfileResponse"; +export * from "./UserRelationshipsResponse"; +export * from "./UserRelationsResponse"; +export * from "./WebAuthnCreateResponse"; +export * from "./WebhookCreateResponse"; diff --git a/src/util/util/Application.ts b/src/util/util/Application.ts new file mode 100644
index 00000000..23019a7f --- /dev/null +++ b/src/util/util/Application.ts
@@ -0,0 +1,24 @@ +import { Request } from "express"; +import { Application, User } from "../entities"; + +export async function createAppBotUser(app: Application, req: Request) { + const user = await User.register({ + username: app.name, + password: undefined, + id: app.id, + req, + }); + + user.id = app.id; + user.premium_since = new Date(); + user.bot = true; + + await user.save(); + + // flags is NaN here? + app.assign({ bot: user, flags: app.flags || 0 }); + + await app.save(); + + return user; +} diff --git a/src/util/util/AutoUpdate.ts b/src/util/util/AutoUpdate.ts
index 1f90a41e..2af5cf1c 100644 --- a/src/util/util/AutoUpdate.ts +++ b/src/util/util/AutoUpdate.ts
@@ -18,7 +18,7 @@ import "missing-native-js-functions"; import fetch from "node-fetch"; -import ProxyAgent from "proxy-agent"; +import { ProxyAgent } from "proxy-agent"; import readline from "readline"; import fs from "fs/promises"; import path from "path"; diff --git a/src/util/util/Gifs.ts b/src/util/util/Gifs.ts new file mode 100644
index 00000000..a5a5e64c --- /dev/null +++ b/src/util/util/Gifs.ts
@@ -0,0 +1,25 @@ +import { HTTPError } from "lambert-server"; +import { Config } from "./Config"; +import { TenorGif } from ".."; + +export function parseGifResult(result: TenorGif) { + return { + id: result.id, + title: result.title, + url: result.itemurl, + src: result.media[0].mp4.url, + gif_src: result.media[0].gif.url, + width: result.media[0].mp4.dims[0], + height: result.media[0].mp4.dims[1], + preview: result.media[0].mp4.preview, + }; +} + +export function getGifApiKey() { + const { enabled, provider, apiKey } = Config.get().gif; + if (!enabled) throw new HTTPError(`Gifs are disabled`); + if (provider !== "tenor" || !apiKey) + throw new HTTPError(`${provider} gif provider not supported`); + + return apiKey; +} 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; }; diff --git a/src/util/util/Token.ts b/src/util/util/Token.ts
index 90310176..97bdec74 100644 --- a/src/util/util/Token.ts +++ b/src/util/util/Token.ts
@@ -19,94 +19,69 @@ 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<UserTokenData> { - // 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<UserTokenData> { - 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 - **/ - - 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. - - if (isEmailVerification) return res(checkEmailToken(decoded)); - - const user = await User.findOne({ - where: { id: decoded.id }, - select: ["data", "bot", "disabled", "deleted", "rights"], - }); - - if (!user) return rej("Invalid Token"); - - // 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); - }); + opts?: { + select?: FindOptionsSelectByString<User>; + relations?: FindOptionsRelationByString; + }, +): Promise<UserTokenData> => + new Promise((resolve, reject) => { + token = token.replace("Bot ", ""); // there is no bot distinction in sb + token = token.replace("Bearer ", ""); // allow bearer tokens + + jwt.verify( + token, + Config.get().security.jwtSecret, + JWTOptions, + async (err, out) => { + const decoded = out as UserTokenData["decoded"]; + if (err || !decoded) return reject("Invalid Token"); + + 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 (!user) 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 reject("Invalid Token"); + + if (user.disabled) return reject("User disabled"); + if (user.deleted) return reject("User not found"); + + return resolve({ decoded, user }); + }, + ); }); -} export async function generateToken(id: string, email?: string) { const iat = Math.floor(Date.now() / 1000); diff --git a/src/util/util/index.ts b/src/util/util/index.ts
index 838239b7..10e09b5c 100644 --- a/src/util/util/index.ts +++ b/src/util/util/index.ts
@@ -41,3 +41,5 @@ export * from "./String"; export * from "./Token"; export * from "./TraverseDirectory"; export * from "./WebAuthn"; +export * from "./Gifs"; +export * from "./Application";