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.ts243
1 files changed, 243 insertions, 0 deletions
diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts
new file mode 100644

index 00000000..39f654be --- /dev/null +++ b/util/src/entities/User.ts
@@ -0,0 +1,243 @@ +import { Column, Entity, FindOneOptions, JoinColumn, ManyToMany, OneToMany, RelationId } from "typeorm"; +import { BaseClass } from "./BaseClass"; +import { BitField } from "../util/BitField"; +import { Relationship } from "./Relationship"; +import { ConnectedAccount } from "./ConnectedAccount"; +import { HTTPError } from "lambert-server"; +import { Channel } from "./Channel"; + +type PublicUserKeys = + | "username" + | "discriminator" + | "id" + | "public_flags" + | "avatar" + | "accent_color" + | "banner" + | "bio" + | "bot"; +export const PublicUserProjection: PublicUserKeys[] = [ + "username", + "discriminator", + "id", + "public_flags", + "avatar", + "accent_color", + "banner", + "bio", + "bot", +]; + +// Private user data that should never get sent to the client +export type PublicUser = Pick<User, PublicUserKeys>; + +@Entity("users") +export class User extends BaseClass { + @Column() + username: string; // username max length 32, min 2 (should be configurable) + + @Column() + discriminator: string; // #0001 4 digit long string from #0001 - #9999 + + setDiscriminator(val: string) { + const number = Number(val); + if (isNaN(number)) throw new Error("invalid discriminator"); + if (number <= 0 || number > 10000) throw new Error("discriminator must be between 1 and 9999"); + 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 }) + phone?: string; // phone number of the user + + @Column() + desktop: boolean; // if the user has desktop app installed + + @Column() + mobile: boolean; // if the user has mobile app installed + + @Column() + premium: boolean; // if user bought nitro + + @Column() + premium_type: number; // nitro 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 sents this field type true, if the generated message comes from a system generated author + + @Column() + nsfw_allowed: boolean; // if the user is older than 18 (resp. Config) + + @Column() + mfa_enabled: boolean; // if multi factor authentication is enabled + + @Column() + created_at: Date = new Date(); // registration date + + @Column() + 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 }) + email?: string; // email of the user + + @Column() + flags: string; // UserFlags + + @Column() + public_flags: string; + + @JoinColumn({ name: "relationship_ids" }) + @OneToMany(() => Relationship, (relationship: Relationship) => relationship.user, { cascade: true }) + relationships: Relationship[]; + + @JoinColumn({ name: "connected_account_ids" }) + @OneToMany(() => ConnectedAccount, (account: ConnectedAccount) => account.user) + 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" }) + fingerprints: string[]; // array of fingerprints -> used to prevent multiple accounts + + @Column({ type: "simple-json" }) + settings: UserSettings; + + static async getPublicUser(user_id: string, opts?: FindOneOptions<User>) { + const user = await User.findOne( + { id: user_id }, + { + ...opts, + select: [...PublicUserProjection, ...(opts?.select || [])], + } + ); + if (!user) throw new HTTPError("User not found", 404); + return user; + } +} + +export const defaultSettings: UserSettings = { + afk_timeout: 300, + allow_accessibility_detection: true, + animate_emoji: true, + animate_stickers: 0, + contact_sync_enabled: false, + convert_emoticons: false, + custom_status: { + emoji_id: undefined, + emoji_name: undefined, + expires_at: undefined, + text: undefined, + }, + default_guilds_restricted: false, + detect_platform_accounts: true, + developer_mode: false, + disable_games_tab: false, + enable_tts_command: true, + 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", + message_display_compact: false, + native_phone_integration_enabled: true, + render_embeds: true, + render_reactions: true, + restricted_guilds: [], + show_current_game: true, + status: "offline", + stream_notifications_enabled: true, + theme: "dark", + timezone_offset: 0, + // timezone_offset: // TODO: timezone from request +}; + +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; + }; + 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"; + stream_notifications_enabled: boolean; + theme: "dark" | "white"; // dark + timezone_offset: number; // e.g -60 +} + +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), + 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), + SYSTEM: BigInt(1) << BigInt(12), + BUGHUNTER_LEVEL_2: BigInt(1) << BigInt(14), + VERIFIED_BOT: BigInt(1) << BigInt(16), + EARLY_VERIFIED_BOT_DEVELOPER: BigInt(1) << BigInt(17), + }; +}