summary refs log tree commit diff
path: root/util/src/entities/User.ts
diff options
context:
space:
mode:
Diffstat (limited to 'util/src/entities/User.ts')
-rw-r--r--util/src/entities/User.ts437
1 files changed, 0 insertions, 437 deletions
diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts
deleted file mode 100644
index 35aeea52..00000000
--- a/util/src/entities/User.ts
+++ /dev/null
@@ -1,437 +0,0 @@
-import { Column, Entity, FindOneOptions, JoinColumn, OneToMany } from "typeorm";
-import { BaseClass } from "./BaseClass";
-import { BitField } from "../util/BitField";
-import { Relationship } from "./Relationship";
-import { ConnectedAccount } from "./ConnectedAccount";
-import { Config, FieldErrors, Snowflake, trimSpecial, BannedWords, adjustEmail } from "..";
-import { Member, Session } from ".";
-
-export enum PublicUserEnum {
-	username,
-	discriminator,
-	id,
-	public_flags,
-	avatar,
-	accent_color,
-	banner,
-	bio,
-	bot,
-	premium_since,
-}
-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<User, PublicUserKeys>;
-
-export interface UserPublic extends Pick<User, PublicUserKeys> { }
-
-export interface UserPrivate extends Pick<User, PrivateUserKeys> {
-	locale: string;
-}
-
-@Entity("users")
-export class User extends BaseClass {
-	@Column()
-	username: string; // username max length 32, min 2 (should be configurable)
-
-	setUsername(val: string) {
-		if (BannedWords.find(val)) throw FieldErrors({ username: { message: "Bad username", code: "INVALID_USERNAME" } });
-		this.username = val;
-	}
-
-	@Column()
-	discriminator: string; // opaque string: 4 digits on discord.com
-
-	setDiscriminator(val: string) {
-		const number = Number(val);
-		if (val.length > 4) throw new Error("invalid discriminator");
-		if (isNaN(number)) throw new Error("invalid discriminator");
-		if (number <= 0 || number >= 10000) throw new Error("discriminator must be between 1 and 9999");
-		val = Number(val).toString();
-		this.discriminator = val.toString().padStart(4, "0");
-	}
-
-	@Column({ nullable: true })
-	avatar?: string; // hash of the user avatar
-
-	@Column({ nullable: true })
-	accent_color?: number; // banner color of user
-
-	@Column({ nullable: true })
-	banner?: string; // hash of the user banner
-
-	@Column({ nullable: true, select: false })
-	phone?: string; // phone number of the user
-
-	@Column({ select: false })
-	desktop: boolean; // if the user has desktop app installed
-
-	@Column({ select: false })
-	mobile: boolean; // if the user has mobile app installed
-
-	@Column()
-	premium: boolean; // if user bought individual premium
-
-	@Column()
-	premium_type: number; // individual premium level
-
-	@Column()
-	bot: boolean; // if user is bot
-
-	@Column()
-	bio: string; // short description of the user (max 190 chars -> should be configurable)
-
-	@Column()
-	system: boolean; // shouldn't be used, the api sends this field type true, if the generated message comes from a system generated author
-
-	@Column({ select: false })
-	nsfw_allowed: boolean; // if the user can do age-restricted actions (NSFW channels/guilds/commands)
-
-	@Column({ select: false })
-	mfa_enabled: boolean; // if multi factor authentication is enabled
-
-	@Column({ select: false, nullable: true })
-	totp_secret?: string;
-
-	@Column({ nullable: true, select: false })
-	totp_last_ticket?: string;
-
-	@Column()
-	created_at: Date; // registration date
-
-	@Column({ nullable: true })
-	premium_since: Date; // premium date
-
-	@Column({ select: false })
-	verified: boolean; // if the user is offically verified
-
-	@Column()
-	disabled: boolean; // if the account is disabled
-
-	@Column()
-	deleted: boolean; // if the user was deleted
-
-	@Column({ nullable: true, select: false })
-	email?: string; // email of the user
-
-	setEmail(val?: string) {
-		val = adjustEmail(val);
-		if (!val) throw FieldErrors({ email: { message: "Invalid email", code: "EMAIL_INVALID" } });
-		if (!val.match(/([a-z\d.-]{3,})@([a-z\d.-]+).([a-z]{2,})/g)) throw FieldErrors({ email: { message: "Invalid email", code: "EMAIL_INVALID" } });
-		this.email = val;
-	}
-
-	@Column()
-	flags: string; // UserFlags
-
-	@Column()
-	public_flags: number;
-
-	@Column()
-	purchased_flags: number;
-
-	@Column()
-	premium_usage_flags: number;
-
-	@Column({ type: "bigint" })
-	rights: string; // Rights
-
-	@OneToMany(() => Session, (session: Session) => session.user)
-	sessions: Session[];
-
-	@JoinColumn({ name: "relationship_ids" })
-	@OneToMany(() => Relationship, (relationship: Relationship) => relationship.from, {
-		cascade: true,
-		orphanedRowAction: "delete",
-	})
-	relationships: Relationship[];
-
-	@JoinColumn({ name: "connected_account_ids" })
-	@OneToMany(() => ConnectedAccount, (account: ConnectedAccount) => account.user, {
-		cascade: true,
-		orphanedRowAction: "delete",
-	})
-	connected_accounts: ConnectedAccount[];
-
-	@Column({ type: "simple-json", select: false })
-	data: {
-		valid_tokens_since: Date; // all tokens with a previous issue date are invalid
-		hash?: string; // hash of the password, salt is saved in password (bcrypt)
-	};
-
-	@Column({ type: "simple-array", select: false })
-	fingerprints: string[]; // array of fingerprints -> used to prevent multiple accounts
-
-	@Column({ type: "simple-json", select: false })
-	settings: UserSettings;
-
-	// workaround to prevent fossord-unaware clients from deleting settings not used by them
-	@Column({ type: "simple-json", select: false })
-	extended_settings: string;
-
-	toPublicUser() {
-		const user: any = {};
-		PublicUserProjection.forEach((x) => {
-			user[x] = this[x];
-		});
-		return user as PublicUser;
-	}
-
-	static async getPublicUser(user_id: string, opts?: FindOneOptions<User>) {
-		return await User.findOneOrFail(
-			{ id: user_id },
-			{
-				...opts,
-				select: [...PublicUserProjection, ...(opts?.select || [])],
-			}
-		);
-	}
-
-	private static async generateDiscriminator(username: string): Promise<string | undefined> {
-		if (Config.get().register.incrementingDiscriminators) {
-			// discriminator will be incrementally generated
-
-			// First we need to figure out the currently highest discrimnator for the given username and then increment it
-			const users = await User.find({ where: { username }, select: ["discriminator"] });
-			const highestDiscriminator = Math.max(0, ...users.map((u) => Number(u.discriminator)));
-
-			const discriminator = highestDiscriminator + 1;
-			if (discriminator >= 10000) {
-				return undefined;
-			}
-
-			return discriminator.toString().padStart(4, "0");
-		} else {
-			// discriminator will be randomly generated
-
-			// randomly generates a discriminator between 1 and 9999 and checks max five times if it already exists
-			// TODO: is there any better way to generate a random discriminator only once, without checking if it already exists in the database?
-			for (let tries = 0; tries < 5; tries++) {
-				const discriminator = Math.randomIntBetween(1, 9999).toString().padStart(4, "0");
-				const exists = await User.findOne({ where: { discriminator, username: username }, select: ["id"] });
-				if (!exists) return discriminator;
-			}
-
-			return undefined;
-		}
-	}
-
-	static async register({
-		email,
-		username,
-		password,
-		date_of_birth,
-		req,
-	}: {
-		username: string;
-		password?: string;
-		email?: string;
-		date_of_birth?: Date; // "2000-04-03"
-		req?: any;
-	}) {
-		// trim special uf8 control characters -> Backspace, Newline, ...
-		username = trimSpecial(username);
-
-		const discriminator = await User.generateDiscriminator(username);
-		if (!discriminator) {
-			// We've failed to generate a valid and unused discriminator
-			throw FieldErrors({
-				username: {
-					code: "USERNAME_TOO_MANY_USERS",
-					message: req.t("auth:register.USERNAME_TOO_MANY_USERS"),
-				},
-			});
-		}
-
-		// TODO: save date_of_birth
-		// appearently discord doesn't save the date of birth and just calculate if nsfw is allowed
-		// if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false
-		const language = req.language === "en" ? "en-US" : req.language || "en-US";
-
-		const user = new User({
-			created_at: new Date(),
-			username: username,
-			discriminator,
-			id: Snowflake.generate(),
-			bot: false,
-			system: false,
-			premium_since: new Date(),
-			desktop: false,
-			mobile: false,
-			premium: true,
-			premium_type: 2,
-			bio: "",
-			mfa_enabled: false,
-			verified: true,
-			disabled: false,
-			deleted: false,
-			email: email,
-			rights: Config.get().security.defaultRights,
-			nsfw_allowed: true, // TODO: depending on age
-			public_flags: "0",
-			flags: "0", // TODO: generate
-			data: {
-				hash: password,
-				valid_tokens_since: new Date(),
-			},
-			settings: { ...defaultSettings, locale: language },
-			purchased_flags: 5, // TODO: idk what the values for this are
-			premium_usage_flags: 2,  // TODO: idk what the values for this are
-			extended_settings: {},
-			fingerprints: [],
-			notes: {},
-		});
-
-		await user.save();
-
-		setImmediate(async () => {
-			if (Config.get().guild.autoJoin.enabled) {
-				for (const guild of Config.get().guild.autoJoin.guilds || []) {
-					await Member.addToGuild(user.id, guild).catch((e) => { });
-				}
-			}
-		});
-
-		return user;
-	}
-}
-
-export const defaultSettings: UserSettings = {
-	afk_timeout: 3600,
-	allow_accessibility_detection: true,
-	animate_emoji: true,
-	animate_stickers: 0,
-	contact_sync_enabled: false,
-	convert_emoticons: false,
-	custom_status: null,
-	default_guilds_restricted: false,
-	detect_platform_accounts: false,
-	developer_mode: true,
-	disable_games_tab: true,
-	enable_tts_command: false,
-	explicit_content_filter: 0,
-	friend_source_flags: { all: true },
-	gateway_connected: false,
-	gif_auto_play: true,
-	guild_folders: [],
-	guild_positions: [],
-	inline_attachment_media: true,
-	inline_embed_media: true,
-	locale: "en-US",
-	message_display_compact: false,
-	native_phone_integration_enabled: true,
-	render_embeds: true,
-	render_reactions: true,
-	restricted_guilds: [],
-	show_current_game: true,
-	status: "online",
-	stream_notifications_enabled: false,
-	theme: "dark",
-	timezone_offset: 0, // TODO: timezone from request
-
-	banner_color: null,
-	friend_discovery_flags: 0,
-	view_nsfw_guilds: true,
-	passwordless: false,
-};
-
-export interface UserSettings {
-	afk_timeout: number;
-	allow_accessibility_detection: boolean;
-	animate_emoji: boolean;
-	animate_stickers: number;
-	contact_sync_enabled: boolean;
-	convert_emoticons: boolean;
-	custom_status: {
-		emoji_id?: string;
-		emoji_name?: string;
-		expires_at?: number;
-		text?: string;
-	} | null;
-	default_guilds_restricted: boolean;
-	detect_platform_accounts: boolean;
-	developer_mode: boolean;
-	disable_games_tab: boolean;
-	enable_tts_command: boolean;
-	explicit_content_filter: number;
-	friend_source_flags: { all: boolean; };
-	gateway_connected: boolean;
-	gif_auto_play: boolean;
-	// every top guild is displayed as a "folder"
-	guild_folders: {
-		color: number;
-		guild_ids: string[];
-		id: number;
-		name: string;
-	}[];
-	guild_positions: string[]; // guild ids ordered by position
-	inline_attachment_media: boolean;
-	inline_embed_media: boolean;
-	locale: string; // en_US
-	message_display_compact: boolean;
-	native_phone_integration_enabled: boolean;
-	render_embeds: boolean;
-	render_reactions: boolean;
-	restricted_guilds: string[];
-	show_current_game: boolean;
-	status: "online" | "offline" | "dnd" | "idle" | "invisible";
-	stream_notifications_enabled: boolean;
-	theme: "dark" | "white"; // dark
-	timezone_offset: number; // e.g -60
-	banner_color: string | null;
-	friend_discovery_flags: number;
-	view_nsfw_guilds: boolean;
-	passwordless: boolean;
-}
-
-export const CUSTOM_USER_FLAG_OFFSET = BigInt(1) << BigInt(32);
-
-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),
-	};
-}