1
0
mirror of https://github.com/spacebarchat/server.git synced 2024-11-05 10:22:31 +01:00

Move UserSettings to own entity

This commit is contained in:
TheArcaneBrony 2022-08-12 01:46:42 +02:00 committed by Madeline
parent d407d8ebe7
commit 803ab38fad
12 changed files with 2914 additions and 207 deletions

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@
"start:gateway": "node dist/gateway/start.js", "start:gateway": "node dist/gateway/start.js",
"build": "tsc -p .", "build": "tsc -p .",
"setup": "npm run build && npm run generate:schema", "setup": "npm run build && npm run generate:schema",
"generate:db": "node scripts/syncronise.js", "sync:db": "npm run build && node scripts/syncronise.js",
"generate:rights": "node scripts/rights.js", "generate:rights": "node scripts/rights.js",
"generate:schema": "node scripts/schema.js", "generate:schema": "node scripts/schema.js",
"generate:client": "node scripts/client.js", "generate:client": "node scripts/client.js",

View File

@ -7,7 +7,7 @@ const router = Router();
router.get("/", route({}), async (req: Request, res: Response) => { router.get("/", route({}), async (req: Request, res: Response) => {
const user = await User.findOneOrFail({ const user = await User.findOneOrFail({
where: { id: req.user_id }, where: { id: req.user_id },
select: ["settings"], relations: ["settings"],
}); });
return res.json(user.settings); return res.json(user.settings);
}); });
@ -21,10 +21,12 @@ router.patch(
const user = await User.findOneOrFail({ const user = await User.findOneOrFail({
where: { id: req.user_id, bot: false }, 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); res.json(user.settings);
}, },

View File

@ -18,9 +18,10 @@ import {
PrivateSessionProjection, PrivateSessionProjection,
MemberPrivateProjection, MemberPrivateProjection,
PresenceUpdateEvent, PresenceUpdateEvent,
UserSettings,
IdentifySchema,
DefaultUserGuildSettings, DefaultUserGuildSettings,
UserGuildSettings, UserGuildSettings,
IdentifySchema,
} from "@fosscord/util"; } from "@fosscord/util";
import { Send } from "../util/Send"; import { Send } from "../util/Send";
import { CLOSECODES, OPCODES } from "../util/Constants"; import { CLOSECODES, OPCODES } from "../util/Constants";
@ -60,7 +61,7 @@ export async function onIdentify(this: WebSocket, data: Payload) {
await Promise.all([ await Promise.all([
User.findOneOrFail({ User.findOneOrFail({
where: { id: this.user_id }, where: { id: this.user_id },
relations: ["relationships", "relationships.to"], relations: ["relationships", "relationships.to", "settings"],
select: [...PrivateUserProjection, "relationships"], select: [...PrivateUserProjection, "relationships"],
}), }),
ReadState.find({ where: { user_id: this.user_id } }), 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) return this.close(CLOSECODES.Authentication_failed);
if (!user.settings) {
user.settings = new UserSettings();
await user.settings.save();
}
if (!identify.intents) identify.intents = BigInt("0x6ffffffff"); if (!identify.intents) identify.intents = BigInt("0x6ffffffff");
this.intents = new Intents(identify.intents); this.intents = new Intents(identify.intents);

View File

@ -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 // '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 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 //TODO the member.user should only have these properties: avatar, discriminator, id, username

View File

@ -1,5 +1,6 @@
import { GuildDefaults } from "."; import { GuildDefaults, UserDefaults } from ".";
export class DefaultsConfiguration { export class DefaultsConfiguration {
guild: GuildDefaults = new GuildDefaults(); guild: GuildDefaults = new GuildDefaults();
user: UserDefaults = new UserDefaults();
} }

View File

@ -0,0 +1,5 @@
export class UserDefaults {
premium: boolean = false;
premium_type: number = 2;
verified: boolean = true;
}

View File

@ -1 +1,2 @@
export * from "./GuildDefaults"; export * from "./GuildDefaults";
export * from "./UserDefaults";

View File

@ -110,7 +110,7 @@ export class Guild extends BaseClass {
max_presences?: number; max_presences?: number;
@Column({ nullable: true }) @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 }) @Column({ nullable: true })
member_count?: number; member_count?: number;
@ -286,6 +286,10 @@ export class Guild extends BaseClass {
// only for developer portal // only for developer portal
permissions?: number; permissions?: number;
//new guild settings, 11/08/2022:
@Column({ nullable: true })
premium_progress_bar_enabled: boolean;
static async createGuild(body: { static async createGuild(body: {
name?: string; name?: string;
icon?: string | null; icon?: string | null;

View File

@ -6,11 +6,15 @@ import {
FindOneOptions, FindOneOptions,
JoinColumn, JoinColumn,
OneToMany, OneToMany,
OneToOne,
} from "typeorm"; } from "typeorm";
import { BaseClass } from "./BaseClass"; import { BaseClass } from "./BaseClass";
import { BitField } from "../util/BitField"; import { BitField } from "../util/BitField";
import { Relationship } from "./Relationship"; import { Relationship } from "./Relationship";
import { ConnectedAccount } from "./ConnectedAccount"; import { ConnectedAccount } from "./ConnectedAccount";
import { Member } from "./Member";
import { UserSettings } from "./UserSettings";
import { Session } from "./Session";
import { import {
Config, Config,
FieldErrors, FieldErrors,
@ -18,7 +22,6 @@ import {
trimSpecial, trimSpecial,
adjustEmail, adjustEmail,
} from ".."; } from "..";
import { Member, Session } from ".";
export enum PublicUserEnum { export enum PublicUserEnum {
username, username,
@ -46,7 +49,7 @@ export enum PrivateUserEnum {
purchased_flags, purchased_flags,
premium_usage_flags, premium_usage_flags,
disabled, disabled,
settings, // settings, // now a relation
// locale // locale
} }
export type PrivateUserKeys = keyof typeof PrivateUserEnum | PublicUserKeys; export type PrivateUserKeys = keyof typeof PrivateUserEnum | PublicUserKeys;
@ -89,67 +92,67 @@ export class User extends BaseClass {
phone?: string; // phone number of the user phone?: string; // phone number of the user
@Column({ select: false }) @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 }) @Column({ select: false })
mobile: boolean; // if the user has mobile app installed mobile: boolean = false; // if the user has mobile app installed
@Column() @Column()
premium: boolean; // if user bought individual premium premium: boolean = false; // if user bought individual premium
@Column() @Column()
premium_type: number; // individual premium level premium_type: number = 0; // individual premium level
@Column() @Column()
bot: boolean; // if user is bot bot: boolean = false; // if user is bot
@Column() @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() @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 }) @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 }) @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 }) @Column({ select: false, nullable: true })
totp_secret?: string; totp_secret?: string = "";
@Column({ nullable: true, select: false }) @Column({ nullable: true, select: false })
totp_last_ticket?: string; totp_last_ticket?: string = "";
@Column() @Column()
created_at: Date; // registration date created_at: Date = new Date(); // registration date
@Column({ nullable: true }) @Column({ nullable: true })
premium_since: Date; // premium date premium_since: Date = new Date(); // premium date
@Column({ select: false }) @Column({ select: false })
verified: boolean; // if the user is offically verified verified: boolean = true; // email is verified
@Column() @Column()
disabled: boolean; // if the account is disabled disabled: boolean = false; // if the account is disabled
@Column() @Column()
deleted: boolean; // if the user was deleted deleted: boolean = false; // if the user was deleted
@Column({ nullable: true, select: false }) @Column({ nullable: true, select: false })
email?: string; // email of the user email?: string; // email of the user
@Column() @Column()
flags: string; // UserFlags flags: string = "0"; // UserFlags // TODO: generate
@Column() @Column()
public_flags: number; public_flags: number = 0;
@Column() @Column()
purchased_flags: number; purchased_flags: number = 0;
@Column() @Column()
premium_usage_flags: number; premium_usage_flags: number = 0;
@Column({ type: "bigint" }) @Column({ type: "bigint" })
rights: string; // Rights rights: string; // Rights
@ -186,14 +189,19 @@ export class User extends BaseClass {
}; };
@Column({ type: "simple-array", select: false }) @Column({ type: "simple-array", select: false })
fingerprints: string[]; // array of fingerprints -> used to prevent multiple accounts fingerprints: string[] = []; // array of fingerprints -> used to prevent multiple accounts
@Column({ type: "simple-json", select: false }) @OneToOne(() => UserSettings, {
cascade: true,
orphanedRowAction: "delete",
eager: false
})
@JoinColumn()
settings: UserSettings; settings: UserSettings;
// workaround to prevent fossord-unaware clients from deleting settings not used by them // workaround to prevent fossord-unaware clients from deleting settings not used by them
@Column({ type: "simple-json", select: false }) @Column({ type: "simple-json", select: false })
extended_settings: string; extended_settings: string = "{}";
// TODO: I don't like this method? // TODO: I don't like this method?
validate() { validate() {
@ -327,40 +335,25 @@ export class User extends BaseClass {
req.language === "en" ? "en-US" : req.language || "en-US"; req.language === "en" ? "en-US" : req.language || "en-US";
const user = User.create({ const user = User.create({
created_at: new Date(),
username: username, username: username,
discriminator, discriminator,
id: Snowflake.generate(), 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, email: email,
rights: Config.get().register.defaultRights, rights: Config.get().register.defaultRights,
nsfw_allowed: true, // TODO: depending on age
public_flags: 0,
flags: "0", // TODO: generate
data: { data: {
hash: password, hash: password,
valid_tokens_since: new Date(), valid_tokens_since: new Date(),
}, },
settings: { ...defaultSettings, locale: language }, extended_settings: "{}",
purchased_flags: 5, // TODO: idk what the values for this are premium_type: Config.get().defaults.user.premium_type,
premium_usage_flags: 2, // TODO: idk what the values for this are premium: Config.get().defaults.user.premium,
extended_settings: "", // TODO: was {} verified: Config.get().defaults.user.verified,
fingerprints: [], settings: { ...new UserSettings(), locale: language }
}); });
user.validate(); user.validate();
await user.save(); await user.save();
await user.settings.save();
setImmediate(async () => { setImmediate(async () => {
if (Config.get().guild.autoJoin.enabled) { 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 const CUSTOM_USER_FLAG_OFFSET = BigInt(1) << BigInt(32);
export class UserFlags extends BitField { export class UserFlags extends BitField {

View File

@ -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
}

View File

@ -30,4 +30,5 @@ export * from "./VoiceState";
export * from "./Webhook"; export * from "./Webhook";
export * from "./ClientRelease"; export * from "./ClientRelease";
export * from "./BackupCodes"; export * from "./BackupCodes";
export * from "./Note"; export * from "./Note";
export * from "./UserSettings";