Newer
Older
percord / src / schemas / api / users / User.ts
/*
	Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
	Copyright (C) 2025 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 { ConnectedAccountSchema, Snowflake, UserSettingsSchema } from "@spacebar/schemas";
import { BitField } from "@spacebar/util/util";
import { Relationship, Session } from "@spacebar/util/entities";

interface UserEntityPleaseRewriteThankYou {
	id: Snowflake;
	username: string;
	discriminator: string;
	avatar?: string;
	accent_color?: number;
	banner?: string;
	theme_colors?: number[];
	pronouns?: string;
	phone?: string;
	desktop: boolean;
	mobile: boolean;
	premium: boolean;
	premium_type: number;
	bot: boolean;
	bio: string;
	system: boolean;
	nsfw_allowed: boolean;
	mfa_enabled: boolean;
	webauthn_enabled: boolean;
	created_at: Date;
	premium_since: Date;
	verified: boolean;
	disabled: boolean;
	deleted: boolean;
	email?: string;
	flags: number;
	public_flags: number;
	purchased_flags: number;
	premium_usage_flags: number;
	rights: string;
	sessions: Session[];
	relationships: Relationship[];
	connected_accounts: ConnectedAccountSchema[];
	fingerprints: string[];
	settings?: UserSettingsSchema;
	extended_settings: string;
	badge_ids?: string[];
}

export interface PartialUser {
	id: Snowflake;
	username: string;
	discriminator: string;
	global_name?: string | null;
	avatar: string | null;
	avatar_decoration_data?: AvatarDecorationData | null;
	collectibles?: Collectibles | null;
	display_name_styles?: DisplayNameStyle | null;
	primary_guild?: PrimaryGuild | null;
	bot?: boolean;
	system?: boolean;
	banner?: string | null;
	accent_color?: number | null;
	public_flags?: number;
}

export interface AvatarDecorationData {
	asset: string;
	sku_id: Snowflake;
	expires_at: string | null;
}

export interface Collectibles {
	nameplate: NameplateData | null;
}

export interface NameplateData {
	asset: string;
	sku_id: Snowflake;
	label: string;
	palette: string;
	expires_at: number | null;
}

export interface DisplayNameStyle {
	font_id: number;
	effect_id: number;
	colors: number;
}

export interface PrimaryGuild {
	identity_enabled: boolean | null;
	identity_guild_id: Snowflake | null;
	tag: string | null;
	badge: string | null;
}

export enum PublicUserEnum {
	username,
	discriminator,
	id,
	public_flags,
	avatar,
	accent_color,
	banner,
	bio,
	bot,
	premium_since,
	premium_type,
	theme_colors,
	pronouns,
	badge_ids,
}
export type PublicUserKeys = keyof typeof PublicUserEnum;

export enum PrivateUserEnum {
	flags,
	mfa_enabled,
	email,
	phone,
	verified,
	nsfw_allowed,
	premium,
	premium_type,
	purchased_flags,
	premium_usage_flags,
	disabled,
	settings,
	// locale
}

export type PrivateUserKeys = keyof typeof PrivateUserEnum | PublicUserKeys;

export const PublicUserProjection = Object.values(PublicUserEnum).filter((x) => typeof x === "string") as PublicUserKeys[];
export const PrivateUserProjection = [...PublicUserProjection, ...Object.values(PrivateUserEnum).filter((x) => typeof x === "string")] as PrivateUserKeys[];

// Private user data that should never get sent to the client
export type PublicUser = Pick<UserEntityPleaseRewriteThankYou, PublicUserKeys>;
export type PrivateUser = Pick<UserEntityPleaseRewriteThankYou, PrivateUserKeys>;

export interface UserPrivate extends Pick<UserEntityPleaseRewriteThankYou, PrivateUserKeys> {
	locale: string;
}

export const CUSTOM_USER_FLAG_OFFSET = BigInt(1) << BigInt(32);

// This causes a failure in openapi.js...?
export class UserFlags extends BitField {
	static FLAGS = {
		DISCORD_EMPLOYEE: BigInt(1) << BigInt(0),
		PARTNERED_SERVER_OWNER: BigInt(1) << BigInt(1),
		HYPESQUAD_EVENTS: BigInt(1) << BigInt(2),
		BUGHUNTER_LEVEL_1: BigInt(1) << BigInt(3),
		MFA_SMS: BigInt(1) << BigInt(4),
		PREMIUM_PROMO_DISMISSED: BigInt(1) << BigInt(5),
		HOUSE_BRAVERY: BigInt(1) << BigInt(6),
		HOUSE_BRILLIANCE: BigInt(1) << BigInt(7),
		HOUSE_BALANCE: BigInt(1) << BigInt(8),
		EARLY_SUPPORTER: BigInt(1) << BigInt(9),
		TEAM_USER: BigInt(1) << BigInt(10),
		TRUST_AND_SAFETY: BigInt(1) << BigInt(11),
		SYSTEM: BigInt(1) << BigInt(12),
		HAS_UNREAD_URGENT_MESSAGES: BigInt(1) << BigInt(13),
		BUGHUNTER_LEVEL_2: BigInt(1) << BigInt(14),
		UNDERAGE_DELETED: BigInt(1) << BigInt(15),
		VERIFIED_BOT: BigInt(1) << BigInt(16),
		EARLY_VERIFIED_BOT_DEVELOPER: BigInt(1) << BigInt(17),
		CERTIFIED_MODERATOR: BigInt(1) << BigInt(18),
		BOT_HTTP_INTERACTIONS: BigInt(1) << BigInt(19),
	};
}