summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/api/routes/users/@me/settings.ts10
-rw-r--r--src/gateway/opcodes/Identify.ts9
-rw-r--r--src/gateway/opcodes/VoiceStateUpdate.ts6
-rw-r--r--src/util/config/types/DefaultsConfiguration.ts3
-rw-r--r--src/util/config/types/subconfigurations/defaults/UserDefaults.ts5
-rw-r--r--src/util/config/types/subconfigurations/defaults/index.ts1
-rw-r--r--src/util/entities/Guild.ts6
-rw-r--r--src/util/entities/User.ts175
-rw-r--r--src/util/entities/UserSettings.ts119
-rw-r--r--src/util/entities/index.ts3
10 files changed, 192 insertions, 145 deletions
diff --git a/src/api/routes/users/@me/settings.ts b/src/api/routes/users/@me/settings.ts
index 8fb86012..0fd8220a 100644
--- a/src/api/routes/users/@me/settings.ts
+++ b/src/api/routes/users/@me/settings.ts
@@ -7,7 +7,7 @@ const router = Router();
 router.get("/", route({}), async (req: Request, res: Response) => {
 	const user = await User.findOneOrFail({
 		where: { id: req.user_id },
-		select: ["settings"],
+		relations: ["settings"],
 	});
 	return res.json(user.settings);
 });
@@ -21,10 +21,12 @@ router.patch(
 
 		const user = await User.findOneOrFail({
 			where: { id: req.user_id, bot: false },
-			select: ["settings"]
+			relations: ["settings"]
 		});
-		user.settings = OrmUtils.mergeDeep(user.settings, body);
-		User.update({ id: user.id }, { settings: user.settings });
+
+		user.settings.assign(body);
+
+		user.settings.save();
 
 		res.json(user.settings);
 	},
diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts
index fc183286..5f569fee 100644
--- a/src/gateway/opcodes/Identify.ts
+++ b/src/gateway/opcodes/Identify.ts
@@ -18,9 +18,10 @@ import {
 	PrivateSessionProjection,
 	MemberPrivateProjection,
 	PresenceUpdateEvent,
+	UserSettings,
+	IdentifySchema,
 	DefaultUserGuildSettings,
 	UserGuildSettings,
-	IdentifySchema,
 } from "@fosscord/util";
 import { Send } from "../util/Send";
 import { CLOSECODES, OPCODES } from "../util/Constants";
@@ -60,7 +61,7 @@ export async function onIdentify(this: WebSocket, data: Payload) {
 		await Promise.all([
 			User.findOneOrFail({
 				where: { id: this.user_id },
-				relations: ["relationships", "relationships.to"],
+				relations: ["relationships", "relationships.to", "settings"],
 				select: [...PrivateUserProjection, "relationships"],
 			}),
 			ReadState.find({ where: { user_id: this.user_id } }),
@@ -105,6 +106,10 @@ export async function onIdentify(this: WebSocket, data: Payload) {
 		]);
 
 	if (!user) return this.close(CLOSECODES.Authentication_failed);
+	if (!user.settings) {
+		user.settings = new UserSettings();
+		await user.settings.save();
+	}
 
 	if (!identify.intents) identify.intents = BigInt("0x6ffffffff");
 	this.intents = new Intents(identify.intents);
diff --git a/src/gateway/opcodes/VoiceStateUpdate.ts b/src/gateway/opcodes/VoiceStateUpdate.ts
index 94cdef44..49d15425 100644
--- a/src/gateway/opcodes/VoiceStateUpdate.ts
+++ b/src/gateway/opcodes/VoiceStateUpdate.ts
@@ -62,7 +62,11 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) {
 	}
 
 	// 'Fix' for this one voice state error. TODO: Find out why this is sent
-	if (!voiceState.guild_id) return;
+	// It seems to be sent on client load, 
+	// so maybe its trying to find which server you were connected to before disconnecting, if any?
+	if (body.guild_id == null) {
+		return;
+	}
 
 	//TODO the member should only have these properties: hoisted_role, deaf, joined_at, mute, roles, user
 	//TODO the member.user should only have these properties: avatar, discriminator, id, username
diff --git a/src/util/config/types/DefaultsConfiguration.ts b/src/util/config/types/DefaultsConfiguration.ts
index c3171a01..9b02a590 100644
--- a/src/util/config/types/DefaultsConfiguration.ts
+++ b/src/util/config/types/DefaultsConfiguration.ts
@@ -1,5 +1,6 @@
-import { GuildDefaults } from ".";
+import { GuildDefaults, UserDefaults } from ".";
 
 export class DefaultsConfiguration {
     guild: GuildDefaults = new GuildDefaults();
+    user: UserDefaults = new UserDefaults();
 }
\ No newline at end of file
diff --git a/src/util/config/types/subconfigurations/defaults/UserDefaults.ts b/src/util/config/types/subconfigurations/defaults/UserDefaults.ts
new file mode 100644
index 00000000..4481c011
--- /dev/null
+++ b/src/util/config/types/subconfigurations/defaults/UserDefaults.ts
@@ -0,0 +1,5 @@
+export class UserDefaults {
+    premium: boolean = false;
+    premium_type: number = 2;
+    verified: boolean = true;
+}
\ No newline at end of file
diff --git a/src/util/config/types/subconfigurations/defaults/index.ts b/src/util/config/types/subconfigurations/defaults/index.ts
index f40c5cd2..50258d1c 100644
--- a/src/util/config/types/subconfigurations/defaults/index.ts
+++ b/src/util/config/types/subconfigurations/defaults/index.ts
@@ -1 +1,2 @@
 export * from "./GuildDefaults";
+export * from "./UserDefaults";
diff --git a/src/util/entities/Guild.ts b/src/util/entities/Guild.ts
index 169921be..b1471f16 100644
--- a/src/util/entities/Guild.ts
+++ b/src/util/entities/Guild.ts
@@ -110,7 +110,7 @@ export class Guild extends BaseClass {
 	max_presences?: number;
 
 	@Column({ nullable: true })
-	max_video_channel_users?: number; // ? default: 25, is this max 25 streaming or watching
+	max_video_channel_users?: number;
 
 	@Column({ nullable: true })
 	member_count?: number;
@@ -286,6 +286,10 @@ export class Guild extends BaseClass {
 	// only for developer portal
 	permissions?: number;
 
+	//new guild settings, 11/08/2022:
+	@Column({ nullable: true })
+	premium_progress_bar_enabled: boolean;
+
 	static async createGuild(body: {
 		name?: string;
 		icon?: string | null;
diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts
index ea726368..79ea6c3c 100644
--- a/src/util/entities/User.ts
+++ b/src/util/entities/User.ts
@@ -6,11 +6,15 @@ import {
 	FindOneOptions,
 	JoinColumn,
 	OneToMany,
+	OneToOne,
 } from "typeorm";
 import { BaseClass } from "./BaseClass";
 import { BitField } from "../util/BitField";
 import { Relationship } from "./Relationship";
 import { ConnectedAccount } from "./ConnectedAccount";
+import { Member } from "./Member";
+import { UserSettings } from "./UserSettings";
+import { Session } from "./Session";
 import {
 	Config,
 	FieldErrors,
@@ -18,7 +22,6 @@ import {
 	trimSpecial,
 	adjustEmail,
 } from "..";
-import { Member, Session } from ".";
 
 export enum PublicUserEnum {
 	username,
@@ -46,7 +49,7 @@ export enum PrivateUserEnum {
 	purchased_flags,
 	premium_usage_flags,
 	disabled,
-	settings,
+	// settings,	// now a relation
 	// locale
 }
 export type PrivateUserKeys = keyof typeof PrivateUserEnum | PublicUserKeys;
@@ -89,67 +92,67 @@ export class User extends BaseClass {
 	phone?: string; // phone number of the user
 
 	@Column({ select: false })
-	desktop: boolean; // if the user has desktop app installed
+	desktop: boolean = false; // if the user has desktop app installed
 
 	@Column({ select: false })
-	mobile: boolean; // if the user has mobile app installed
+	mobile: boolean = false; // if the user has mobile app installed
 
 	@Column()
-	premium: boolean; // if user bought individual premium
+	premium: boolean = false; // if user bought individual premium
 
 	@Column()
-	premium_type: number; // individual premium level
+	premium_type: number = 0; // individual premium level
 
 	@Column()
-	bot: boolean; // if user is bot
+	bot: boolean = false; // if user is bot
 
 	@Column()
-	bio: string; // short description of the user (max 190 chars -> should be configurable)
+	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
+	system: boolean = false; // 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)
+	nsfw_allowed: boolean = true; // if the user can do age-restricted actions (NSFW channels/guilds/commands) // TODO: depending on age
 
 	@Column({ select: false })
-	mfa_enabled: boolean; // if multi factor authentication is enabled
+	mfa_enabled: boolean = false; // if multi factor authentication is enabled
 
 	@Column({ select: false, nullable: true })
-	totp_secret?: string;
+	totp_secret?: string = "";
 
 	@Column({ nullable: true, select: false })
-	totp_last_ticket?: string;
+	totp_last_ticket?: string = "";
 
 	@Column()
-	created_at: Date; // registration date
+	created_at: Date = new Date(); // registration date
 
 	@Column({ nullable: true })
-	premium_since: Date; // premium date
+	premium_since: Date = new Date(); // premium date
 
 	@Column({ select: false })
-	verified: boolean; // if the user is offically verified
+	verified: boolean = true;	// email is verified
 
 	@Column()
-	disabled: boolean; // if the account is disabled
+	disabled: boolean = false; // if the account is disabled
 
 	@Column()
-	deleted: boolean; // if the user was deleted
+	deleted: boolean = false; // if the user was deleted
 
 	@Column({ nullable: true, select: false })
 	email?: string; // email of the user
 
 	@Column()
-	flags: string; // UserFlags
+	flags: string = "0"; // UserFlags // TODO: generate
 
 	@Column()
-	public_flags: number;
+	public_flags: number = 0;
 
 	@Column()
-	purchased_flags: number;
+	purchased_flags: number = 0;
 
 	@Column()
-	premium_usage_flags: number;
+	premium_usage_flags: number = 0;
 
 	@Column({ type: "bigint" })
 	rights: string; // Rights
@@ -186,14 +189,19 @@ export class User extends BaseClass {
 	};
 
 	@Column({ type: "simple-array", select: false })
-	fingerprints: string[]; // array of fingerprints -> used to prevent multiple accounts
-
-	@Column({ type: "simple-json", select: false })
+	fingerprints: string[] = []; // array of fingerprints -> used to prevent multiple accounts
+
+	@OneToOne(() => UserSettings, {
+		cascade: true,
+		orphanedRowAction: "delete",
+		eager: false
+	})
+	@JoinColumn()
 	settings: UserSettings;
 
 	// workaround to prevent fossord-unaware clients from deleting settings not used by them
 	@Column({ type: "simple-json", select: false })
-	extended_settings: string;
+	extended_settings: string = "{}";
 
 	// TODO: I don't like this method?
 	validate() {
@@ -327,40 +335,25 @@ export class User extends BaseClass {
 			req.language === "en" ? "en-US" : req.language || "en-US";
 
 		const user = User.create({
-			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().register.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: "", // TODO: was {}
-			fingerprints: [],
+			extended_settings: "{}",
+			premium_type: Config.get().defaults.user.premium_type,
+			premium: Config.get().defaults.user.premium,
+			verified: Config.get().defaults.user.verified,
+			settings: { ...new UserSettings(), locale: language }
 		});
 
 		user.validate();
 		await user.save();
+		await user.settings.save();
 
 		setImmediate(async () => {
 			if (Config.get().guild.autoJoin.enabled) {
@@ -374,94 +367,6 @@ export class User extends BaseClass {
 	}
 }
 
-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" | "light"; // 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 {
diff --git a/src/util/entities/UserSettings.ts b/src/util/entities/UserSettings.ts
new file mode 100644
index 00000000..b1a1fe68
--- /dev/null
+++ b/src/util/entities/UserSettings.ts
@@ -0,0 +1,119 @@
+import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
+import { BaseClassWithoutId } from "./BaseClass";
+
+@Entity("user_settings")
+export class UserSettings extends BaseClassWithoutId {
+    @PrimaryGeneratedColumn()
+	index: string;
+
+	@Column({ nullable: true })
+    afk_timeout: number = 3600;
+
+	@Column({ nullable: true })
+    allow_accessibility_detection: boolean = true;
+	
+    @Column({ nullable: true })
+    animate_emoji: boolean = true;
+	
+    @Column({ nullable: true })
+    animate_stickers: number = 0;
+	
+    @Column({ nullable: true })
+    contact_sync_enabled: boolean = false;
+	
+    @Column({ nullable: true })
+    convert_emoticons: boolean = false;
+	
+    @Column({ nullable: true, type: "simple-json" })
+    custom_status: CustomStatus | null = null;
+	
+    @Column({ nullable: true })
+    default_guilds_restricted: boolean = false;
+	
+    @Column({ nullable: true })
+    detect_platform_accounts: boolean = false;
+	
+    @Column({ nullable: true })
+    developer_mode: boolean = true;
+	
+    @Column({ nullable: true })
+    disable_games_tab: boolean = true;
+	
+    @Column({ nullable: true })
+    enable_tts_command: boolean = false;
+	
+    @Column({ nullable: true })
+    explicit_content_filter: number = 0;
+	
+    @Column({ nullable: true, type: "simple-json" })
+    friend_source_flags: FriendSourceFlags = { all: true };
+	
+    @Column({ nullable: true })
+    gateway_connected: boolean = false;
+	
+    @Column({ nullable: true })
+    gif_auto_play: boolean = false;
+	
+    @Column({ nullable: true, type: "simple-json" })
+    guild_folders: GuildFolder[] = []; // every top guild is displayed as a "folder"
+	
+    @Column({ nullable: true, type: "simple-json" })
+    guild_positions: string[] = []; // guild ids ordered by position
+	
+    @Column({ nullable: true })
+    inline_attachment_media: boolean = true;
+	
+    @Column({ nullable: true })
+    inline_embed_media: boolean = true;
+	
+    @Column({ nullable: true })
+    locale: string = "en-US"; // en_US
+	
+    @Column({ nullable: true })
+    message_display_compact: boolean = false;
+	
+    @Column({ nullable: true })
+    native_phone_integration_enabled: boolean = true;
+	
+    @Column({ nullable: true })
+    render_embeds: boolean = true;
+	
+    @Column({ nullable: true })
+    render_reactions: boolean = true;
+	
+    @Column({ nullable: true, type: "simple-json" })
+    restricted_guilds: string[] = [];
+	
+    @Column({ nullable: true })
+    show_current_game: boolean = true;
+	
+    @Column({ nullable: true })
+    status: "online" | "offline" | "dnd" | "idle" | "invisible" = "online";
+	
+    @Column({ nullable: true })
+    stream_notifications_enabled: boolean = false;
+	
+    @Column({ nullable: true })
+    theme: "dark" | "light" = "dark"; // dark
+	
+    @Column({ nullable: true })
+    timezone_offset: number = 0; // e.g -60
+}
+
+interface CustomStatus {
+    emoji_id?: string;
+    emoji_name?: string;
+    expires_at?: number;
+    text?: string;
+}
+
+interface GuildFolder {
+    color: number;
+    guild_ids: string[];
+    id: number;
+    name: string;
+}
+
+interface FriendSourceFlags { 
+    all: boolean
+}
\ No newline at end of file
diff --git a/src/util/entities/index.ts b/src/util/entities/index.ts
index 04f3bace..7b24e21c 100644
--- a/src/util/entities/index.ts
+++ b/src/util/entities/index.ts
@@ -30,4 +30,5 @@ export * from "./VoiceState";
 export * from "./Webhook";
 export * from "./ClientRelease";
 export * from "./BackupCodes";
-export * from "./Note";
\ No newline at end of file
+export * from "./Note";
+export * from "./UserSettings";