From 1e5ed285141de779be3df51b7737400a57e12a8d Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Tue, 24 Aug 2021 16:34:46 +0200 Subject: :sparkles: typeorm entities --- util/src/entities/Guild.ts | 179 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 util/src/entities/Guild.ts (limited to 'util/src/entities/Guild.ts') diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts new file mode 100644 index 00000000..d46d31bc --- /dev/null +++ b/util/src/entities/Guild.ts @@ -0,0 +1,179 @@ +import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToOne } from "typeorm"; +import { BaseClass } from "./BaseClass"; +import { Channel } from "./Channel"; +import { Emoji } from "./Emoji"; +import { Invite } from "./Invite"; +import { Member } from "./Member"; +import { Role } from "./Role"; +import { User } from "./User"; +import { VoiceState } from "./VoiceState"; + +@Entity("guilds") +export class Guild extends BaseClass { + @Column() + afk_channel_id?: string; + + @JoinColumn({ name: "afk_channel_id" }) + @ManyToOne(() => Channel, (channel: Channel) => channel.id) + afk_channel?: Channel; + + @Column() + afk_timeout?: number; + + // * commented out -> use owner instead + // application id of the guild creator if it is bot-created + // @Column() + // application?: string; + + @Column() + banner?: string; + + @Column() + default_message_notifications?: number; + + @Column() + description?: string; + + @Column() + discovery_splash?: string; + + @Column() + explicit_content_filter?: number; + + @Column("simple-array") + features: string[]; + + @Column() + icon?: string; + + @Column() + large?: boolean; + + @Column() + max_members?: number; // e.g. default 100.000 + + @Column() + max_presences?: number; + + @Column() + max_video_channel_users?: number; // ? default: 25, is this max 25 streaming or watching + + @Column() + member_count?: number; + + @Column() + presence_count?: number; // users online + + @Column("simple-array") + member_ids: string[]; + + @JoinColumn({ name: "member_ids" }) + @ManyToMany(() => Member, (member: Member) => member.id) + members: Member[]; + + @Column("simple-array") + role_ids: string[]; + + @JoinColumn({ name: "role_ids" }) + @ManyToMany(() => Role, (role: Role) => role.id) + roles: Role[]; + + @Column("simple-array") + channel_ids: string[]; + + @JoinColumn({ name: "channel_ids" }) + @ManyToMany(() => Channel, (channel: Channel) => channel.id) + channels: Channel[]; + + @Column("simple-array") + emoji_ids: string[]; + + @JoinColumn({ name: "emoji_ids" }) + @ManyToMany(() => Emoji, (emoji: Emoji) => emoji.id) + emojis: Emoji[]; + + @Column("simple-array") + voice_state_ids: string[]; + + @JoinColumn({ name: "voice_state_ids" }) + @ManyToMany(() => VoiceState, (voicestate: VoiceState) => voicestate.id) + voice_states: VoiceState[]; + + @Column() + mfa_level?: number; + + @Column() + name: string; + + @Column() + owner_id: string; + + @JoinColumn({ name: "owner_id" }) + @ManyToOne(() => User, (user: User) => user.id) + owner: User; + + @Column() + preferred_locale?: string; // only community guilds can choose this + + @Column() + premium_subscription_count?: number; + + @Column() + premium_tier?: number; // nitro boost level + + @JoinColumn({ name: "public_updates_channel_id" }) + @ManyToOne(() => Channel, (channel: Channel) => channel.id) + public_updates_channel?: Channel; + + @Column() + rules_channel_id?: string; + + @JoinColumn({ name: "rules_channel_id" }) + @ManyToOne(() => Channel, (channel: Channel) => channel.id) + rules_channel?: string; + + @Column() + region?: string; + + @Column() + splash?: string; + + @Column() + system_channel_id?: string; + + @JoinColumn({ name: "system_channel_id" }) + @ManyToMany(() => Channel, (channel: Channel) => channel.id) + system_channel?: Channel; + + @Column() + system_channel_flags?: number; + + @Column() + unavailable?: boolean; + + @JoinColumn({ name: "vanity_url_code" }) + @OneToOne(() => Invite, (invite: Invite) => invite.code) + vanity_url?: Invite; + + @Column() + verification_level?: number; + + @Column("simple-json") + welcome_screen: { + enabled: boolean; + description: string; + welcome_channels: { + description: string; + emoji_id?: string; + emoji_name: string; + channel_id: string; + }[]; + }; + + @JoinColumn({ name: "widget_channel_id" }) + @ManyToOne(() => Channel, (channel: Channel) => channel.id) + widget_channel?: Channel; + + @Column() + widget_enabled?: boolean; +} -- cgit 1.5.1 From 18deb1e4b0ea5b709a854966166077faa537ebef Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Thu, 26 Aug 2021 20:48:09 +0200 Subject: :sparkles: use RelationId --- util/src/entities/Application.ts | 6 +- util/src/entities/AuditLog.ts | 6 +- util/src/entities/Ban.ts | 8 +- util/src/entities/Channel.ts | 12 +- util/src/entities/Config.ts | 274 ++++++++++++++++++++++++++++++++++++++ util/src/entities/Emoji.ts | 4 +- util/src/entities/Guild.ts | 29 ++-- util/src/entities/Invite.ts | 12 +- util/src/entities/Member.ts | 6 +- util/src/entities/Message.ts | 29 ++-- util/src/entities/RateLimit.ts | 4 +- util/src/entities/ReadState.ts | 9 +- util/src/entities/Relationship.ts | 4 +- util/src/entities/Role.ts | 4 +- util/src/entities/Team.ts | 6 +- util/src/entities/TeamMember.ts | 6 +- util/src/entities/Template.ts | 8 +- util/src/entities/User.ts | 41 +++++- util/src/entities/VoiceState.ts | 11 +- util/src/entities/Webhook.ts | 46 +++++-- util/src/entities/index.ts | 1 + 21 files changed, 444 insertions(+), 82 deletions(-) create mode 100644 util/src/entities/Config.ts (limited to 'util/src/entities/Guild.ts') diff --git a/util/src/entities/Application.ts b/util/src/entities/Application.ts index 64b5d2e2..90d0f056 100644 --- a/util/src/entities/Application.ts +++ b/util/src/entities/Application.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Team } from "./Team"; @@ -38,14 +38,14 @@ export class Application extends BaseClass { @Column() verify_key: string; - @Column() + @RelationId((application: Application) => application.team) team_id: string; @JoinColumn({ name: "team_id" }) @ManyToOne(() => Team, (team: Team) => team.id) team?: Team; - @Column() + @RelationId((application: Application) => application.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) diff --git a/util/src/entities/AuditLog.ts b/util/src/entities/AuditLog.ts index 53e99261..2195771b 100644 --- a/util/src/entities/AuditLog.ts +++ b/util/src/entities/AuditLog.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { ChannelPermissionOverwrite } from "./Channel"; import { User } from "./User"; @@ -43,14 +43,14 @@ export enum AuditLogEvents { @Entity("audit_logs") export class AuditLogEntry extends BaseClass { - @Column() + @RelationId((auditlog: AuditLogEntry) => auditlog.target) target_id: string; @JoinColumn({ name: "user_id" }) @ManyToOne(() => User, (user: User) => user.id) target?: User; - @Column() + @RelationId((auditlog: AuditLogEntry) => auditlog.user) user_id: string; @JoinColumn({ name: "user_id" }) diff --git a/util/src/entities/Ban.ts b/util/src/entities/Ban.ts index ceea4a05..2553e886 100644 --- a/util/src/entities/Ban.ts +++ b/util/src/entities/Ban.ts @@ -1,25 +1,25 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { User } from "./User"; @Entity("bans") export class Ban extends BaseClass { - @Column() + @RelationId((ban: Ban) => ban.user) user_id: string; @JoinColumn({ name: "user_id" }) @ManyToOne(() => User, (user: User) => user.id) user: User; - @Column() + @RelationId((ban: Ban) => ban.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) @ManyToOne(() => Guild, (guild: Guild) => guild.id) guild: Guild; - @Column() + @RelationId((ban: Ban) => ban.executor) executor_id: string; @JoinColumn({ name: "executor_id" }) diff --git a/util/src/entities/Channel.ts b/util/src/entities/Channel.ts index 5845607a..d26f1054 100644 --- a/util/src/entities/Channel.ts +++ b/util/src/entities/Channel.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Message } from "./Message"; @@ -25,35 +25,35 @@ export class Channel extends BaseClass { @Column({ type: "simple-enum", enum: ChannelType }) type: ChannelType; - @Column("simple-array") + @RelationId((channel: Channel) => channel.recipients) recipient_ids: string[]; @JoinColumn({ name: "recipient_ids" }) @ManyToMany(() => User, (user: User) => user.id) recipients?: User[]; - @Column() + @RelationId((channel: Channel) => channel.last_message) last_message_id: string; @JoinColumn({ name: "last_message_id" }) @ManyToOne(() => Message, (message: Message) => message.id) last_message?: Message; - @Column() + @RelationId((channel: Channel) => channel.guild) guild_id?: string; @JoinColumn({ name: "guild_id" }) @ManyToOne(() => Guild, (guild: Guild) => guild.id) guild: Guild; - @Column() + @RelationId((channel: Channel) => channel.parent) parent_id: string; @JoinColumn({ name: "parent_id" }) @ManyToOne(() => Channel, (channel: Channel) => channel.id) parent?: Channel; - @Column() + @RelationId((channel: Channel) => channel.owner) owner_id: string; @JoinColumn({ name: "owner_id" }) diff --git a/util/src/entities/Config.ts b/util/src/entities/Config.ts new file mode 100644 index 00000000..60d84958 --- /dev/null +++ b/util/src/entities/Config.ts @@ -0,0 +1,274 @@ +import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Snowflake } from "../util"; +import { BaseClass } from "./BaseClass"; +import crypto from "crypto"; + +@Entity("config") +export class ConfigEntity extends BaseClass { + @Column("simple-json") + value: ConfigValue; +} + +export interface RateLimitOptions { + bot?: number; + count: number; + window: number; + onyIp?: boolean; +} + +export interface Region { + id: string; + name: string; + vip: boolean; + custom: boolean; + deprecated: boolean; + optimal: boolean; +} + +export interface KafkaBroker { + ip: string; + port: number; +} + +export interface ConfigValue { + gateway: { + endpointClient: string | null; + endpoint: string | null; + }; + cdn: { + endpointClient: string | null; + endpoint: string | null; + }; + general: { + instance_id: string; + }; + permissions: { + user: { + createGuilds: boolean; + }; + }; + limits: { + user: { + maxGuilds: number; + maxUsername: number; + maxFriends: number; + }; + guild: { + maxRoles: number; + maxMembers: number; + maxChannels: number; + maxChannelsInCategory: number; + hideOfflineMember: number; + }; + message: { + maxCharacters: number; + maxTTSCharacters: number; + maxReactions: number; + maxAttachmentSize: number; + maxBulkDelete: number; + }; + channel: { + maxPins: number; + maxTopic: number; + }; + rate: { + ip: Omit; + global: RateLimitOptions; + error: RateLimitOptions; + routes: { + guild: RateLimitOptions; + webhook: RateLimitOptions; + channel: RateLimitOptions; + auth: { + login: RateLimitOptions; + register: RateLimitOptions; + }; + // TODO: rate limit configuration for all routes + }; + }; + }; + security: { + autoUpdate: boolean | number; + requestSignature: string; + jwtSecret: string; + forwadedFor: string | null; // header to get the real user ip address + captcha: { + enabled: boolean; + service: "recaptcha" | "hcaptcha" | null; // TODO: hcaptcha, custom + sitekey: string | null; + secret: string | null; + }; + ipdataApiKey: string | null; + }; + login: { + requireCaptcha: boolean; + }; + register: { + email: { + necessary: boolean; // we have to use necessary instead of required as the cli tool uses json schema and can't use required + allowlist: boolean; + blocklist: boolean; + domains: string[]; + }; + dateOfBirth: { + necessary: boolean; + minimum: number; // in years + }; + requireCaptcha: boolean; + requireInvite: boolean; + allowNewRegistration: boolean; + allowMultipleAccounts: boolean; + blockProxies: boolean; + password: { + minLength: number; + minNumbers: number; + minUpperCase: number; + minSymbols: number; + }; + }; + regions: { + default: string; + available: Region[]; + }; + rabbitmq: { + host: string | null; + }; + kafka: { + brokers: KafkaBroker[] | null; + }; +} + +export const DefaultConfigOptions: ConfigValue = { + gateway: { + endpointClient: null, + endpoint: null, + }, + cdn: { + endpointClient: null, + endpoint: null, + }, + general: { + instance_id: Snowflake.generate(), + }, + permissions: { + user: { + createGuilds: true, + }, + }, + limits: { + user: { + maxGuilds: 100, + maxUsername: 32, + maxFriends: 1000, + }, + guild: { + maxRoles: 250, + maxMembers: 250000, + maxChannels: 500, + maxChannelsInCategory: 50, + hideOfflineMember: 1000, + }, + message: { + maxCharacters: 2000, + maxTTSCharacters: 200, + maxReactions: 20, + maxAttachmentSize: 8388608, + maxBulkDelete: 100, + }, + channel: { + maxPins: 50, + maxTopic: 1024, + }, + rate: { + ip: { + count: 500, + window: 5, + }, + global: { + count: 20, + window: 5, + bot: 250, + }, + error: { + count: 10, + window: 5, + }, + routes: { + guild: { + count: 5, + window: 5, + }, + webhook: { + count: 5, + window: 20, + }, + channel: { + count: 5, + window: 20, + }, + auth: { + login: { + count: 5, + window: 60, + }, + register: { + count: 2, + window: 60 * 60 * 12, + }, + }, + }, + }, + }, + security: { + autoUpdate: true, + requestSignature: crypto.randomBytes(32).toString("base64"), + jwtSecret: crypto.randomBytes(256).toString("base64"), + forwadedFor: null, + // forwadedFor: "X-Forwarded-For" // nginx/reverse proxy + // forwadedFor: "CF-Connecting-IP" // cloudflare: + captcha: { + enabled: false, + service: null, + sitekey: null, + secret: null, + }, + ipdataApiKey: "eca677b284b3bac29eb72f5e496aa9047f26543605efe99ff2ce35c9", + }, + login: { + requireCaptcha: false, + }, + register: { + email: { + necessary: true, + allowlist: false, + blocklist: true, + domains: [], // TODO: efficiently save domain blocklist in database + // domains: fs.readFileSync(__dirname + "/blockedEmailDomains.txt", { encoding: "utf8" }).split("\n"), + }, + dateOfBirth: { + necessary: true, + minimum: 13, + }, + requireInvite: false, + requireCaptcha: true, + allowNewRegistration: true, + allowMultipleAccounts: true, + blockProxies: true, + password: { + minLength: 8, + minNumbers: 2, + minUpperCase: 2, + minSymbols: 0, + }, + }, + regions: { + default: "fosscord", + available: [{ id: "fosscord", name: "Fosscord", vip: false, custom: false, deprecated: false, optimal: false }], + }, + rabbitmq: { + host: null, + }, + kafka: { + brokers: null, + }, +}; diff --git a/util/src/entities/Emoji.ts b/util/src/entities/Emoji.ts index 366549db..b31ddb3b 100644 --- a/util/src/entities/Emoji.ts +++ b/util/src/entities/Emoji.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Role } from "./Role"; @@ -30,7 +30,7 @@ export class Emoji extends BaseClass { @Column() url: string; - @Column("simple-array") + @RelationId((emoji: Emoji) => emoji.roles) role_ids: string[]; @JoinColumn({ name: "role_ids" }) diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts index d46d31bc..d7b4dff4 100644 --- a/util/src/entities/Guild.ts +++ b/util/src/entities/Guild.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Emoji } from "./Emoji"; @@ -10,7 +10,7 @@ import { VoiceState } from "./VoiceState"; @Entity("guilds") export class Guild extends BaseClass { - @Column() + @RelationId((guild: Guild) => guild.afk_channel) afk_channel_id?: string; @JoinColumn({ name: "afk_channel_id" }) @@ -64,35 +64,35 @@ export class Guild extends BaseClass { @Column() presence_count?: number; // users online - @Column("simple-array") + @RelationId((guild: Guild) => guild.members) member_ids: string[]; @JoinColumn({ name: "member_ids" }) @ManyToMany(() => Member, (member: Member) => member.id) members: Member[]; - @Column("simple-array") + @RelationId((guild: Guild) => guild.roles) role_ids: string[]; @JoinColumn({ name: "role_ids" }) @ManyToMany(() => Role, (role: Role) => role.id) roles: Role[]; - @Column("simple-array") + @RelationId((guild: Guild) => guild.channels) channel_ids: string[]; @JoinColumn({ name: "channel_ids" }) @ManyToMany(() => Channel, (channel: Channel) => channel.id) channels: Channel[]; - @Column("simple-array") + @RelationId((guild: Guild) => guild.emojis) emoji_ids: string[]; @JoinColumn({ name: "emoji_ids" }) @ManyToMany(() => Emoji, (emoji: Emoji) => emoji.id) emojis: Emoji[]; - @Column("simple-array") + @RelationId((guild: Guild) => guild.voice_states) voice_state_ids: string[]; @JoinColumn({ name: "voice_state_ids" }) @@ -105,7 +105,7 @@ export class Guild extends BaseClass { @Column() name: string; - @Column() + @RelationId((guild: Guild) => guild.owner) owner_id: string; @JoinColumn({ name: "owner_id" }) @@ -121,11 +121,14 @@ export class Guild extends BaseClass { @Column() premium_tier?: number; // nitro boost level + @RelationId((guild: Guild) => guild.public_updates_channel) + public_updates_channel_id: string; + @JoinColumn({ name: "public_updates_channel_id" }) @ManyToOne(() => Channel, (channel: Channel) => channel.id) public_updates_channel?: Channel; - @Column() + @RelationId((guild: Guild) => guild.rules_channel) rules_channel_id?: string; @JoinColumn({ name: "rules_channel_id" }) @@ -138,7 +141,7 @@ export class Guild extends BaseClass { @Column() splash?: string; - @Column() + @RelationId((guild: Guild) => guild.system_channel) system_channel_id?: string; @JoinColumn({ name: "system_channel_id" }) @@ -151,6 +154,9 @@ export class Guild extends BaseClass { @Column() unavailable?: boolean; + @RelationId((guild: Guild) => guild.vanity_url) + vanity_url_code?: string; + @JoinColumn({ name: "vanity_url_code" }) @OneToOne(() => Invite, (invite: Invite) => invite.code) vanity_url?: Invite; @@ -170,6 +176,9 @@ export class Guild extends BaseClass { }[]; }; + @RelationId((guild: Guild) => guild.widget_channel) + widget_channel_id?: string; + @JoinColumn({ name: "widget_channel_id" }) @ManyToOne(() => Channel, (channel: Channel) => channel.id) widget_channel?: Channel; diff --git a/util/src/entities/Invite.ts b/util/src/entities/Invite.ts index 19f7206a..eac0cba0 100644 --- a/util/src/entities/Invite.ts +++ b/util/src/entities/Invite.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Guild } from "./Guild"; @@ -27,29 +27,29 @@ export class Invite extends BaseClass { @Column() expires_at: Date; - @Column() + @RelationId((invite: Invite) => invite.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) @ManyToOne(() => Guild, (guild: Guild) => guild.id) guild: Guild; - @Column() + @RelationId((invite: Invite) => invite.channel) channel_id: string; @JoinColumn({ name: "channel_id" }) @ManyToOne(() => Channel, (channel: Channel) => channel.id) channel: Channel; - @Column() + @RelationId((invite: Invite) => invite.inviter) inviter_id: string; @JoinColumn({ name: "inviter_id" }) @ManyToOne(() => User, (user: User) => user.id) inviter: User; - @Column() - target_usser_id: string; + @RelationId((invite: Invite) => invite.target_user) + target_user_id: string; @JoinColumn({ name: "target_user_id" }) @ManyToOne(() => User, (user: User) => user.id) diff --git a/util/src/entities/Member.ts b/util/src/entities/Member.ts index c367755e..01634d9e 100644 --- a/util/src/entities/Member.ts +++ b/util/src/entities/Member.ts @@ -1,18 +1,18 @@ import { PublicUser, User } from "./User"; import { BaseClass } from "./BaseClass"; -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { Guild } from "./Guild"; @Entity("members") export class Member extends BaseClass { - @Column() + @RelationId((member: Member) => member.user) user_id: string; @JoinColumn({ name: "user_id" }) @ManyToOne(() => User, (user: User) => user.id) user: User; - @Column() + @RelationId((member: Member) => member.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts index 2c0918c7..9daf042c 100644 --- a/util/src/entities/Message.ts +++ b/util/src/entities/Message.ts @@ -4,7 +4,16 @@ import { Role } from "./Role"; import { Channel } from "./Channel"; import { InteractionType } from "../interfaces/Interaction"; import { Application } from "./Application"; -import { Column, CreateDateColumn, Entity, JoinColumn, ManyToMany, ManyToOne, UpdateDateColumn } from "typeorm"; +import { + Column, + CreateDateColumn, + Entity, + JoinColumn, + ManyToMany, + ManyToOne, + RelationId, + UpdateDateColumn, +} from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Webhook } from "./Webhook"; @@ -34,42 +43,42 @@ export class Message extends BaseClass { @Column() id: string; - @Column() + @RelationId((message: Message) => message.channel) channel_id: string; @JoinColumn({ name: "channel_id" }) @ManyToOne(() => Channel, (channel: Channel) => channel.id) channel: Channel; - @Column() + @RelationId((message: Message) => message.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) @ManyToOne(() => Guild, (guild: Guild) => guild.id) guild?: Guild; - @Column() + @RelationId((message: Message) => message.author) author_id: string; @JoinColumn({ name: "author_id" }) @ManyToOne(() => User, (user: User) => user.id) author?: User; - @Column() + @RelationId((message: Message) => message.member) member_id: string; @JoinColumn({ name: "member_id" }) @ManyToOne(() => Member, (member: Member) => member.id) member?: Member; - @Column() + @RelationId((message: Message) => message.webhook) webhook_id: string; @JoinColumn({ name: "webhook_id" }) @ManyToOne(() => Webhook, (webhook: Webhook) => webhook.id) webhook?: Webhook; - @Column() + @RelationId((message: Message) => message.application) application_id: string; @JoinColumn({ name: "application_id" }) @@ -93,21 +102,21 @@ export class Message extends BaseClass { @Column() mention_everyone?: boolean; - @Column("simple-array") + @RelationId((message: Message) => message.mention_users) mention_user_ids: string[]; @JoinColumn({ name: "mention_user_ids" }) @ManyToMany(() => User, (user: User) => user.id) mention_users: User[]; - @Column("simple-array") + @RelationId((message: Message) => message.mention_roles) mention_role_ids: string[]; @JoinColumn({ name: "mention_role_ids" }) @ManyToMany(() => Role, (role: Role) => role.id) mention_roles: Role[]; - @Column("simple-array") + @RelationId((message: Message) => message.mention_channels) mention_channel_ids: string[]; @JoinColumn({ name: "mention_channel_ids" }) diff --git a/util/src/entities/RateLimit.ts b/util/src/entities/RateLimit.ts index 374a0759..3ac35df3 100644 --- a/util/src/entities/RateLimit.ts +++ b/util/src/entities/RateLimit.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { User } from "./User"; @@ -7,7 +7,7 @@ export class RateLimit extends BaseClass { @Column() id: "global" | "error" | string; // channel_239842397 | guild_238927349823 | webhook_238923423498 - @Column() + @RelationId((rate_limit: RateLimit) => rate_limit.user) user_id: string; @JoinColumn({ name: "user_id" }) diff --git a/util/src/entities/ReadState.ts b/util/src/entities/ReadState.ts index 7c56b6c6..0310cb5f 100644 --- a/util/src/entities/ReadState.ts +++ b/util/src/entities/ReadState.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Message } from "./Message"; @@ -6,20 +6,23 @@ import { User } from "./User"; @Entity("read_states") export class ReadState extends BaseClass { - @Column() + @RelationId((read_state: ReadState) => read_state.channel) channel_id: string; @JoinColumn({ name: "channel_id" }) @ManyToOne(() => Channel, (channel: Channel) => channel.id) channel: Channel; - @Column() + @RelationId((read_state: ReadState) => read_state.user) user_id: string; @JoinColumn({ name: "user_id" }) @ManyToOne(() => User, (user: User) => user.id) user: User; + @RelationId((read_state: ReadState) => read_state.last_message) + last_message_id: string; + @JoinColumn({ name: "last_message_id" }) @ManyToOne(() => Message, (message: Message) => message.id) last_message?: Message; diff --git a/util/src/entities/Relationship.ts b/util/src/entities/Relationship.ts index bd5861f0..3e1280c7 100644 --- a/util/src/entities/Relationship.ts +++ b/util/src/entities/Relationship.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { User } from "./User"; @@ -11,7 +11,7 @@ export enum RelationshipType { @Entity("relationships") export class Relationship extends BaseClass { - @Column() + @RelationId((relationship: Relationship) => relationship.user) user_id: string; @JoinColumn({ name: "user_id" }) diff --git a/util/src/entities/Role.ts b/util/src/entities/Role.ts index 7bb144cc..e48fd293 100644 --- a/util/src/entities/Role.ts +++ b/util/src/entities/Role.ts @@ -1,10 +1,10 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; @Entity("roles") export class Role extends BaseClass { - @Column() + @RelationId((role: Role) => role.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) diff --git a/util/src/entities/Team.ts b/util/src/entities/Team.ts index 5e645650..fa1b0ed2 100644 --- a/util/src/entities/Team.ts +++ b/util/src/entities/Team.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { TeamMember } from "./TeamMember"; import { User } from "./User"; @@ -8,7 +8,7 @@ export class Team extends BaseClass { @Column() icon?: string; - @Column("simple-array") + @RelationId((team: Team) => team.members) member_ids: string[]; @JoinColumn({ name: "member_ids" }) @@ -18,7 +18,7 @@ export class Team extends BaseClass { @Column() name: string; - @Column() + @RelationId((team: Team) => team.owner_user) owner_user_id: string; @JoinColumn({ name: "owner_user_id" }) diff --git a/util/src/entities/TeamMember.ts b/util/src/entities/TeamMember.ts index 2b1c76f1..f0b54c6f 100644 --- a/util/src/entities/TeamMember.ts +++ b/util/src/entities/TeamMember.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { User } from "./User"; @@ -15,14 +15,14 @@ export class TeamMember extends BaseClass { @Column("simple-array") permissions: string[]; - @Column() + @RelationId((member: TeamMember) => member.team) team_id: string; @JoinColumn({ name: "team_id" }) @ManyToOne(() => require("./Team").Team, (team: import("./Team").Team) => team.id) team: import("./Team").Team; - @Column() + @RelationId((member: TeamMember) => member.user) user_id: string; @JoinColumn({ name: "user_id" }) diff --git a/util/src/entities/Template.ts b/util/src/entities/Template.ts index 5c9a5120..c8d2034c 100644 --- a/util/src/entities/Template.ts +++ b/util/src/entities/Template.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { User } from "./User"; @@ -17,6 +17,9 @@ export class Template extends BaseClass { @Column() usage_count?: number; + @RelationId((template: Template) => template.creator) + creator_id: string; + @JoinColumn({ name: "creator_id" }) @ManyToOne(() => User, (user: User) => user.id) creator: User; @@ -27,6 +30,9 @@ export class Template extends BaseClass { @Column() updated_at: Date; + @RelationId((template: Template) => template.source_guild) + source_guild_id: string; + @JoinColumn({ name: "source_guild_id" }) @ManyToOne(() => Guild, (guild: Guild) => guild.id) source_guild: Guild; diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts index 39e59da4..bdf0d35f 100644 --- a/util/src/entities/User.ts +++ b/util/src/entities/User.ts @@ -1,8 +1,10 @@ -import { Column, Entity, JoinColumn, OneToMany } from "typeorm"; +import { Column, Entity, JoinColumn, 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 { Guild } from "./Guild"; export const PublicUserProjection = { username: true, @@ -24,6 +26,13 @@ export class User extends BaseClass { @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; + } + @Column() avatar?: string; // hash of the user avatar @@ -84,17 +93,21 @@ export class User extends BaseClass { @Column({ type: "bigint" }) public_flags: bigint; - @Column("simple-array") // string in simple-array must not contain commas - guilds: string[]; // array of guild ids the user is part of + @RelationId((user: User) => user.guilds) + guild_ids: string[]; // array of guild ids the user is part of + + @JoinColumn({ name: "guild_ids" }) + @OneToMany(() => Guild, (guild: Guild) => guild.id) + guilds: Guild[]; - @Column("simple-array") // string in simple-array must not contain commas + @RelationId((user: User) => user.relationships) relationship_ids: string[]; // array of guild ids the user is part of @JoinColumn({ name: "relationship_ids" }) @OneToMany(() => User, (user: User) => user.id) relationships: Relationship[]; - @Column("simple-array") // string in simple-array must not contain commas + @RelationId((user: User) => user.connected_accounts) connected_account_ids: string[]; // array of guild ids the user is part of @JoinColumn({ name: "connected_account_ids" }) @@ -102,14 +115,28 @@ export class User extends BaseClass { connected_accounts: ConnectedAccount[]; @Column({ type: "simple-json", select: false }) - user_data: { + 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) - fingerprints: string[]; // array of fingerprints -> used to prevent multiple accounts }; + @Column({ type: "simple-array" }) + fingerprints: string[]; // array of fingerprints -> used to prevent multiple accounts + @Column("simple-json") settings: UserSettings; + + static async getPublicUser(user_id: string, additional_fields?: any) { + const user = await User.findOne( + { id: user_id }, + { + ...PublicUserProjection, + ...additional_fields, + } + ); + if (!user) throw new HTTPError("User not found", 404); + return user; + } } export interface UserSettings { diff --git a/util/src/entities/VoiceState.ts b/util/src/entities/VoiceState.ts index 2416c6c0..6707a575 100644 --- a/util/src/entities/VoiceState.ts +++ b/util/src/entities/VoiceState.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne, OneToOne } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, OneToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Guild } from "./Guild"; @@ -6,14 +6,23 @@ import { User } from "./User"; @Entity("voice_states") export class VoiceState extends BaseClass { + @RelationId((voice_state: VoiceState) => voice_state.guild) + guild_id: string; + @JoinColumn({ name: "guild_id" }) @ManyToOne(() => Guild, (guild: Guild) => guild.id) guild?: Guild; + @RelationId((voice_state: VoiceState) => voice_state.channel) + channel_id: string; + @JoinColumn({ name: "channel_id" }) @ManyToOne(() => Channel, (channel: Channel) => channel.id) channel: Channel; + @RelationId((voice_state: VoiceState) => voice_state.user) + user_id: string; + @JoinColumn({ name: "user_id" }) @ManyToOne(() => User, (user: User) => user.id) user: User; diff --git a/util/src/entities/Webhook.ts b/util/src/entities/Webhook.ts index 54233638..dc929c18 100644 --- a/util/src/entities/Webhook.ts +++ b/util/src/entities/Webhook.ts @@ -1,5 +1,9 @@ -import { Column, Entity, JoinColumn } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; +import { Application } from "./Application"; import { BaseClass } from "./BaseClass"; +import { Channel } from "./Channel"; +import { Guild } from "./Guild"; +import { User } from "./User"; export enum WebhookType { Incoming = 1, @@ -23,18 +27,38 @@ export class Webhook extends BaseClass { @Column() token?: string; - @JoinColumn() - guild?: string; + @RelationId((webhook: Webhook) => webhook.guild) + guild_id: string; - @JoinColumn() - channel: string; + @JoinColumn({ name: "guild_id" }) + @ManyToOne(() => Guild, (guild: Guild) => guild.id) + guild: Guild; - @JoinColumn() - application?: string; + @RelationId((webhook: Webhook) => webhook.channel) + channel_id: string; - @JoinColumn() - user?: string; + @JoinColumn({ name: "channel_id" }) + @ManyToOne(() => Channel, (channel: Channel) => channel.id) + channel: Channel; - @JoinColumn() - source_guild: string; + @RelationId((webhook: Webhook) => webhook.application) + application_id: string; + + @JoinColumn({ name: "application_id" }) + @ManyToOne(() => Application, (application: Application) => application.id) + application: Application; + + @RelationId((webhook: Webhook) => webhook.user) + user_id: string; + + @JoinColumn({ name: "user_id" }) + @ManyToOne(() => User, (user: User) => user.id) + user: User; + + @RelationId((webhook: Webhook) => webhook.guild) + source_guild_id: string; + + @JoinColumn({ name: "source_guild_id" }) + @ManyToOne(() => Guild, (guild: Guild) => guild.id) + source_guild: Guild; } diff --git a/util/src/entities/index.ts b/util/src/entities/index.ts index 9cb10016..b9e361c1 100644 --- a/util/src/entities/index.ts +++ b/util/src/entities/index.ts @@ -3,6 +3,7 @@ export * from "./AuditLog"; export * from "./Ban"; export * from "./BaseClass"; export * from "./Channel"; +export * from "./Config"; export * from "./ConnectedAccount"; export * from "./Emoji"; export * from "./Guild"; -- cgit 1.5.1 From 766dcc24aa1f1de60cc15af699f933d521aa85b6 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Sun, 29 Aug 2021 00:03:58 +0200 Subject: :sparkles: update util --- util/.vscode/launch.json | 1 + util/package-lock.json | 313 +- util/package.json | 8 +- util/src/entities/Application.ts | 16 +- util/src/entities/AuditLog.ts | 8 +- util/src/entities/Ban.ts | 2 +- util/src/entities/BaseClass.ts | 46 +- util/src/entities/Channel.ts | 14 +- util/src/entities/Config.ts | 2 +- util/src/entities/ConnectedAccount.ts | 20 +- util/src/entities/Emoji.ts | 5 +- util/src/entities/Guild.ts | 52 +- util/src/entities/Invite.ts | 6 +- util/src/entities/Member.ts | 234 +- util/src/entities/Message.ts | 45 +- util/src/entities/ReadState.ts | 2 +- util/src/entities/Relationship.ts | 6 +- util/src/entities/Role.ts | 2 +- util/src/entities/Sticker.ts | 42 + util/src/entities/Team.ts | 2 +- util/src/entities/TeamMember.ts | 2 +- util/src/entities/Template.ts | 6 +- util/src/entities/User.ts | 174 +- util/src/entities/VoiceState.ts | 2 +- util/src/entities/Webhook.ts | 6 +- util/src/entities/schema.json | 6218 --------------------------------- util/src/interfaces/Event.ts | 12 +- util/src/tes.ts | 19 + util/src/util/AutoUpdate.ts | 1 + util/src/util/Database.ts | 12 +- util/src/util/Permissions.ts | 39 +- util/tests/User.test.js | 16 +- util/tests/setupJest.js | 4 + util/tests/validate.test.js | 33 - util/tsconfig.json | 2 +- 35 files changed, 855 insertions(+), 6517 deletions(-) create mode 100644 util/src/entities/Sticker.ts delete mode 100644 util/src/entities/schema.json create mode 100644 util/src/tes.ts delete mode 100644 util/tests/validate.test.js (limited to 'util/src/entities/Guild.ts') diff --git a/util/.vscode/launch.json b/util/.vscode/launch.json index 0de6e587..524622d1 100644 --- a/util/.vscode/launch.json +++ b/util/.vscode/launch.json @@ -18,6 +18,7 @@ "type": "node", "request": "launch", "runtimeArgs": ["--inspect-brk", "${workspaceRoot}/node_modules/jest/bin/jest.js", "--runInBand"], + "preLaunchTask": "tsc: build - tsconfig.json", "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "port": 9229 diff --git a/util/package-lock.json b/util/package-lock.json index f30fcf47..bb04d0bc 100644 --- a/util/package-lock.json +++ b/util/package-lock.json @@ -16,10 +16,11 @@ "dot-prop": "^6.0.1", "env-paths": "^2.2.1", "jsonwebtoken": "^8.5.1", - "lambert-server": "^1.2.8", - "missing-native-js-functions": "^1.2.10", + "lambert-server": "^1.2.10", + "missing-native-js-functions": "^1.2.11", "node-fetch": "^2.6.1", "patch-package": "^6.4.7", + "pg": "^8.7.1", "reflect-metadata": "^0.1.13", "sqlite3": "^5.0.2", "typeorm": "^0.2.37", @@ -1765,6 +1766,14 @@ "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==" }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, "node_modules/bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -6098,16 +6107,16 @@ } }, "node_modules/lambert-server": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/lambert-server/-/lambert-server-1.2.8.tgz", - "integrity": "sha512-vi/Ku/QudY+WIdGO9bc0qLfVhfuJFWXk1+etesPW1vW29sPbmevLL6IwfvCtw+/MyzRAJLOyCBfQ310a68+2QQ==", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/lambert-server/-/lambert-server-1.2.10.tgz", + "integrity": "sha512-BHGPmpUrRklFJHPu0vAA8NBewtEd4IX80FRpV4nX9z8kHTUYHqnYHoBeUEWoUmxAeFQvQae1Axk5RQXRQk4VNw==", "dependencies": { "body-parser": "^1.19.0", "chalk": "^4.1.1", "express": "^4.17.1", "express-async-errors": "^3.1.1", "helmet": "^4.4.1", - "missing-native-js-functions": "^1.1.8" + "missing-native-js-functions": "^1.2.11" } }, "node_modules/leven": { @@ -6329,9 +6338,9 @@ } }, "node_modules/missing-native-js-functions": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/missing-native-js-functions/-/missing-native-js-functions-1.2.10.tgz", - "integrity": "sha512-sq+oAw/C3OtUyKopLNOf/+U85YNx7db6fy5nVfGVKlGdcV8tX24GjOSkcZeCAnAIjMEnlQBWTr17JXa3OJj22g==" + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/missing-native-js-functions/-/missing-native-js-functions-1.2.11.tgz", + "integrity": "sha512-U97IscNBL4Wg9adYjEBT46Hb0Ld5dPT8vbdwFX+TNzGrFQCc4WqoGAZouaLNFwUqxzzHZ9DVg59unwnQyeIIQg==" }, "node_modules/mkdirp": { "version": "1.0.4", @@ -6916,6 +6925,11 @@ "node": ">=6" } }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "node_modules/parent-require": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz", @@ -7065,6 +7079,80 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "optional": true }, + "node_modules/pg": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.1.tgz", + "integrity": "sha512-7bdYcv7V6U3KAtWjpQJJBww0UEsWuh4yQ/EjNf2HeO/NnvKjpvhEIe/A/TleP6wtmSKnUnghs5A9jUoK6iDdkA==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.4.1", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "pg-native": ">=2.0.0" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.4.1.tgz", + "integrity": "sha512-TVHxR/gf3MeJRvchgNHxsYsTCHQ+4wm3VIHSS19z8NC0+gioEhq1okDY1sm/TYbfoP6JLFx01s0ShvZ3puP/iQ==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz", + "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==", + "dependencies": { + "split2": "^3.1.1" + } + }, "node_modules/picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -7088,6 +7176,41 @@ "node": ">= 6" } }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -7604,6 +7727,35 @@ "memory-pager": "^1.0.2" } }, + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/split2/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/split2/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -8492,6 +8644,14 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -10030,6 +10190,11 @@ "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==" }, + "buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -13373,16 +13538,16 @@ "dev": true }, "lambert-server": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/lambert-server/-/lambert-server-1.2.8.tgz", - "integrity": "sha512-vi/Ku/QudY+WIdGO9bc0qLfVhfuJFWXk1+etesPW1vW29sPbmevLL6IwfvCtw+/MyzRAJLOyCBfQ310a68+2QQ==", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/lambert-server/-/lambert-server-1.2.10.tgz", + "integrity": "sha512-BHGPmpUrRklFJHPu0vAA8NBewtEd4IX80FRpV4nX9z8kHTUYHqnYHoBeUEWoUmxAeFQvQae1Axk5RQXRQk4VNw==", "requires": { "body-parser": "^1.19.0", "chalk": "^4.1.1", "express": "^4.17.1", "express-async-errors": "^3.1.1", "helmet": "^4.4.1", - "missing-native-js-functions": "^1.1.8" + "missing-native-js-functions": "^1.2.11" } }, "leven": { @@ -13570,9 +13735,9 @@ } }, "missing-native-js-functions": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/missing-native-js-functions/-/missing-native-js-functions-1.2.10.tgz", - "integrity": "sha512-sq+oAw/C3OtUyKopLNOf/+U85YNx7db6fy5nVfGVKlGdcV8tX24GjOSkcZeCAnAIjMEnlQBWTr17JXa3OJj22g==" + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/missing-native-js-functions/-/missing-native-js-functions-1.2.11.tgz", + "integrity": "sha512-U97IscNBL4Wg9adYjEBT46Hb0Ld5dPT8vbdwFX+TNzGrFQCc4WqoGAZouaLNFwUqxzzHZ9DVg59unwnQyeIIQg==" }, "mkdirp": { "version": "1.0.4", @@ -14013,6 +14178,11 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "parent-require": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz", @@ -14136,6 +14306,61 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "optional": true }, + "pg": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.1.tgz", + "integrity": "sha512-7bdYcv7V6U3KAtWjpQJJBww0UEsWuh4yQ/EjNf2HeO/NnvKjpvhEIe/A/TleP6wtmSKnUnghs5A9jUoK6iDdkA==", + "requires": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.4.1", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + } + }, + "pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-pool": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.4.1.tgz", + "integrity": "sha512-TVHxR/gf3MeJRvchgNHxsYsTCHQ+4wm3VIHSS19z8NC0+gioEhq1okDY1sm/TYbfoP6JLFx01s0ShvZ3puP/iQ==", + "requires": {} + }, + "pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz", + "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==", + "requires": { + "split2": "^3.1.1" + } + }, "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -14150,6 +14375,29 @@ "node-modules-regexp": "^1.0.0" } }, + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "requires": { + "xtend": "^4.0.0" + } + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -14564,6 +14812,34 @@ "memory-pager": "^1.0.2" } }, + "split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "requires": { + "readable-stream": "^3.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -15187,6 +15463,11 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/util/package.json b/util/package.json index fc474d68..af14a521 100644 --- a/util/package.json +++ b/util/package.json @@ -9,8 +9,7 @@ "patch": "patch-package", "test": "npm run build && jest", "postinstall": "npm run patch && npm run build", - "build": "npx tsc -b .", - "generate:schema": "npx typescript-json-schema tsconfig.json '*' -o src/entities/schema.json" + "build": "npx tsc -b ." }, "repository": { "type": "git", @@ -44,10 +43,11 @@ "dot-prop": "^6.0.1", "env-paths": "^2.2.1", "jsonwebtoken": "^8.5.1", - "lambert-server": "^1.2.8", - "missing-native-js-functions": "^1.2.10", + "lambert-server": "^1.2.10", + "missing-native-js-functions": "^1.2.11", "node-fetch": "^2.6.1", "patch-package": "^6.4.7", + "pg": "^8.7.1", "reflect-metadata": "^0.1.13", "sqlite3": "^5.0.2", "typeorm": "^0.2.37", diff --git a/util/src/entities/Application.ts b/util/src/entities/Application.ts index 90d0f056..a87b5cea 100644 --- a/util/src/entities/Application.ts +++ b/util/src/entities/Application.ts @@ -8,13 +8,13 @@ export class Application extends BaseClass { @Column() name: string; - @Column() + @Column({ nullable: true }) icon?: string; @Column() description: string; - @Column("simple-array") + @Column({ type: "simple-array", nullable: true }) rpc_origins?: string[]; @Column() @@ -23,16 +23,16 @@ export class Application extends BaseClass { @Column() bot_require_code_grant: boolean; - @Column() + @Column({ nullable: true }) terms_of_service_url?: string; - @Column() + @Column({ nullable: true }) privacy_policy_url?: string; @Column() owner_id: string; - @Column() + @Column({ nullable: true }) summary?: string; @Column() @@ -52,13 +52,13 @@ export class Application extends BaseClass { @ManyToOne(() => Guild, (guild: Guild) => guild.id) guild: Guild; // if this application is a game sold, this field will be the guild to which it has been linked - @Column() + @Column({ nullable: true }) primary_sku_id?: string; // if this application is a game sold, this field will be the id of the "Game SKU" that is created, - @Column() + @Column({ nullable: true }) slug?: string; // if this application is a game sold, this field will be the URL slug that links to the store page - @Column() + @Column({ nullable: true }) cover_image?: string; // the application's default rich presence invite cover image hash @Column() diff --git a/util/src/entities/AuditLog.ts b/util/src/entities/AuditLog.ts index 2195771b..ea8aa641 100644 --- a/util/src/entities/AuditLog.ts +++ b/util/src/entities/AuditLog.ts @@ -46,7 +46,7 @@ export class AuditLogEntry extends BaseClass { @RelationId((auditlog: AuditLogEntry) => auditlog.target) target_id: string; - @JoinColumn({ name: "user_id" }) + @JoinColumn({ name: "target_id" }) @ManyToOne(() => User, (user: User) => user.id) target?: User; @@ -63,7 +63,7 @@ export class AuditLogEntry extends BaseClass { }) action_type: AuditLogEvents; - @Column("simple-json") + @Column({ type: "simple-json", nullable: true }) options?: { delete_member_days?: string; members_removed?: string; @@ -76,10 +76,10 @@ export class AuditLogEntry extends BaseClass { }; @Column() - @Column("simple-json") + @Column({ type: "simple-json" }) changes: AuditLogChange[]; - @Column() + @Column({ nullable: true }) reason?: string; } diff --git a/util/src/entities/Ban.ts b/util/src/entities/Ban.ts index 2553e886..f1cbd849 100644 --- a/util/src/entities/Ban.ts +++ b/util/src/entities/Ban.ts @@ -29,6 +29,6 @@ export class Ban extends BaseClass { @Column() ip: string; - @Column() + @Column({ nullable: true }) reason?: string; } diff --git a/util/src/entities/BaseClass.ts b/util/src/entities/BaseClass.ts index bb6ccea1..31338ff6 100644 --- a/util/src/entities/BaseClass.ts +++ b/util/src/entities/BaseClass.ts @@ -1,34 +1,26 @@ import "reflect-metadata"; -import { BaseEntity, BeforeInsert, BeforeUpdate, PrimaryColumn } from "typeorm"; +import { + BaseEntity, + BeforeInsert, + BeforeUpdate, + EntityMetadata, + FindConditions, + FindManyOptions, + PrimaryColumn, +} from "typeorm"; import { Snowflake } from "../util/Snowflake"; -import Ajv, { ValidateFunction } from "ajv"; -import schema from "./schema.json"; import "missing-native-js-functions"; // TODO use class-validator https://typeorm.io/#/validation with class annotators (isPhone/isEmail) combined with types from typescript-json-schema // btw. we don't use class-validator for everything, because we need to explicitly set the type instead of deriving it from typescript also it doesn't easily support nested objects -const ajv = new Ajv({ - removeAdditional: "all", - useDefaults: true, - coerceTypes: true, - // @ts-ignore - validateFormats: false, - allowUnionTypes: true, -}); - export class BaseClass extends BaseEntity { @PrimaryColumn() id: string; // @ts-ignore - constructor(props?: any, public opts: { id?: string } = {}) { + constructor(public props?: any, public opts: { id?: string } = {}) { super(); - this.assign(props); - - if (!this.construct.schema) { - this.construct.schema = ajv.compile({ ...schema, $ref: `#/definitions/${this.construct.name}` }); - } this.id = this.opts.id || Snowflake.generate(); } @@ -38,19 +30,20 @@ export class BaseClass extends BaseEntity { } get metadata() { - return this.construct.getRepository().metadata; + return this.construct.getRepository().metadata as EntityMetadata; } assign(props: any) { if (!props || typeof props !== "object") return; + delete props.id; delete props.opts; + delete props.props; const properties = new Set(this.metadata.columns.map((x: any) => x.propertyName)); // will not include relational properties (e.g. @RelationId @ManyToMany) for (const key in props) { - if (this.hasOwnProperty(key)) continue; if (!properties.has(key)) continue; // @ts-ignore const setter = this[`set${key.capitalize()}`]; @@ -66,8 +59,7 @@ export class BaseClass extends BaseEntity { @BeforeUpdate() @BeforeInsert() validate() { - const valid = this.construct.schema(this.toJSON()); - if (!valid) throw ajv.errors; + this.assign(this.props); return this; } @@ -75,4 +67,14 @@ export class BaseClass extends BaseEntity { // @ts-ignore return Object.fromEntries(this.metadata.columns.map((x) => [x.propertyName, this[x.propertyName]])); } + + static increment(conditions: FindConditions, propertyPath: string, value: number | string) { + const repository = this.getRepository(); + return repository.increment(conditions, propertyPath, value); + } + + static decrement(conditions: FindConditions, propertyPath: string, value: number | string) { + const repository = this.getRepository(); + return repository.decrement(conditions, propertyPath, value); + } } diff --git a/util/src/entities/Channel.ts b/util/src/entities/Channel.ts index d26f1054..d02cedb4 100644 --- a/util/src/entities/Channel.ts +++ b/util/src/entities/Channel.ts @@ -60,25 +60,25 @@ export class Channel extends BaseClass { @ManyToOne(() => User, (user: User) => user.id) owner: User; - @Column() + @Column({ nullable: true }) last_pin_timestamp?: number; - @Column() + @Column({ nullable: true }) default_auto_archive_duration?: number; @Column() position: number; - @Column("simple-json") + @Column({ type: "simple-json" }) permission_overwrites: ChannelPermissionOverwrite[]; - @Column() + @Column({ nullable: true }) video_quality_mode?: number; - @Column() + @Column({ nullable: true }) bitrate?: number; - @Column() + @Column({ nullable: true }) user_limit?: number; @Column() @@ -87,7 +87,7 @@ export class Channel extends BaseClass { @Column() rate_limit_per_user: number; - @Column() + @Column({ nullable: true }) topic?: string; } diff --git a/util/src/entities/Config.ts b/util/src/entities/Config.ts index c9326be2..320a729c 100644 --- a/util/src/entities/Config.ts +++ b/util/src/entities/Config.ts @@ -5,7 +5,7 @@ import { Snowflake } from "../util/Snowflake"; @Entity("config") export class ConfigEntity extends BaseClass { - @Column("simple-json") + @Column({ type: "simple-json" }) value: ConfigValue; } diff --git a/util/src/entities/ConnectedAccount.ts b/util/src/entities/ConnectedAccount.ts index 6aa2b401..e48bc322 100644 --- a/util/src/entities/ConnectedAccount.ts +++ b/util/src/entities/ConnectedAccount.ts @@ -1,21 +1,29 @@ -import { Column, Entity } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; +import { User } from "./User"; @Entity("connected_accounts") export class ConnectedAccount extends BaseClass { - @Column() + @RelationId((account: ConnectedAccount) => account.user) + user_id: string; + + @JoinColumn({ name: "user_id" }) + @ManyToOne(() => User, (user: User) => user.connected_accounts) + user: User; + + @Column({ select: false }) access_token: string; - @Column() + @Column({ select: false }) friend_sync: boolean; @Column() name: string; - @Column() + @Column({ select: false }) revoked: boolean; - @Column() + @Column({ select: false }) show_activity: boolean; @Column() @@ -24,6 +32,6 @@ export class ConnectedAccount extends BaseClass { @Column() verifie: boolean; - @Column() + @Column({ select: false }) visibility: number; } diff --git a/util/src/entities/Emoji.ts b/util/src/entities/Emoji.ts index b31ddb3b..0686d476 100644 --- a/util/src/entities/Emoji.ts +++ b/util/src/entities/Emoji.ts @@ -9,7 +9,7 @@ export class Emoji extends BaseClass { animated: boolean; @Column() - available: boolean; + available: boolean; // whether this emoji can be used, may be false due to loss of Server Boosts @Column() guild_id: string; @@ -27,9 +27,6 @@ export class Emoji extends BaseClass { @Column() require_colons: boolean; - @Column() - url: string; - @RelationId((emoji: Emoji) => emoji.roles) role_ids: string[]; diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts index d7b4dff4..9ca4b1e4 100644 --- a/util/src/entities/Guild.ts +++ b/util/src/entities/Guild.ts @@ -17,51 +17,51 @@ export class Guild extends BaseClass { @ManyToOne(() => Channel, (channel: Channel) => channel.id) afk_channel?: Channel; - @Column() + @Column({ nullable: true }) afk_timeout?: number; // * commented out -> use owner instead // application id of the guild creator if it is bot-created - // @Column() + // @Column({ nullable: true }) // application?: string; - @Column() + @Column({ nullable: true }) banner?: string; - @Column() + @Column({ nullable: true }) default_message_notifications?: number; - @Column() + @Column({ nullable: true }) description?: string; - @Column() + @Column({ nullable: true }) discovery_splash?: string; - @Column() + @Column({ nullable: true }) explicit_content_filter?: number; - @Column("simple-array") + @Column({ type: "simple-array" }) features: string[]; - @Column() + @Column({ nullable: true }) icon?: string; - @Column() + @Column({ nullable: true }) large?: boolean; - @Column() + @Column({ nullable: true }) max_members?: number; // e.g. default 100.000 - @Column() + @Column({ nullable: true }) max_presences?: number; - @Column() + @Column({ nullable: true }) max_video_channel_users?: number; // ? default: 25, is this max 25 streaming or watching - @Column() + @Column({ nullable: true }) member_count?: number; - @Column() + @Column({ nullable: true }) presence_count?: number; // users online @RelationId((guild: Guild) => guild.members) @@ -99,7 +99,7 @@ export class Guild extends BaseClass { @ManyToMany(() => VoiceState, (voicestate: VoiceState) => voicestate.id) voice_states: VoiceState[]; - @Column() + @Column({ nullable: true }) mfa_level?: number; @Column() @@ -112,13 +112,13 @@ export class Guild extends BaseClass { @ManyToOne(() => User, (user: User) => user.id) owner: User; - @Column() + @Column({ nullable: true }) preferred_locale?: string; // only community guilds can choose this - @Column() + @Column({ nullable: true }) premium_subscription_count?: number; - @Column() + @Column({ nullable: true }) premium_tier?: number; // nitro boost level @RelationId((guild: Guild) => guild.public_updates_channel) @@ -135,10 +135,10 @@ export class Guild extends BaseClass { @ManyToOne(() => Channel, (channel: Channel) => channel.id) rules_channel?: string; - @Column() + @Column({ nullable: true }) region?: string; - @Column() + @Column({ nullable: true }) splash?: string; @RelationId((guild: Guild) => guild.system_channel) @@ -148,10 +148,10 @@ export class Guild extends BaseClass { @ManyToMany(() => Channel, (channel: Channel) => channel.id) system_channel?: Channel; - @Column() + @Column({ nullable: true }) system_channel_flags?: number; - @Column() + @Column({ nullable: true }) unavailable?: boolean; @RelationId((guild: Guild) => guild.vanity_url) @@ -161,10 +161,10 @@ export class Guild extends BaseClass { @OneToOne(() => Invite, (invite: Invite) => invite.code) vanity_url?: Invite; - @Column() + @Column({ nullable: true }) verification_level?: number; - @Column("simple-json") + @Column({ type: "simple-json" }) welcome_screen: { enabled: boolean; description: string; @@ -183,6 +183,6 @@ export class Guild extends BaseClass { @ManyToOne(() => Channel, (channel: Channel) => channel.id) widget_channel?: Channel; - @Column() + @Column({ nullable: true }) widget_enabled?: boolean; } diff --git a/util/src/entities/Invite.ts b/util/src/entities/Invite.ts index eac0cba0..d8c6d69c 100644 --- a/util/src/entities/Invite.ts +++ b/util/src/entities/Invite.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Guild } from "./Guild"; @@ -6,7 +6,7 @@ import { User } from "./User"; @Entity("invites") export class Invite extends BaseClass { - @Column() + @PrimaryColumn() code: string; @Column() @@ -55,6 +55,6 @@ export class Invite extends BaseClass { @ManyToOne(() => User, (user: User) => user.id) target_user?: string; // could be used for "User specific invites" https://github.com/fosscord/fosscord/issues/62 - @Column() + @Column({ nullable: true }) target_user_type?: number; } diff --git a/util/src/entities/Member.ts b/util/src/entities/Member.ts index 01634d9e..5b588d70 100644 --- a/util/src/entities/Member.ts +++ b/util/src/entities/Member.ts @@ -1,7 +1,17 @@ import { PublicUser, User } from "./User"; import { BaseClass } from "./BaseClass"; -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, RelationId } from "typeorm"; import { Guild } from "./Guild"; +import { Config, emitEvent } from "../util"; +import { + GuildCreateEvent, + GuildDeleteEvent, + GuildMemberAddEvent, + GuildMemberRemoveEvent, + GuildMemberUpdateEvent, +} from "../interfaces"; +import { HTTPError } from "lambert-server"; +import { Role } from "./Role"; @Entity("members") export class Member extends BaseClass { @@ -19,16 +29,20 @@ export class Member extends BaseClass { @ManyToOne(() => Guild, (guild: Guild) => guild.id) guild: Guild; - @Column() + @Column({ nullable: true }) nick?: string; - @Column("simple-array") - roles: string[]; + @RelationId((member: Member) => member.roles) + role_ids: string[]; + + @JoinColumn({ name: "role_ids" }) + @ManyToMany(() => Role) + roles: Role[]; @Column() joined_at: Date; - @Column() + @Column({ nullable: true }) premium_since?: number; @Column() @@ -40,12 +54,180 @@ export class Member extends BaseClass { @Column() pending: boolean; - @Column("simple-json") + @Column({ type: "simple-json" }) settings: UserGuildSettings; // TODO: update - @Column("simple-json") + @Column({ type: "simple-json" }) read_state: Record; + + static async IsInGuildOrFail(user_id: string, guild_id: string) { + if (await Member.count({ id: user_id, guild_id })) return true; + throw new HTTPError("You are not member of this guild", 403); + } + + static async removeFromGuild(user_id: string, guild_id: string) { + const guild = await Guild.findOneOrFail({ select: ["owner_id"], where: { id: guild_id } }); + if (guild.owner_id === user_id) throw new Error("The owner cannot be removed of the guild"); + const member = await Member.findOneOrFail({ where: { id: user_id, guild_id }, relations: ["user"] }); + + // use promise all to execute all promises at the same time -> save time + return Promise.all([ + Member.delete({ + id: user_id, + guild_id: guild_id, + }), + Guild.decrement({ id: guild_id }, "member_count", -1), + + emitEvent({ + event: "GUILD_DELETE", + data: { + id: guild_id, + }, + user_id: user_id, + } as GuildDeleteEvent), + emitEvent({ + event: "GUILD_MEMBER_REMOVE", + data: { + guild_id: guild_id, + user: member.user, + }, + guild_id: guild_id, + } as GuildMemberRemoveEvent), + ]); + } + + static async addRole(user_id: string, guild_id: string, role_id: string) { + const [member] = await Promise.all([ + Member.findOneOrFail({ + where: { id: user_id, guild_id: guild_id }, + relations: ["user"], // we don't want to load the role objects just the ids + }), + await Role.findOneOrFail({ id: role_id, guild_id: guild_id }), + ]); + member.role_ids.push(role_id); + member.save(); + + await emitEvent({ + event: "GUILD_MEMBER_UPDATE", + data: { + guild_id: guild_id, + user: member.user, + roles: member.role_ids, + }, + guild_id: guild_id, + } as GuildMemberUpdateEvent); + } + + static async removeRole(user_id: string, guild_id: string, role_id: string) { + const [member] = await Promise.all([ + Member.findOneOrFail({ + where: { id: user_id, guild_id: guild_id }, + relations: ["user"], // we don't want to load the role objects just the ids + }), + await Role.findOneOrFail({ id: role_id, guild_id: guild_id }), + ]); + member.role_ids.remove(role_id); + member.save(); + + await emitEvent({ + event: "GUILD_MEMBER_UPDATE", + data: { + guild_id: guild_id, + user: member.user, + roles: member.role_ids, + }, + guild_id: guild_id, + } as GuildMemberUpdateEvent); + } + + static async changeNickname(user_id: string, guild_id: string, nickname: string) { + const member = await Member.findOneOrFail({ + where: { + id: user_id, + guild_id: guild_id, + }, + relations: ["user"], + }); + member.nick = nickname; + + await Promise.all([ + member.save(), + + emitEvent({ + event: "GUILD_MEMBER_UPDATE", + data: { + guild_id: guild_id, + user: member.user, + nick: nickname, + }, + guild_id: guild_id, + } as GuildMemberUpdateEvent), + ]); + } + + static async addToGuild(user_id: string, guild_id: string) { + const user = await User.getPublicUser(user_id); + + const { maxGuilds } = Config.get().limits.user; + const guild_count = await Member.count({ id: user_id }); + if (guild_count >= maxGuilds) { + throw new HTTPError(`You are at the ${maxGuilds} server limit.`, 403); + } + + const guild = await Guild.findOneOrFail(guild_id, { + relations: ["channels", "emojis", "members", "roles", "stickers"], + }); + + if (await Member.count({ id: user.id, guild_id })) + throw new HTTPError("You are already a member of this guild", 400); + + const member = { + id: user_id, + guild_id: guild_id, + nick: undefined, + roles: [guild_id], // @everyone role + joined_at: new Date(), + premium_since: undefined, + deaf: false, + mute: false, + pending: false, + }; + // @ts-ignore + guild.joined_at = member.joined_at; + + await Promise.all([ + new Member({ + ...member, + read_state: {}, + settings: { + channel_overrides: [], + message_notifications: 0, + mobile_push: true, + mute_config: null, + muted: false, + suppress_everyone: false, + suppress_roles: false, + version: 0, + }, + }).save(), + Guild.increment({ id: guild_id }, "member_count", 1), + emitEvent({ + event: "GUILD_MEMBER_ADD", + data: { + ...member, + user, + guild_id: guild_id, + }, + guild_id: guild_id, + } as GuildMemberAddEvent), + emitEvent({ + event: "GUILD_CREATE", + data: guild, + user_id, + } as GuildCreateEvent), + ]); + } } export interface UserGuildSettings { @@ -69,19 +251,31 @@ export interface MuteConfig { selected_time_window: number; } +export type PublicMemberKeys = + | "id" + | "guild_id" + | "nick" + | "roles" + | "joined_at" + | "pending" + | "deaf" + | "mute" + | "premium_since"; + +export const PublicMemberProjection: PublicMemberKeys[] = [ + "id", + "guild_id", + "nick", + "roles", + "joined_at", + "pending", + "deaf", + "mute", + "premium_since", +]; + // @ts-ignore -export interface PublicMember extends Omit { +export type PublicMember = Pick> & { user: PublicUser; -} - -export const PublicMemberProjection = { - id: true, - guild_id: true, - nick: true, - roles: true, - joined_at: true, - pending: true, - deaf: true, - mute: true, - premium_since: true, + roles: string[]; // only role ids not objects }; diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts index 9daf042c..0c41a2eb 100644 --- a/util/src/entities/Message.ts +++ b/util/src/entities/Message.ts @@ -17,6 +17,7 @@ import { import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Webhook } from "./Webhook"; +import { Sticker } from "./Sticker"; export enum MessageType { DEFAULT = 0, @@ -51,7 +52,7 @@ export class Message extends BaseClass { channel: Channel; @RelationId((message: Message) => message.guild) - guild_id: string; + guild_id?: string; @JoinColumn({ name: "guild_id" }) @ManyToOne(() => Guild, (guild: Guild) => guild.id) @@ -85,7 +86,7 @@ export class Message extends BaseClass { @ManyToOne(() => Application, (application: Application) => application.id) application?: Application; - @Column() + @Column({ nullable: true }) content?: string; @Column() @@ -96,18 +97,18 @@ export class Message extends BaseClass { @UpdateDateColumn() edited_timestamp?: Date; - @Column() + @Column({ nullable: true }) tts?: boolean; - @Column() + @Column({ nullable: true }) mention_everyone?: boolean; - @RelationId((message: Message) => message.mention_users) + @RelationId((message: Message) => message.mentions) mention_user_ids: string[]; @JoinColumn({ name: "mention_user_ids" }) @ManyToMany(() => User, (user: User) => user.id) - mention_users: User[]; + mentions: User[]; @RelationId((message: Message) => message.mention_roles) mention_role_ids: string[]; @@ -123,44 +124,52 @@ export class Message extends BaseClass { @ManyToMany(() => Channel, (channel: Channel) => channel.id) mention_channels: Channel[]; - @Column("simple-json") + @Column({ type: "simple-json" }) attachments: Attachment[]; - @Column("simple-json") + @Column({ type: "simple-json" }) embeds: Embed[]; - @Column("simple-json") + @Column({ type: "simple-json" }) reactions: Reaction[]; - @Column({ type: "text" }) + @Column({ type: "text", nullable: true }) nonce?: string | number; - @Column() + @Column({ nullable: true }) pinned?: boolean; @Column({ type: "simple-enum", enum: MessageType }) type: MessageType; - @Column("simple-json") + @Column({ type: "simple-json", nullable: true }) activity?: { type: number; party_id: string; }; - @Column({ type: "bigint" }) + @Column({ type: "bigint", nullable: true }) flags?: bigint; - @Column("simple-json") - stickers?: any[]; + @RelationId((message: Message) => message.stickers) + sticker_ids: string[]; + + @JoinColumn({ name: "sticker_ids" }) + @ManyToMany(() => Sticker, (sticker: Sticker) => sticker.id) + stickers?: Sticker[]; - @Column("simple-json") + @Column({ type: "simple-json", nullable: true }) message_reference?: { message_id: string; channel_id?: string; guild_id?: string; }; - @Column("simple-json") + @JoinColumn({ name: "message_reference_id" }) + @ManyToOne(() => Message, (message: Message) => message.id) + referenced_message?: Message; + + @Column({ type: "simple-json", nullable: true }) interaction?: { id: string; type: InteractionType; @@ -169,7 +178,7 @@ export class Message extends BaseClass { // user: User; // TODO: autopopulate user }; - @Column("simple-json") + @Column({ type: "simple-json" }) components: MessageComponent[]; } diff --git a/util/src/entities/ReadState.ts b/util/src/entities/ReadState.ts index 70a38b84..650ee4c3 100644 --- a/util/src/entities/ReadState.ts +++ b/util/src/entities/ReadState.ts @@ -31,7 +31,7 @@ export class ReadState extends BaseClass { @ManyToOne(() => Message, (message: Message) => message.id) last_message?: Message; - @Column() + @Column({ nullable: true }) last_pin_timestamp?: Date; @Column() diff --git a/util/src/entities/Relationship.ts b/util/src/entities/Relationship.ts index 3e1280c7..cf3624bf 100644 --- a/util/src/entities/Relationship.ts +++ b/util/src/entities/Relationship.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { User } from "./User"; @@ -15,10 +15,10 @@ export class Relationship extends BaseClass { user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User, (user: User) => user.relationships) user: User; - @Column() + @Column({ nullable: true }) nickname?: string; @Column({ type: "simple-enum", enum: RelationshipType }) diff --git a/util/src/entities/Role.ts b/util/src/entities/Role.ts index e48fd293..7c6ce64e 100644 --- a/util/src/entities/Role.ts +++ b/util/src/entities/Role.ts @@ -32,7 +32,7 @@ export class Role extends BaseClass { @Column() position: number; - @Column({ type: "simple-json" }) + @Column({ type: "simple-json", nullable: true }) tags?: { bot_id?: string; integration_id?: string; diff --git a/util/src/entities/Sticker.ts b/util/src/entities/Sticker.ts new file mode 100644 index 00000000..12394207 --- /dev/null +++ b/util/src/entities/Sticker.ts @@ -0,0 +1,42 @@ +import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; +import { BaseClass } from "./BaseClass"; +import { Guild } from "./Guild"; + +export enum StickerType { + STANDARD = 1, + GUILD = 2, +} + +export enum StickerFormatType { + PNG = 1, + APNG = 2, + LOTTIE = 3, +} + +@Entity("stickers") +export class Sticker extends BaseClass { + @Column() + name: string; + + @Column({ nullable: true }) + description?: string; + + @Column() + tags: string; + + @Column() + pack_id: string; + + @Column({ nullable: true }) + guild_id?: string; + + @JoinColumn({ name: "guild_id" }) + @ManyToOne(() => Guild, (guild: Guild) => guild.id) + guild?: Guild; + + @Column({ type: "simple-enum", enum: StickerType }) + type: StickerType; + + @Column({ type: "simple-enum", enum: StickerFormatType }) + format_type: StickerFormatType; +} diff --git a/util/src/entities/Team.ts b/util/src/entities/Team.ts index fa1b0ed2..b37f368c 100644 --- a/util/src/entities/Team.ts +++ b/util/src/entities/Team.ts @@ -5,7 +5,7 @@ import { User } from "./User"; @Entity("teams") export class Team extends BaseClass { - @Column() + @Column({ nullable: true }) icon?: string; @RelationId((team: Team) => team.members) diff --git a/util/src/entities/TeamMember.ts b/util/src/entities/TeamMember.ts index f0b54c6f..d4c95397 100644 --- a/util/src/entities/TeamMember.ts +++ b/util/src/entities/TeamMember.ts @@ -12,7 +12,7 @@ export class TeamMember extends BaseClass { @Column({ type: "simple-enum", enum: TeamMemberState }) membership_state: TeamMemberState; - @Column("simple-array") + @Column({ type: "simple-array" }) permissions: string[]; @RelationId((member: TeamMember) => member.team) diff --git a/util/src/entities/Template.ts b/util/src/entities/Template.ts index 1dfc3b1b..2d8c9b6c 100644 --- a/util/src/entities/Template.ts +++ b/util/src/entities/Template.ts @@ -11,10 +11,10 @@ export class Template extends BaseClass { @Column() name: string; - @Column() + @Column({ nullable: true }) description?: string; - @Column() + @Column({ nullable: true }) usage_count?: number; @RelationId((template: Template) => template.creator) @@ -37,6 +37,6 @@ export class Template extends BaseClass { @ManyToOne(() => Guild, (guild: Guild) => guild.id) source_guild: Guild; - @Column("simple-json") + @Column({ type: "simple-json" }) serialized_source_guild: Guild; } diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts index 03cb3af4..c5f870fa 100644 --- a/util/src/entities/User.ts +++ b/util/src/entities/User.ts @@ -1,22 +1,34 @@ -import { Column, Entity, JoinColumn, OneToMany, RelationId } from "typeorm"; +import { Column, Entity, FindOneOptions, JoinColumn, 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 { Guild } from "./Guild"; - -export const PublicUserProjection = { - username: true, - discriminator: true, - id: true, - public_flags: true, - avatar: true, - accent_color: true, - banner: true, - bio: true, - bot: true, -}; + +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; @Entity("users") export class User extends BaseClass { @@ -30,115 +42,145 @@ export class User extends BaseClass { 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(); + this.discriminator = val.toString().padStart(4, "0"); } - @Column() + @Column({ nullable: true }) avatar?: string; // hash of the user avatar - @Column() - accent_color?: number; // banner color of user + @Column({ nullable: true }) + accent_color?: number = 0; // banner color of user - @Column() + @Column({ nullable: true }) banner?: string; // hash of the user banner - @Column() + @Column({ nullable: true }) phone?: string; // phone number of the user @Column() - desktop: boolean; // if the user has desktop app installed + desktop: boolean = false; // if the user has desktop app installed @Column() - 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 nitro + premium: boolean = false; // if user bought nitro @Column() - premium_type: number; // nitro level + premium_type: number = 0; // nitro 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 sents this field type true, if the generated message comes from a system generated author + system: boolean = false; // 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) + nsfw_allowed: boolean = false; // if the user is older than 18 (resp. Config) @Column() - mfa_enabled: boolean; // if multi factor authentication is enabled + mfa_enabled: boolean = false; // if multi factor authentication is enabled @Column() - created_at: Date; // registration date + created_at: Date = new Date(); // registration date @Column() - verified: boolean; // if the user is offically verified + verified: boolean = false; // if the user is offically 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() + @Column({ nullable: true }) email?: string; // email of the user @Column({ type: "bigint" }) - flags: bigint; // UserFlags + flags: bigint = BigInt(0); // UserFlags @Column({ type: "bigint" }) - public_flags: bigint; - - @RelationId((user: User) => user.guilds) - guild_ids: string[]; // array of guild ids the user is part of - - @JoinColumn({ name: "guild_ids" }) - @OneToMany(() => Guild, (guild: Guild) => guild.id) - guilds: Guild[]; + public_flags: bigint = BigInt(0); @RelationId((user: User) => user.relationships) relationship_ids: string[]; // array of guild ids the user is part of @JoinColumn({ name: "relationship_ids" }) - @OneToMany(() => User, (user: User) => user.id) + @OneToMany(() => Relationship, (relationship: Relationship) => relationship.user, { cascade: true }) relationships: Relationship[]; @RelationId((user: User) => user.connected_accounts) connected_account_ids: string[]; // array of guild ids the user is part of @JoinColumn({ name: "connected_account_ids" }) - @OneToMany(() => ConnectedAccount, (account: ConnectedAccount) => account.id) + @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) - }; + hash?: string; // hash of the password, salt is saved in password (bcrypt) + } = { valid_tokens_since: new Date() }; @Column({ type: "simple-array" }) - fingerprints: string[]; // array of fingerprints -> used to prevent multiple accounts - - @Column("simple-json") - settings: UserSettings; - - static async getPublicUser(user_id: string, additional_fields?: any) { - const user = await User.findOne( - { id: user_id }, - { - ...PublicUserProjection, - ...additional_fields, - } - ); + fingerprints: string[] = []; // array of fingerprints -> used to prevent multiple accounts + + @Column({ type: "simple-json" }) + settings: UserSettings = defaultSettings; + + static async getPublicUser(user_id: string, opts?: FindOneOptions) { + const user = await User.findOne(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; @@ -184,18 +226,6 @@ export interface UserSettings { timezone_offset: number; // e.g -60 } -// Private user data that should never get sent to the client -export interface PublicUser { - id: string; - discriminator: string; - username: string; - avatar?: string; - accent_color?: number; - banner?: string; - public_flags: bigint; - bot: boolean; -} - export class UserFlags extends BitField { static FLAGS = { DISCORD_EMPLOYEE: BigInt(1) << BigInt(0), diff --git a/util/src/entities/VoiceState.ts b/util/src/entities/VoiceState.ts index 6707a575..e9d3dfa2 100644 --- a/util/src/entities/VoiceState.ts +++ b/util/src/entities/VoiceState.ts @@ -42,7 +42,7 @@ export class VoiceState extends BaseClass { @Column() self_mute: boolean; - @Column() + @Column({ nullable: true }) self_stream?: boolean; @Column() diff --git a/util/src/entities/Webhook.ts b/util/src/entities/Webhook.ts index dc929c18..a75cb959 100644 --- a/util/src/entities/Webhook.ts +++ b/util/src/entities/Webhook.ts @@ -18,13 +18,13 @@ export class Webhook extends BaseClass { @Column({ type: "simple-enum", enum: WebhookType }) type: WebhookType; - @Column() + @Column({ nullable: true }) name?: string; - @Column() + @Column({ nullable: true }) avatar?: string; - @Column() + @Column({ nullable: true }) token?: string; @RelationId((webhook: Webhook) => webhook.guild) diff --git a/util/src/entities/schema.json b/util/src/entities/schema.json deleted file mode 100644 index 7f192690..00000000 --- a/util/src/entities/schema.json +++ /dev/null @@ -1,6218 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "definitions": { - "Activity": { - "properties": { - "application_id": { - "type": "string" - }, - "assets": { - "properties": { - "large_image": { - "type": "string" - }, - "large_text": { - "type": "string" - }, - "small_image": { - "type": "string" - }, - "small_text": { - "type": "string" - } - }, - "type": "object" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "details": { - "type": "string" - }, - "emoji": { - "properties": { - "amimated": { - "type": "boolean" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - } - }, - "type": "object" - }, - "flags": { - "type": "bigint" - }, - "instance": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "party": { - "properties": { - "id": { - "type": "string" - }, - "size": { - "items": [ - { - "type": "number" - }, - { - "type": "number" - } - ], - "maxItems": 2, - "minItems": 2, - "type": "array" - } - }, - "type": "object" - }, - "secrets": { - "properties": { - "join": { - "type": "string" - }, - "match": { - "type": "string" - }, - "spectate": { - "type": "string" - } - }, - "type": "object" - }, - "state": { - "type": "string" - }, - "timestamps": { - "items": { - "properties": { - "end": { - "type": "number" - }, - "start": { - "type": "number" - } - }, - "type": "object" - }, - "type": "array" - }, - "type": { - "$ref": "#/definitions/ActivityType" - }, - "url": { - "type": "string" - } - }, - "type": "object" - }, - "ActivityType": { - "enum": [ - 0, - 1, - 2, - 4, - 5 - ], - "type": "number" - }, - "AllowedMentions": { - "properties": { - "parse": { - "items": { - "enum": [ - "everyone", - "roles", - "users" - ], - "type": "string" - }, - "type": "array" - }, - "replied_user": { - "type": "boolean" - }, - "roles": { - "items": { - "type": "string" - }, - "type": "array" - }, - "users": { - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - }, - "Application": { - "properties": { - "bot_public": { - "type": "boolean" - }, - "bot_require_code_grant": { - "type": "boolean" - }, - "construct": { - }, - "cover_image": { - "type": "string" - }, - "description": { - "type": "string" - }, - "flags": { - "type": "number" - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "guild_id": { - "type": "string" - }, - "icon": { - "type": "string" - }, - "id": { - "type": "string" - }, - "metadata": { - }, - "name": { - "type": "string" - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "owner_id": { - "type": "string" - }, - "primary_sku_id": { - "type": "string" - }, - "privacy_policy_url": { - "type": "string" - }, - "rpc_origins": { - "items": { - "type": "string" - }, - "type": "array" - }, - "slug": { - "type": "string" - }, - "summary": { - "type": "string" - }, - "team": { - "$ref": "#/definitions/Team" - }, - "team_id": { - "type": "string" - }, - "terms_of_service_url": { - "type": "string" - }, - "verify_key": { - "type": "string" - } - }, - "type": "object" - }, - "ApplicationCommand": { - "properties": { - "application_id": { - "type": "string" - }, - "description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "options": { - "items": { - "$ref": "#/definitions/ApplicationCommandOption" - }, - "type": "array" - } - }, - "type": "object" - }, - "ApplicationCommandCreateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "allOf": [ - { - "$ref": "#/definitions/ApplicationCommand" - }, - { - "properties": { - "guild_id": { - "type": "string" - } - }, - "type": "object" - } - ] - }, - "event": { - "enum": [ - "APPLICATION_COMMAND_CREATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "ApplicationCommandDeleteEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "allOf": [ - { - "$ref": "#/definitions/ApplicationCommand" - }, - { - "properties": { - "guild_id": { - "type": "string" - } - }, - "type": "object" - } - ] - }, - "event": { - "enum": [ - "APPLICATION_COMMAND_DELETE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "ApplicationCommandInteractionData": { - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "options": { - "items": { - "$ref": "#/definitions/ApplicationCommandInteractionDataOption" - }, - "type": "array" - } - }, - "type": "object" - }, - "ApplicationCommandInteractionDataOption": { - "properties": { - "name": { - "type": "string" - }, - "options": { - "items": { - "$ref": "#/definitions/ApplicationCommandInteractionDataOption" - }, - "type": "array" - }, - "value": { - } - }, - "type": "object" - }, - "ApplicationCommandOption": { - "properties": { - "choices": { - "items": { - "$ref": "#/definitions/ApplicationCommandOptionChoice" - }, - "type": "array" - }, - "description": { - "type": "string" - }, - "name": { - "type": "string" - }, - "options": { - "items": { - "$ref": "#/definitions/ApplicationCommandOption" - }, - "type": "array" - }, - "required": { - "type": "boolean" - }, - "type": { - "$ref": "#/definitions/ApplicationCommandOptionType" - } - }, - "type": "object" - }, - "ApplicationCommandOptionChoice": { - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": [ - "string", - "number" - ] - } - }, - "type": "object" - }, - "ApplicationCommandOptionType": { - "enum": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8 - ], - "type": "number" - }, - "ApplicationCommandPayload": { - "allOf": [ - { - "$ref": "#/definitions/ApplicationCommand" - }, - { - "properties": { - "guild_id": { - "type": "string" - } - }, - "type": "object" - } - ] - }, - "ApplicationCommandUpdateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "allOf": [ - { - "$ref": "#/definitions/ApplicationCommand" - }, - { - "properties": { - "guild_id": { - "type": "string" - } - }, - "type": "object" - } - ] - }, - "event": { - "enum": [ - "APPLICATION_COMMAND_UPDATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "Attachment": { - "properties": { - "content_type": { - "type": "string" - }, - "filename": { - "type": "string" - }, - "height": { - "type": "number" - }, - "id": { - "type": "string" - }, - "proxy_url": { - "type": "string" - }, - "size": { - "type": "number" - }, - "url": { - "type": "string" - }, - "width": { - "type": "number" - } - }, - "type": "object" - }, - "AuditLogChange": { - "properties": { - "key": { - "type": "string" - }, - "new_value": { - "$ref": "#/definitions/AuditLogChangeValue" - }, - "old_value": { - "$ref": "#/definitions/AuditLogChangeValue" - } - }, - "type": "object" - }, - "AuditLogChangeValue": { - "properties": { - "$add": { - "items": { - "properties": { - }, - "type": "object" - }, - "type": "array" - }, - "$remove": { - "items": { - "properties": { - }, - "type": "object" - }, - "type": "array" - }, - "afk_channel_id": { - "type": "string" - }, - "afk_timeout": { - "type": "number" - }, - "allow": { - "type": "string" - }, - "application_id": { - "type": "string" - }, - "avatar_hash": { - "type": "string" - }, - "banner_hash": { - "type": "string" - }, - "bitrate": { - "type": "number" - }, - "channel_id": { - "type": "string" - }, - "code": { - "type": "string" - }, - "color": { - "type": "number" - }, - "deaf": { - "type": "boolean" - }, - "default_message_notifications": { - "type": "number" - }, - "deny": { - "type": "string" - }, - "description": { - "type": "string" - }, - "discovery_splash_hash": { - "type": "string" - }, - "enable_emoticons": { - "type": "boolean" - }, - "expire_behavior": { - "type": "number" - }, - "expire_grace_period": { - "type": "number" - }, - "explicit_content_filter": { - "type": "number" - }, - "hoist": { - "type": "boolean" - }, - "icon_hash": { - "type": "string" - }, - "id": { - "type": "string" - }, - "inviter_id": { - "type": "string" - }, - "max_age": { - "type": "number" - }, - "max_uses": { - "type": "number" - }, - "mentionable": { - "type": "boolean" - }, - "mfa_level": { - "type": "number" - }, - "mute": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "nick": { - "type": "string" - }, - "nsfw": { - "type": "boolean" - }, - "owner_id": { - "type": "string" - }, - "permission_overwrites": { - "items": { - "$ref": "#/definitions/ChannelPermissionOverwrite" - }, - "type": "array" - }, - "permissions": { - "type": "string" - }, - "position": { - "type": "number" - }, - "preferred_locale": { - "type": "string" - }, - "prune_delete_days": { - "type": "number" - }, - "public_updates_channel_id": { - "type": "string" - }, - "rate_limit_per_user": { - "type": "number" - }, - "region": { - "type": "string" - }, - "rules_channel_id": { - "type": "string" - }, - "splash_hash": { - "type": "string" - }, - "system_channel_id": { - "type": "string" - }, - "temporary": { - "type": "boolean" - }, - "topic": { - "type": "string" - }, - "type": { - "type": "number" - }, - "user_limit": { - "type": "number" - }, - "uses": { - "type": "number" - }, - "vanity_url_code": { - "type": "string" - }, - "verification_level": { - "type": "number" - }, - "widget_channel_id": { - "type": "string" - }, - "widget_enabled": { - "type": "boolean" - } - }, - "type": "object" - }, - "AuditLogEntry": { - "properties": { - "action_type": { - "$ref": "#/definitions/AuditLogEvents" - }, - "changes": { - "items": { - "$ref": "#/definitions/AuditLogChange" - }, - "type": "array" - }, - "construct": { - }, - "id": { - "type": "string" - }, - "metadata": { - }, - "options": { - "properties": { - "channel_id": { - "type": "string" - }, - "count": { - "type": "string" - }, - "delete_member_days": { - "type": "string" - }, - "id": { - "type": "string" - }, - "members_removed": { - "type": "string" - }, - "messaged_id": { - "type": "string" - }, - "role_name": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "type": "object" - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "reason": { - "type": "string" - }, - "target": { - "$ref": "#/definitions/User" - }, - "target_id": { - "type": "string" - }, - "user": { - "$ref": "#/definitions/User" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "AuditLogEvents": { - "enum": [ - 1, - 10, - 11, - 12, - 13, - 14, - 15, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 30, - 31, - 32, - 40, - 41, - 42, - 50, - 51, - 52, - 60, - 61, - 62, - 72, - 73, - 74, - 75, - 80, - 81, - 82 - ], - "type": "number" - }, - "Ban": { - "properties": { - "construct": { - }, - "executor": { - "$ref": "#/definitions/User" - }, - "executor_id": { - "type": "string" - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "guild_id": { - "type": "string" - }, - "id": { - "type": "string" - }, - "ip": { - "type": "string" - }, - "metadata": { - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "reason": { - "type": "string" - }, - "user": { - "$ref": "#/definitions/User" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "BaseClass": { - "properties": { - "construct": { - }, - "id": { - "type": "string" - }, - "metadata": { - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - } - }, - "type": "object" - }, - "BigInt": { - "properties": { - "__@toStringTag": { - "enum": [ - "BigInt" - ], - "type": "string" - } - }, - "type": "object" - }, - "BitField": { - "description": "Data structure that makes it easy to interact with a bitfield.", - "properties": { - "bitfield": { - "type": "bigint" - } - }, - "type": "object" - }, - "BitFieldResolvable": { - "anyOf": [ - { - "$ref": "#/definitions/BigInt" - }, - { - "$ref": "#/definitions/BitField" - }, - { - "items": { - "$ref": "#/definitions/BitFieldResolvable" - }, - "type": "array" - }, - { - "type": [ - "string", - "number" - ] - } - ] - }, - "CUSTOMEVENTS": { - "enum": [ - "INVALIDATED" - ], - "type": "string" - }, - "Channel": { - "properties": { - "bitrate": { - "type": "number" - }, - "construct": { - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "default_auto_archive_duration": { - "type": "number" - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "guild_id": { - "type": "string" - }, - "id": { - "type": "string" - }, - "last_message": { - "$ref": "#/definitions/Message" - }, - "last_message_id": { - "type": "string" - }, - "last_pin_timestamp": { - "type": "number" - }, - "metadata": { - }, - "name": { - "type": "string" - }, - "nsfw": { - "type": "boolean" - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "owner": { - "$ref": "#/definitions/User" - }, - "owner_id": { - "type": "string" - }, - "parent": { - "$ref": "#/definitions/Channel" - }, - "parent_id": { - "type": "string" - }, - "permission_overwrites": { - "items": { - "$ref": "#/definitions/ChannelPermissionOverwrite" - }, - "type": "array" - }, - "position": { - "type": "number" - }, - "rate_limit_per_user": { - "type": "number" - }, - "recipient_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "recipients": { - "items": { - "$ref": "#/definitions/User" - }, - "type": "array" - }, - "topic": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/ChannelType" - }, - "user_limit": { - "type": "number" - }, - "video_quality_mode": { - "type": "number" - } - }, - "type": "object" - }, - "ChannelCreateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "$ref": "#/definitions/Channel" - }, - "event": { - "enum": [ - "CHANNEL_CREATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "ChannelDeleteEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "$ref": "#/definitions/Channel" - }, - "event": { - "enum": [ - "CHANNEL_DELETE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "ChannelPermissionOverwrite": { - "properties": { - "allow": { - "type": "bigint" - }, - "deny": { - "type": "bigint" - }, - "id": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/ChannelPermissionOverwriteType" - } - }, - "type": "object" - }, - "ChannelPermissionOverwriteType": { - "enum": [ - 0, - 1 - ], - "type": "number" - }, - "ChannelPinsUpdateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "last_pin_timestamp": { - "type": "number" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "CHANNEL_PINS_UPDATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "ChannelType": { - "enum": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6 - ], - "type": "number" - }, - "ChannelUpdateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "$ref": "#/definitions/Channel" - }, - "event": { - "enum": [ - "CHANNEL_UPDATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "Channel_1": { - "type": "object" - }, - "ClientStatus": { - "properties": { - "desktop": { - "type": "string" - }, - "mobile": { - "type": "string" - }, - "web": { - "type": "string" - } - }, - "type": "object" - }, - "ConfigEntity": { - "properties": { - "construct": { - }, - "id": { - "type": "string" - }, - "metadata": { - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "value": { - "$ref": "#/definitions/ConfigValue" - } - }, - "type": "object" - }, - "ConfigValue": { - "properties": { - "cdn": { - "properties": { - "endpoint": { - "type": [ - "null", - "string" - ] - }, - "endpointClient": { - "type": [ - "null", - "string" - ] - } - }, - "type": "object" - }, - "gateway": { - "properties": { - "endpoint": { - "type": [ - "null", - "string" - ] - }, - "endpointClient": { - "type": [ - "null", - "string" - ] - } - }, - "type": "object" - }, - "general": { - "properties": { - "instance_id": { - "type": "string" - } - }, - "type": "object" - }, - "kafka": { - "properties": { - "brokers": { - "anyOf": [ - { - "items": { - "$ref": "#/definitions/KafkaBroker" - }, - "type": "array" - }, - { - "type": "null" - } - ] - } - }, - "type": "object" - }, - "limits": { - "properties": { - "channel": { - "properties": { - "maxPins": { - "type": "number" - }, - "maxTopic": { - "type": "number" - } - }, - "type": "object" - }, - "guild": { - "properties": { - "hideOfflineMember": { - "type": "number" - }, - "maxChannels": { - "type": "number" - }, - "maxChannelsInCategory": { - "type": "number" - }, - "maxMembers": { - "type": "number" - }, - "maxRoles": { - "type": "number" - } - }, - "type": "object" - }, - "message": { - "properties": { - "maxAttachmentSize": { - "type": "number" - }, - "maxBulkDelete": { - "type": "number" - }, - "maxCharacters": { - "type": "number" - }, - "maxReactions": { - "type": "number" - }, - "maxTTSCharacters": { - "type": "number" - } - }, - "type": "object" - }, - "rate": { - "properties": { - "error": { - "$ref": "#/definitions/RateLimitOptions" - }, - "global": { - "$ref": "#/definitions/RateLimitOptions" - }, - "ip": { - "$ref": "#/definitions/Omit" - }, - "routes": { - "properties": { - "auth": { - "properties": { - "login": { - "$ref": "#/definitions/RateLimitOptions" - }, - "register": { - "$ref": "#/definitions/RateLimitOptions" - } - }, - "type": "object" - }, - "channel": { - "$ref": "#/definitions/RateLimitOptions" - }, - "guild": { - "$ref": "#/definitions/RateLimitOptions" - }, - "webhook": { - "$ref": "#/definitions/RateLimitOptions" - } - }, - "type": "object" - } - }, - "type": "object" - }, - "user": { - "properties": { - "maxFriends": { - "type": "number" - }, - "maxGuilds": { - "type": "number" - }, - "maxUsername": { - "type": "number" - } - }, - "type": "object" - } - }, - "type": "object" - }, - "login": { - "properties": { - "requireCaptcha": { - "type": "boolean" - } - }, - "type": "object" - }, - "permissions": { - "properties": { - "user": { - "properties": { - "createGuilds": { - "type": "boolean" - } - }, - "type": "object" - } - }, - "type": "object" - }, - "rabbitmq": { - "properties": { - "host": { - "type": [ - "null", - "string" - ] - } - }, - "type": "object" - }, - "regions": { - "properties": { - "available": { - "items": { - "$ref": "#/definitions/Region" - }, - "type": "array" - }, - "default": { - "type": "string" - } - }, - "type": "object" - }, - "register": { - "properties": { - "allowMultipleAccounts": { - "type": "boolean" - }, - "allowNewRegistration": { - "type": "boolean" - }, - "blockProxies": { - "type": "boolean" - }, - "dateOfBirth": { - "properties": { - "minimum": { - "type": "number" - }, - "necessary": { - "type": "boolean" - } - }, - "type": "object" - }, - "email": { - "properties": { - "allowlist": { - "type": "boolean" - }, - "blocklist": { - "type": "boolean" - }, - "domains": { - "items": { - "type": "string" - }, - "type": "array" - }, - "necessary": { - "type": "boolean" - } - }, - "type": "object" - }, - "password": { - "properties": { - "minLength": { - "type": "number" - }, - "minNumbers": { - "type": "number" - }, - "minSymbols": { - "type": "number" - }, - "minUpperCase": { - "type": "number" - } - }, - "type": "object" - }, - "requireCaptcha": { - "type": "boolean" - }, - "requireInvite": { - "type": "boolean" - } - }, - "type": "object" - }, - "security": { - "properties": { - "autoUpdate": { - "type": [ - "number", - "boolean" - ] - }, - "captcha": { - "properties": { - "enabled": { - "type": "boolean" - }, - "secret": { - "type": [ - "null", - "string" - ] - }, - "service": { - "anyOf": [ - { - "enum": [ - "hcaptcha", - "recaptcha" - ], - "type": "string" - }, - { - "type": "null" - } - ] - }, - "sitekey": { - "type": [ - "null", - "string" - ] - } - }, - "type": "object" - }, - "forwadedFor": { - "type": [ - "null", - "string" - ] - }, - "ipdataApiKey": { - "type": [ - "null", - "string" - ] - }, - "jwtSecret": { - "type": "string" - }, - "requestSignature": { - "type": "string" - } - }, - "type": "object" - } - }, - "type": "object" - }, - "ConnectedAccount": { - "properties": { - "access_token": { - "type": "string" - }, - "construct": { - }, - "friend_sync": { - "type": "boolean" - }, - "id": { - "type": "string" - }, - "metadata": { - }, - "name": { - "type": "string" - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "revoked": { - "type": "boolean" - }, - "show_activity": { - "type": "boolean" - }, - "type": { - "type": "string" - }, - "verifie": { - "type": "boolean" - }, - "visibility": { - "type": "number" - } - }, - "type": "object" - }, - "EVENT": { - "enum": [ - "APPLICATION_COMMAND_CREATE", - "APPLICATION_COMMAND_DELETE", - "APPLICATION_COMMAND_UPDATE", - "CHANNEL_CREATE", - "CHANNEL_DELETE", - "CHANNEL_PINS_UPDATE", - "CHANNEL_UPDATE", - "GUILD_BAN_ADD", - "GUILD_BAN_REMOVE", - "GUILD_CREATE", - "GUILD_DELETE", - "GUILD_EMOJI_UPDATE", - "GUILD_INTEGRATIONS_UPDATE", - "GUILD_MEMBERS_CHUNK", - "GUILD_MEMBER_ADD", - "GUILD_MEMBER_REMOVE", - "GUILD_MEMBER_SPEAKING", - "GUILD_MEMBER_UPDATE", - "GUILD_ROLE_CREATE", - "GUILD_ROLE_DELETE", - "GUILD_ROLE_UPDATE", - "GUILD_UPDATE", - "INTERACTION_CREATE", - "INVALIDATED", - "INVITE_CREATE", - "INVITE_DELETE", - "MESSAGE_ACK", - "MESSAGE_CREATE", - "MESSAGE_DELETE", - "MESSAGE_DELETE_BULK", - "MESSAGE_REACTION_ADD", - "MESSAGE_REACTION_REMOVE", - "MESSAGE_REACTION_REMOVE_ALL", - "MESSAGE_REACTION_REMOVE_EMOJI", - "MESSAGE_UPDATE", - "PRESENCE_UPDATE", - "READY", - "RELATIONSHIP_ADD", - "RELATIONSHIP_REMOVE", - "TYPING_START", - "USER_UPDATE", - "VOICE_SERVER_UPDATE", - "VOICE_STATE_UPDATE", - "WEBHOOKS_UPDATE" - ], - "type": "string" - }, - "EVENTEnum": { - "enum": [ - "APPLICATION_COMMAND_CREATE", - "APPLICATION_COMMAND_DELETE", - "APPLICATION_COMMAND_UPDATE", - "CHANNEL_CREATE", - "CHANNEL_DELETE", - "CHANNEL_PINS_UPDATE", - "CHANNEL_UPDATE", - "GUILD_BAN_ADD", - "GUILD_BAN_REMOVE", - "GUILD_CREATE", - "GUILD_DELETE", - "GUILD_EMOJI_UPDATE", - "GUILD_INTEGRATIONS_UPDATE", - "GUILD_MEMBERS_CHUNK", - "GUILD_MEMBER_ADD", - "GUILD_MEMBER_REMOVE", - "GUILD_MEMBER_SPEAKING", - "GUILD_MEMBER_UPDATE", - "GUILD_ROLE_CREATE", - "GUILD_ROLE_DELETE", - "GUILD_ROLE_UPDATE", - "GUILD_UPDATE", - "INTERACTION_CREATE", - "INVITE_CREATE", - "INVITE_DELETE", - "MESSAGE_CREATE", - "MESSAGE_DELETE", - "MESSAGE_DELETE_BULK", - "MESSAGE_REACTION_ADD", - "MESSAGE_REACTION_REMOVE", - "MESSAGE_REACTION_REMOVE_ALL", - "MESSAGE_REACTION_REMOVE_EMOJI", - "MESSAGE_UPDATE", - "PRESENCE_UPDATE", - "READY", - "TYPING_START", - "USER_UPDATE", - "VOICE_SERVER_UPDATE", - "VOICE_STATE_UPDATE", - "WEBHOOKS_UPDATE" - ], - "type": "string" - }, - "Embed": { - "properties": { - "author": { - "properties": { - "icon_url": { - "type": "string" - }, - "name": { - "type": "string" - }, - "proxy_icon_url": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "type": "object" - }, - "color": { - "type": "number" - }, - "description": { - "type": "string" - }, - "fields": { - "items": { - "properties": { - "inline": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "type": "object" - }, - "type": "array" - }, - "footer": { - "properties": { - "icon_url": { - "type": "string" - }, - "proxy_icon_url": { - "type": "string" - }, - "text": { - "type": "string" - } - }, - "type": "object" - }, - "image": { - "$ref": "#/definitions/EmbedImage" - }, - "provider": { - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "type": "object" - }, - "thumbnail": { - "$ref": "#/definitions/EmbedImage" - }, - "timestamp": { - "format": "date-time", - "type": "string" - }, - "title": { - "type": "string" - }, - "type": { - "enum": [ - "article", - "gifv", - "image", - "link", - "rich", - "video" - ], - "type": "string" - }, - "url": { - "type": "string" - }, - "video": { - "$ref": "#/definitions/EmbedImage" - } - }, - "type": "object" - }, - "EmbedImage": { - "properties": { - "height": { - "type": "number" - }, - "proxy_url": { - "type": "string" - }, - "url": { - "type": "string" - }, - "width": { - "type": "number" - } - }, - "type": "object" - }, - "EmbedType": { - "enum": [ - "article", - "gifv", - "image", - "link", - "rich", - "video" - ], - "type": "string" - }, - "Emoji": { - "properties": { - "animated": { - "type": "boolean" - }, - "available": { - "type": "boolean" - }, - "construct": { - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "guild_id": { - "type": "string" - }, - "id": { - "type": "string" - }, - "managed": { - "type": "boolean" - }, - "metadata": { - }, - "name": { - "type": "string" - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "require_colons": { - "type": "boolean" - }, - "role_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "roles": { - "items": { - "$ref": "#/definitions/Role" - }, - "type": "array" - }, - "url": { - "type": "string" - } - }, - "type": "object" - }, - "Event": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - }, - "event": { - "$ref": "#/definitions/EVENT" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "EventOpts": { - "properties": { - "acknowledge": { - "$ref": "#/definitions/Function" - }, - "cancel": { - "$ref": "#/definitions/Function" - }, - "channel": { - "$ref": "#/definitions/Channel_1" - }, - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - }, - "event": { - "$ref": "#/definitions/EVENT" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "Function": { - "properties": { - "arguments": { - }, - "caller": { - "$ref": "#/definitions/Function" - }, - "length": { - "type": "number" - }, - "name": { - "type": "string" - }, - "prototype": { - } - }, - "type": "object" - }, - "Guild": { - "properties": { - "afk_channel": { - "$ref": "#/definitions/Channel" - }, - "afk_channel_id": { - "type": "string" - }, - "afk_timeout": { - "type": "number" - }, - "banner": { - "type": "string" - }, - "channel_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "channels": { - "items": { - "$ref": "#/definitions/Channel" - }, - "type": "array" - }, - "construct": { - }, - "default_message_notifications": { - "type": "number" - }, - "description": { - "type": "string" - }, - "discovery_splash": { - "type": "string" - }, - "emoji_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "emojis": { - "items": { - "$ref": "#/definitions/Emoji" - }, - "type": "array" - }, - "explicit_content_filter": { - "type": "number" - }, - "features": { - "items": { - "type": "string" - }, - "type": "array" - }, - "icon": { - "type": "string" - }, - "id": { - "type": "string" - }, - "large": { - "type": "boolean" - }, - "max_members": { - "type": "number" - }, - "max_presences": { - "type": "number" - }, - "max_video_channel_users": { - "type": "number" - }, - "member_count": { - "type": "number" - }, - "member_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "members": { - "items": { - "$ref": "#/definitions/Member" - }, - "type": "array" - }, - "metadata": { - }, - "mfa_level": { - "type": "number" - }, - "name": { - "type": "string" - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "owner": { - "$ref": "#/definitions/User" - }, - "owner_id": { - "type": "string" - }, - "preferred_locale": { - "type": "string" - }, - "premium_subscription_count": { - "type": "number" - }, - "premium_tier": { - "type": "number" - }, - "presence_count": { - "type": "number" - }, - "public_updates_channel": { - "$ref": "#/definitions/Channel" - }, - "public_updates_channel_id": { - "type": "string" - }, - "region": { - "type": "string" - }, - "role_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "roles": { - "items": { - "$ref": "#/definitions/Role" - }, - "type": "array" - }, - "rules_channel": { - "type": "string" - }, - "rules_channel_id": { - "type": "string" - }, - "splash": { - "type": "string" - }, - "system_channel": { - "$ref": "#/definitions/Channel" - }, - "system_channel_flags": { - "type": "number" - }, - "system_channel_id": { - "type": "string" - }, - "unavailable": { - "type": "boolean" - }, - "vanity_url": { - "$ref": "#/definitions/Invite" - }, - "vanity_url_code": { - "type": "string" - }, - "verification_level": { - "type": "number" - }, - "voice_state_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "voice_states": { - "items": { - "$ref": "#/definitions/VoiceState" - }, - "type": "array" - }, - "welcome_screen": { - "properties": { - "description": { - "type": "string" - }, - "enabled": { - "type": "boolean" - }, - "welcome_channels": { - "items": { - "properties": { - "channel_id": { - "type": "string" - }, - "description": { - "type": "string" - }, - "emoji_id": { - "type": "string" - }, - "emoji_name": { - "type": "string" - } - }, - "type": "object" - }, - "type": "array" - } - }, - "type": "object" - }, - "widget_channel": { - "$ref": "#/definitions/Channel" - }, - "widget_channel_id": { - "type": "string" - }, - "widget_enabled": { - "type": "boolean" - } - }, - "type": "object" - }, - "GuildBanAddEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "guild_id": { - "type": "string" - }, - "user": { - "$ref": "#/definitions/User" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "GUILD_BAN_ADD" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "GuildBanRemoveEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "guild_id": { - "type": "string" - }, - "user": { - "$ref": "#/definitions/User" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "GUILD_BAN_REMOVE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "GuildCreateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "$ref": "#/definitions/Guild" - }, - "event": { - "enum": [ - "GUILD_CREATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "GuildDeleteEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "id": { - "type": "string" - }, - "unavailable": { - "type": "boolean" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "GUILD_DELETE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "GuildEmojiUpdateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "emojis": { - "items": { - "$ref": "#/definitions/Emoji" - }, - "type": "array" - }, - "guild_id": { - "type": "string" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "GUILD_EMOJI_UPDATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "GuildIntegrationUpdateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "guild_id": { - "type": "string" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "GUILD_INTEGRATIONS_UPDATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "GuildMemberAddEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "allOf": [ - { - "$ref": "#/definitions/PublicMember" - }, - { - "properties": { - "guild_id": { - "type": "string" - } - }, - "type": "object" - } - ] - }, - "event": { - "enum": [ - "GUILD_MEMBER_ADD" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "GuildMemberRemoveEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "guild_id": { - "type": "string" - }, - "user": { - "$ref": "#/definitions/User" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "GUILD_MEMBER_REMOVE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "GuildMemberUpdateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "guild_id": { - "type": "string" - }, - "joined_at": { - "format": "date-time", - "type": "string" - }, - "nick": { - "type": "string" - }, - "pending": { - "type": "boolean" - }, - "premium_since": { - "type": "number" - }, - "roles": { - "items": { - "type": "string" - }, - "type": "array" - }, - "user": { - "$ref": "#/definitions/User" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "GUILD_MEMBER_UPDATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "GuildMembersChunkEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "chunk_count": { - "type": "number" - }, - "chunk_index": { - "type": "number" - }, - "guild_id": { - "type": "string" - }, - "members": { - "items": { - "$ref": "#/definitions/PublicMember" - }, - "type": "array" - }, - "nonce": { - "type": "string" - }, - "not_found": { - "items": { - "type": "string" - }, - "type": "array" - }, - "presences": { - "items": { - "$ref": "#/definitions/Presence" - }, - "type": "array" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "GUILD_MEMBERS_CHUNK" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "GuildRoleCreateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "guild_id": { - "type": "string" - }, - "role": { - "$ref": "#/definitions/Role" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "GUILD_ROLE_CREATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "GuildRoleDeleteEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "guild_id": { - "type": "string" - }, - "role_id": { - "type": "string" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "GUILD_ROLE_DELETE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "GuildRoleUpdateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "guild_id": { - "type": "string" - }, - "role": { - "$ref": "#/definitions/Role" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "GUILD_ROLE_UPDATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "GuildUpdateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "$ref": "#/definitions/Guild" - }, - "event": { - "enum": [ - "GUILD_UPDATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "Intents": { - "properties": { - "bitfield": { - "type": "bigint" - } - }, - "type": "object" - }, - "Interaction": { - "properties": { - "channel_id": { - "type": "string" - }, - "data": { - "properties": { - }, - "type": "object" - }, - "guild_id": { - "type": "string" - }, - "id": { - "type": "string" - }, - "member_id": { - "type": "string" - }, - "token": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/InteractionType" - }, - "version": { - "type": "number" - } - }, - "type": "object" - }, - "InteractionApplicationCommandCallbackData": { - "properties": { - "allowed_mentions": { - "$ref": "#/definitions/AllowedMentions" - }, - "content": { - "type": "string" - }, - "embeds": { - "items": { - "$ref": "#/definitions/Embed" - }, - "type": "array" - }, - "tts": { - "type": "boolean" - } - }, - "type": "object" - }, - "InteractionCreateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "$ref": "#/definitions/Interaction" - }, - "event": { - "enum": [ - "INTERACTION_CREATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "InteractionResponseType": { - "enum": [ - 1, - 2, - 3, - 4, - 5 - ], - "type": "number" - }, - "InteractionType": { - "enum": [ - 1, - 2 - ], - "type": "number" - }, - "InvalidatedEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - }, - "event": { - "enum": [ - "INVALIDATED" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "Invite": { - "properties": { - "channel": { - "$ref": "#/definitions/Channel" - }, - "channel_id": { - "type": "string" - }, - "code": { - "type": "string" - }, - "construct": { - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "expires_at": { - "format": "date-time", - "type": "string" - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "guild_id": { - "type": "string" - }, - "id": { - "type": "string" - }, - "inviter": { - "$ref": "#/definitions/User" - }, - "inviter_id": { - "type": "string" - }, - "max_age": { - "type": "number" - }, - "max_uses": { - "type": "number" - }, - "metadata": { - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "target_user": { - "type": "string" - }, - "target_user_id": { - "type": "string" - }, - "target_user_type": { - "type": "number" - }, - "temporary": { - "type": "boolean" - }, - "uses": { - "type": "number" - } - }, - "type": "object" - }, - "InviteCreateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "allOf": [ - { - "$ref": "#/definitions/Omit" - }, - { - "properties": { - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - } - }, - "type": "object" - } - ] - }, - "event": { - "enum": [ - "INVITE_CREATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "InviteDeleteEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "channel_id": { - "type": "string" - }, - "code": { - "type": "string" - }, - "guild_id": { - "type": "string" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "INVITE_DELETE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "KafkaBroker": { - "properties": { - "ip": { - "type": "string" - }, - "port": { - "type": "number" - } - }, - "type": "object" - }, - "ListenEventOpts": { - "properties": { - "acknowledge": { - "type": "boolean" - }, - "channel": { - "$ref": "#/definitions/Channel_1" - } - }, - "type": "object" - }, - "Member": { - "properties": { - "construct": { - }, - "deaf": { - "type": "boolean" - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "guild_id": { - "type": "string" - }, - "id": { - "type": "string" - }, - "joined_at": { - "format": "date-time", - "type": "string" - }, - "metadata": { - }, - "mute": { - "type": "boolean" - }, - "nick": { - "type": "string" - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "pending": { - "type": "boolean" - }, - "premium_since": { - "type": "number" - }, - "read_state": { - "$ref": "#/definitions/Record" - }, - "roles": { - "items": { - "type": "string" - }, - "type": "array" - }, - "settings": { - "$ref": "#/definitions/UserGuildSettings" - }, - "user": { - "$ref": "#/definitions/User" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "Message": { - "properties": { - "activity": { - "properties": { - "party_id": { - "type": "string" - }, - "type": { - "type": "number" - } - }, - "type": "object" - }, - "application": { - "$ref": "#/definitions/Application" - }, - "application_id": { - "type": "string" - }, - "attachments": { - "items": { - "$ref": "#/definitions/Attachment" - }, - "type": "array" - }, - "author": { - "$ref": "#/definitions/User" - }, - "author_id": { - "type": "string" - }, - "channel": { - "$ref": "#/definitions/Channel" - }, - "channel_id": { - "type": "string" - }, - "components": { - "items": { - "$ref": "#/definitions/MessageComponent" - }, - "type": "array" - }, - "construct": { - }, - "content": { - "type": "string" - }, - "edited_timestamp": { - "format": "date-time", - "type": "string" - }, - "embeds": { - "items": { - "$ref": "#/definitions/Embed" - }, - "type": "array" - }, - "flags": { - "type": "bigint" - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "guild_id": { - "type": "string" - }, - "id": { - "type": "string" - }, - "interaction": { - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/InteractionType" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "member": { - "$ref": "#/definitions/Member" - }, - "member_id": { - "type": "string" - }, - "mention_channel_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "mention_channels": { - "items": { - "$ref": "#/definitions/Channel" - }, - "type": "array" - }, - "mention_everyone": { - "type": "boolean" - }, - "mention_role_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "mention_roles": { - "items": { - "$ref": "#/definitions/Role" - }, - "type": "array" - }, - "mention_user_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "mention_users": { - "items": { - "$ref": "#/definitions/User" - }, - "type": "array" - }, - "message_reference": { - "properties": { - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "message_id": { - "type": "string" - } - }, - "type": "object" - }, - "metadata": { - }, - "nonce": { - "type": [ - "string", - "number" - ] - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "pinned": { - "type": "boolean" - }, - "reactions": { - "items": { - "$ref": "#/definitions/Reaction" - }, - "type": "array" - }, - "stickers": { - "items": { - }, - "type": "array" - }, - "timestamp": { - "format": "date-time", - "type": "string" - }, - "tts": { - "type": "boolean" - }, - "type": { - "$ref": "#/definitions/MessageType" - }, - "webhook": { - "$ref": "#/definitions/Webhook" - }, - "webhook_id": { - "type": "string" - } - }, - "type": "object" - }, - "MessageAckEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "channel_id": { - "type": "string" - }, - "manual": { - "type": "boolean" - }, - "mention_count": { - "type": "number" - }, - "message_id": { - "type": "string" - }, - "version": { - "type": "number" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "MESSAGE_ACK" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "MessageComponent": { - "properties": { - "components": { - "items": { - "$ref": "#/definitions/MessageComponent" - }, - "type": "array" - }, - "custom_id": { - "type": "string" - }, - "disabled": { - "type": "boolean" - }, - "emoji": { - "$ref": "#/definitions/PartialEmoji" - }, - "label": { - "type": "string" - }, - "style": { - "type": "number" - }, - "type": { - "type": "number" - }, - "url": { - "type": "string" - } - }, - "type": "object" - }, - "MessageComponentType": { - "enum": [ - 1, - 2 - ], - "type": "number" - }, - "MessageCreateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "allOf": [ - { - "$ref": "#/definitions/Omit" - }, - { - "properties": { - "author": { - "$ref": "#/definitions/PublicUser" - }, - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "member": { - "$ref": "#/definitions/PublicMember" - }, - "mentions": { - "items": { - "allOf": [ - { - "$ref": "#/definitions/PublicUser" - }, - { - "properties": { - "member": { - "$ref": "#/definitions/PublicMember" - } - }, - "type": "object" - } - ] - }, - "type": "array" - } - }, - "type": "object" - } - ] - }, - "event": { - "enum": [ - "MESSAGE_CREATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "MessageDeleteBulkEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "ids": { - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "MESSAGE_DELETE_BULK" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "MessageDeleteEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "id": { - "type": "string" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "MESSAGE_DELETE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "MessageFlags": { - "properties": { - "bitfield": { - "type": "bigint" - } - }, - "type": "object" - }, - "MessagePayload": { - "allOf": [ - { - "$ref": "#/definitions/Omit" - }, - { - "properties": { - "author": { - "$ref": "#/definitions/PublicUser" - }, - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "member": { - "$ref": "#/definitions/PublicMember" - }, - "mentions": { - "items": { - "allOf": [ - { - "$ref": "#/definitions/PublicUser" - }, - { - "properties": { - "member": { - "$ref": "#/definitions/PublicMember" - } - }, - "type": "object" - } - ] - }, - "type": "array" - } - }, - "type": "object" - } - ] - }, - "MessageReactionAddEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "channel_id": { - "type": "string" - }, - "emoji": { - "$ref": "#/definitions/PartialEmoji" - }, - "guild_id": { - "type": "string" - }, - "member": { - "$ref": "#/definitions/PublicMember" - }, - "message_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "MESSAGE_REACTION_ADD" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "MessageReactionRemoveAllEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "message_id": { - "type": "string" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "MESSAGE_REACTION_REMOVE_ALL" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "MessageReactionRemoveEmojiEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "channel_id": { - "type": "string" - }, - "emoji": { - "$ref": "#/definitions/PartialEmoji" - }, - "guild_id": { - "type": "string" - }, - "message_id": { - "type": "string" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "MESSAGE_REACTION_REMOVE_EMOJI" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "MessageReactionRemoveEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "channel_id": { - "type": "string" - }, - "emoji": { - "$ref": "#/definitions/PartialEmoji" - }, - "guild_id": { - "type": "string" - }, - "message_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "MESSAGE_REACTION_REMOVE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "MessageType": { - "enum": [ - 0, - 1, - 10, - 11, - 12, - 14, - 15, - 19, - 2, - 20, - 3, - 4, - 5, - 6, - 7, - 8, - 9 - ], - "type": "number" - }, - "MessageUpdateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "allOf": [ - { - "$ref": "#/definitions/Omit" - }, - { - "properties": { - "author": { - "$ref": "#/definitions/PublicUser" - }, - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "member": { - "$ref": "#/definitions/PublicMember" - }, - "mentions": { - "items": { - "allOf": [ - { - "$ref": "#/definitions/PublicUser" - }, - { - "properties": { - "member": { - "$ref": "#/definitions/PublicMember" - } - }, - "type": "object" - } - ] - }, - "type": "array" - } - }, - "type": "object" - } - ] - }, - "event": { - "enum": [ - "MESSAGE_UPDATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "MuteConfig": { - "properties": { - "end_time": { - "type": "number" - }, - "selected_time_window": { - "type": "number" - } - }, - "type": "object" - }, - "Omit": { - "properties": { - "assign": { - "type": "object" - }, - "channel_id": { - "type": "string" - }, - "code": { - "type": "string" - }, - "construct": { - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "expires_at": { - "format": "date-time", - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "hasId": { - "description": "Checks if entity has an id.\nIf entity composite compose ids, it will check them all.", - "type": "object" - }, - "id": { - "type": "string" - }, - "inviter": { - "$ref": "#/definitions/User" - }, - "inviter_id": { - "type": "string" - }, - "max_age": { - "type": "number" - }, - "max_uses": { - "type": "number" - }, - "metadata": { - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "recover": { - "description": "Recovers a given entity in the database.", - "type": "object" - }, - "reload": { - "description": "Reloads entity data from the database.", - "type": "object" - }, - "remove": { - "description": "Removes current entity from the database.", - "type": "object" - }, - "save": { - "description": "Saves current entity in the database.\nIf entity does not exist in the database then inserts, otherwise updates.", - "type": "object" - }, - "softRemove": { - "description": "Records the delete date of current entity.", - "type": "object" - }, - "target_user": { - "type": "string" - }, - "target_user_id": { - "type": "string" - }, - "target_user_type": { - "type": "number" - }, - "temporary": { - "type": "boolean" - }, - "toJSON": { - "type": "object" - }, - "uses": { - "type": "number" - }, - "validate": { - "type": "object" - } - }, - "type": "object" - }, - "Omit": { - "properties": { - "assign": { - "type": "object" - }, - "construct": { - }, - "deaf": { - "type": "boolean" - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "guild_id": { - "type": "string" - }, - "hasId": { - "description": "Checks if entity has an id.\nIf entity composite compose ids, it will check them all.", - "type": "object" - }, - "id": { - "type": "string" - }, - "joined_at": { - "format": "date-time", - "type": "string" - }, - "metadata": { - }, - "mute": { - "type": "boolean" - }, - "nick": { - "type": "string" - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "pending": { - "type": "boolean" - }, - "premium_since": { - "type": "number" - }, - "read_state": { - "$ref": "#/definitions/Record" - }, - "recover": { - "description": "Recovers a given entity in the database.", - "type": "object" - }, - "reload": { - "description": "Reloads entity data from the database.", - "type": "object" - }, - "remove": { - "description": "Removes current entity from the database.", - "type": "object" - }, - "roles": { - "items": { - "type": "string" - }, - "type": "array" - }, - "save": { - "description": "Saves current entity in the database.\nIf entity does not exist in the database then inserts, otherwise updates.", - "type": "object" - }, - "softRemove": { - "description": "Records the delete date of current entity.", - "type": "object" - }, - "toJSON": { - "type": "object" - }, - "user_id": { - "type": "string" - }, - "validate": { - "type": "object" - } - }, - "type": "object" - }, - "Omit": { - "properties": { - "activity": { - "properties": { - "party_id": { - "type": "string" - }, - "type": { - "type": "number" - } - }, - "type": "object" - }, - "application": { - "$ref": "#/definitions/Application" - }, - "application_id": { - "type": "string" - }, - "assign": { - "type": "object" - }, - "attachments": { - "items": { - "$ref": "#/definitions/Attachment" - }, - "type": "array" - }, - "author": { - "$ref": "#/definitions/User" - }, - "channel": { - "$ref": "#/definitions/Channel" - }, - "channel_id": { - "type": "string" - }, - "components": { - "items": { - "$ref": "#/definitions/MessageComponent" - }, - "type": "array" - }, - "construct": { - }, - "content": { - "type": "string" - }, - "edited_timestamp": { - "format": "date-time", - "type": "string" - }, - "embeds": { - "items": { - "$ref": "#/definitions/Embed" - }, - "type": "array" - }, - "flags": { - "type": "bigint" - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "guild_id": { - "type": "string" - }, - "hasId": { - "description": "Checks if entity has an id.\nIf entity composite compose ids, it will check them all.", - "type": "object" - }, - "id": { - "type": "string" - }, - "interaction": { - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/InteractionType" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "member": { - "$ref": "#/definitions/Member" - }, - "member_id": { - "type": "string" - }, - "mention_channel_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "mention_channels": { - "items": { - "$ref": "#/definitions/Channel" - }, - "type": "array" - }, - "mention_everyone": { - "type": "boolean" - }, - "mention_role_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "mention_roles": { - "items": { - "$ref": "#/definitions/Role" - }, - "type": "array" - }, - "mention_user_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "mention_users": { - "items": { - "$ref": "#/definitions/User" - }, - "type": "array" - }, - "message_reference": { - "properties": { - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "message_id": { - "type": "string" - } - }, - "type": "object" - }, - "metadata": { - }, - "nonce": { - "type": [ - "string", - "number" - ] - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "pinned": { - "type": "boolean" - }, - "reactions": { - "items": { - "$ref": "#/definitions/Reaction" - }, - "type": "array" - }, - "recover": { - "description": "Recovers a given entity in the database.", - "type": "object" - }, - "reload": { - "description": "Reloads entity data from the database.", - "type": "object" - }, - "remove": { - "description": "Removes current entity from the database.", - "type": "object" - }, - "save": { - "description": "Saves current entity in the database.\nIf entity does not exist in the database then inserts, otherwise updates.", - "type": "object" - }, - "softRemove": { - "description": "Records the delete date of current entity.", - "type": "object" - }, - "stickers": { - "items": { - }, - "type": "array" - }, - "timestamp": { - "format": "date-time", - "type": "string" - }, - "toJSON": { - "type": "object" - }, - "tts": { - "type": "boolean" - }, - "type": { - "$ref": "#/definitions/MessageType" - }, - "validate": { - "type": "object" - }, - "webhook": { - "$ref": "#/definitions/Webhook" - }, - "webhook_id": { - "type": "string" - } - }, - "type": "object" - }, - "Omit": { - "properties": { - "bot": { - "type": "number" - }, - "count": { - "type": "number" - }, - "onyIp": { - "type": "boolean" - }, - "window": { - "type": "number" - } - }, - "type": "object" - }, - "Omit": { - "properties": { - "assign": { - "type": "object" - }, - "construct": { - }, - "hasId": { - "description": "Checks if entity has an id.\nIf entity composite compose ids, it will check them all.", - "type": "object" - }, - "id": { - "type": "string" - }, - "metadata": { - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "recover": { - "description": "Recovers a given entity in the database.", - "type": "object" - }, - "reload": { - "description": "Reloads entity data from the database.", - "type": "object" - }, - "remove": { - "description": "Removes current entity from the database.", - "type": "object" - }, - "save": { - "description": "Saves current entity in the database.\nIf entity does not exist in the database then inserts, otherwise updates.", - "type": "object" - }, - "softRemove": { - "description": "Records the delete date of current entity.", - "type": "object" - }, - "toJSON": { - "type": "object" - }, - "type": { - "$ref": "#/definitions/RelationshipType" - }, - "user": { - "$ref": "#/definitions/User" - }, - "user_id": { - "type": "string" - }, - "validate": { - "type": "object" - } - }, - "type": "object" - }, - "PartialEmoji": { - "properties": { - "animated": { - "type": "boolean" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - } - }, - "type": "object" - }, - "PermissionCache": { - "properties": { - "channel": { - "anyOf": [ - { - "$ref": "#/definitions/Channel" - }, - { - "type": "null" - } - ] - }, - "guild": { - "anyOf": [ - { - "$ref": "#/definitions/Guild" - }, - { - "type": "null" - } - ] - }, - "member": { - "anyOf": [ - { - "$ref": "#/definitions/Member" - }, - { - "type": "null" - } - ] - }, - "roles": { - "anyOf": [ - { - "items": { - "$ref": "#/definitions/Role" - }, - "type": "array" - }, - { - "type": "null" - } - ] - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "PermissionResolvable": { - "anyOf": [ - { - "$ref": "#/definitions/Permissions" - }, - { - "items": { - "$ref": "#/definitions/PermissionResolvable" - }, - "type": "array" - }, - { - "enum": [ - "ADD_REACTIONS", - "ADMINISTRATOR", - "ATTACH_FILES", - "BAN_MEMBERS", - "CHANGE_NICKNAME", - "CONNECT", - "CREATE_INSTANT_INVITE", - "DEAFEN_MEMBERS", - "EMBED_LINKS", - "KICK_MEMBERS", - "MANAGE_CHANNELS", - "MANAGE_EMOJIS_AND_STICKERS", - "MANAGE_GUILD", - "MANAGE_MESSAGES", - "MANAGE_NICKNAMES", - "MANAGE_ROLES", - "MANAGE_WEBHOOKS", - "MENTION_EVERYONE", - "MOVE_MEMBERS", - "MUTE_MEMBERS", - "PRIORITY_SPEAKER", - "READ_MESSAGE_HISTORY", - "SEND_MESSAGES", - "SEND_TTS_MESSAGES", - "SPEAK", - "STREAM", - "USE_EXTERNAL_EMOJIS", - "USE_VAD", - "VIEW_AUDIT_LOG", - "VIEW_CHANNEL", - "VIEW_GUILD_INSIGHTS" - ], - "type": "string" - }, - { - "type": [ - "number", - "bigint" - ] - } - ] - }, - "PermissionString": { - "enum": [ - "ADD_REACTIONS", - "ADMINISTRATOR", - "ATTACH_FILES", - "BAN_MEMBERS", - "CHANGE_NICKNAME", - "CONNECT", - "CREATE_INSTANT_INVITE", - "DEAFEN_MEMBERS", - "EMBED_LINKS", - "KICK_MEMBERS", - "MANAGE_CHANNELS", - "MANAGE_EMOJIS_AND_STICKERS", - "MANAGE_GUILD", - "MANAGE_MESSAGES", - "MANAGE_NICKNAMES", - "MANAGE_ROLES", - "MANAGE_WEBHOOKS", - "MENTION_EVERYONE", - "MOVE_MEMBERS", - "MUTE_MEMBERS", - "PRIORITY_SPEAKER", - "READ_MESSAGE_HISTORY", - "SEND_MESSAGES", - "SEND_TTS_MESSAGES", - "SPEAK", - "STREAM", - "USE_EXTERNAL_EMOJIS", - "USE_VAD", - "VIEW_AUDIT_LOG", - "VIEW_CHANNEL", - "VIEW_GUILD_INSIGHTS" - ], - "type": "string" - }, - "Permissions": { - "properties": { - "bitfield": { - "type": "bigint" - }, - "cache": { - "properties": { - "channel": { - "anyOf": [ - { - "$ref": "#/definitions/Channel" - }, - { - "type": "null" - } - ] - }, - "guild": { - "anyOf": [ - { - "$ref": "#/definitions/Guild" - }, - { - "type": "null" - } - ] - }, - "member": { - "anyOf": [ - { - "$ref": "#/definitions/Member" - }, - { - "type": "null" - } - ] - }, - "roles": { - "anyOf": [ - { - "items": { - "$ref": "#/definitions/Role" - }, - "type": "array" - }, - { - "type": "null" - } - ] - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - } - }, - "type": "object" - }, - "Presence": { - "properties": { - "activities": { - "items": { - "$ref": "#/definitions/Activity" - }, - "type": "array" - }, - "client_status": { - "$ref": "#/definitions/ClientStatus" - }, - "guild_id": { - "type": "string" - }, - "status": { - "$ref": "#/definitions/Status" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "PresenceUpdateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "$ref": "#/definitions/Presence" - }, - "event": { - "enum": [ - "PRESENCE_UPDATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "PublicMember": { - "properties": { - "assign": { - "type": "object" - }, - "construct": { - }, - "deaf": { - "type": "boolean" - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "guild_id": { - "type": "string" - }, - "hasId": { - "description": "Checks if entity has an id.\nIf entity composite compose ids, it will check them all.", - "type": "object" - }, - "joined_at": { - "format": "date-time", - "type": "string" - }, - "metadata": { - }, - "mute": { - "type": "boolean" - }, - "nick": { - "type": "string" - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "pending": { - "type": "boolean" - }, - "premium_since": { - "type": "number" - }, - "recover": { - "description": "Recovers a given entity in the database.", - "type": "object" - }, - "reload": { - "description": "Reloads entity data from the database.", - "type": "object" - }, - "remove": { - "description": "Removes current entity from the database.", - "type": "object" - }, - "roles": { - "items": { - "type": "string" - }, - "type": "array" - }, - "save": { - "description": "Saves current entity in the database.\nIf entity does not exist in the database then inserts, otherwise updates.", - "type": "object" - }, - "softRemove": { - "description": "Records the delete date of current entity.", - "type": "object" - }, - "toJSON": { - "type": "object" - }, - "user": { - "$ref": "#/definitions/PublicUser" - }, - "user_id": { - "type": "string" - }, - "validate": { - "type": "object" - } - }, - "type": "object" - }, - "PublicUser": { - "properties": { - "accent_color": { - "type": "number" - }, - "avatar": { - "type": "string" - }, - "banner": { - "type": "string" - }, - "bot": { - "type": "boolean" - }, - "discriminator": { - "type": "string" - }, - "id": { - "type": "string" - }, - "public_flags": { - "type": "bigint" - }, - "username": { - "type": "string" - } - }, - "type": "object" - }, - "RateLimit": { - "properties": { - "blocked": { - "type": "boolean" - }, - "construct": { - }, - "expires_at": { - "format": "date-time", - "type": "string" - }, - "hits": { - "type": "number" - }, - "id": { - "type": "string" - }, - "metadata": { - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "user": { - "$ref": "#/definitions/User" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "RateLimitOptions": { - "properties": { - "bot": { - "type": "number" - }, - "count": { - "type": "number" - }, - "onyIp": { - "type": "boolean" - }, - "window": { - "type": "number" - } - }, - "type": "object" - }, - "Reaction": { - "properties": { - "count": { - "type": "number" - }, - "emoji": { - "$ref": "#/definitions/PartialEmoji" - }, - "user_ids": { - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - }, - "ReadState": { - "properties": { - "channel": { - "$ref": "#/definitions/Channel" - }, - "channel_id": { - "type": "string" - }, - "construct": { - }, - "id": { - "type": "string" - }, - "last_message": { - "$ref": "#/definitions/Message" - }, - "last_message_id": { - "type": "string" - }, - "last_pin_timestamp": { - "format": "date-time", - "type": "string" - }, - "manual": { - "type": "boolean" - }, - "mention_count": { - "type": "number" - }, - "metadata": { - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "user": { - "$ref": "#/definitions/User" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "ReadyEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "$ref": "#/definitions/ReadyEventData" - }, - "event": { - "enum": [ - "READY" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "ReadyEventData": { - "properties": { - "analytics_token": { - "type": "string" - }, - "application": { - "properties": { - "flags": { - "type": "bigint" - }, - "id": { - "type": "string" - } - }, - "type": "object" - }, - "connected_accounts": { - "items": { - "$ref": "#/definitions/ConnectedAccount" - }, - "type": "array" - }, - "consents": { - "properties": { - "personalization": { - "properties": { - "consented": { - "type": "boolean" - } - }, - "type": "object" - } - }, - "type": "object" - }, - "country_code": { - "type": "string" - }, - "experiments": { - "items": { - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "maxItems": 5, - "minItems": 5, - "type": "array" - }, - "type": "array" - }, - "friend_suggestion_count": { - "type": "number" - }, - "geo_ordered_rtc_regions": { - "items": { - "type": "string" - }, - "type": "array" - }, - "guild_experiments": { - "items": { - "items": [ - { - "type": "number" - }, - { - "type": "null" - }, - { - "type": "number" - }, - { - "items": [ - { - "items": [ - { - "type": "number" - }, - { - "items": { - "properties": { - "e": { - "type": "number" - }, - "s": { - "type": "number" - } - }, - "type": "object" - }, - "type": "array" - } - ], - "maxItems": 2, - "minItems": 2, - "type": "array" - } - ], - "maxItems": 1, - "minItems": 1, - "type": "array" - }, - { - "items": [ - { - "type": "number" - }, - { - "items": [ - { - "items": [ - { - "type": "number" - }, - { - "items": [ - { - "type": "number" - }, - { - "type": "number" - } - ], - "maxItems": 2, - "minItems": 2, - "type": "array" - } - ], - "maxItems": 2, - "minItems": 2, - "type": "array" - } - ], - "maxItems": 1, - "minItems": 1, - "type": "array" - } - ], - "maxItems": 2, - "minItems": 2, - "type": "array" - }, - { - "items": { - "properties": { - "b": { - "type": "number" - }, - "k": { - "items": { - "type": "bigint" - }, - "type": "array" - } - }, - "type": "object" - }, - "type": "array" - } - ], - "maxItems": 6, - "minItems": 6, - "type": "array" - }, - "type": "array" - }, - "guild_join_requests": { - "items": { - }, - "type": "array" - }, - "guilds": { - "items": { - "$ref": "#/definitions/Guild" - }, - "type": "array" - }, - "merged_members": { - "items": { - "items": { - "$ref": "#/definitions/Omit" - }, - "type": "array" - }, - "type": "array" - }, - "private_channels": { - "items": { - "$ref": "#/definitions/Channel" - }, - "type": "array" - }, - "read_state": { - "properties": { - "entries": { - "items": { - }, - "type": "array" - }, - "partial": { - "type": "boolean" - }, - "version": { - "type": "number" - } - }, - "type": "object" - }, - "relationships": { - "items": { - "$ref": "#/definitions/Relationship" - }, - "type": "array" - }, - "session_id": { - "type": "string" - }, - "shard": { - "items": [ - { - "type": "number" - }, - { - "type": "number" - } - ], - "maxItems": 2, - "minItems": 2, - "type": "array" - }, - "user": { - "allOf": [ - { - "$ref": "#/definitions/PublicUser" - }, - { - "properties": { - "bot": { - "type": "boolean" - }, - "desktop": { - "type": "boolean" - }, - "email": { - "type": [ - "null", - "string" - ] - }, - "flags": { - "type": "bigint" - }, - "mfa_enabled": { - "type": "boolean" - }, - "mobile": { - "type": "boolean" - }, - "nsfw_allowed": { - "type": "boolean" - }, - "phone": { - "type": [ - "null", - "string" - ] - }, - "premium": { - "type": "boolean" - }, - "premium_type": { - "type": "number" - }, - "verified": { - "type": "boolean" - } - }, - "type": "object" - } - ] - }, - "user_guild_settings": { - "properties": { - "entries": { - "items": { - "$ref": "#/definitions/UserGuildSettings" - }, - "type": "array" - }, - "partial": { - "type": "boolean" - }, - "version": { - "type": "number" - } - }, - "type": "object" - }, - "user_settings": { - "$ref": "#/definitions/UserSettings" - }, - "users": { - "items": { - "properties": { - "avatar": { - "type": [ - "null", - "string" - ] - }, - "bot": { - "type": "boolean" - }, - "discriminator": { - "type": "string" - }, - "id": { - "type": "string" - }, - "public_flags": { - "type": "bigint" - }, - "username": { - "type": "string" - } - }, - "type": "object" - }, - "type": "array" - }, - "v": { - "type": "number" - } - }, - "type": "object" - }, - "Record": { - "type": "object" - }, - "Region": { - "properties": { - "custom": { - "type": "boolean" - }, - "deprecated": { - "type": "boolean" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "optimal": { - "type": "boolean" - }, - "vip": { - "type": "boolean" - } - }, - "type": "object" - }, - "Relationship": { - "properties": { - "construct": { - }, - "id": { - "type": "string" - }, - "metadata": { - }, - "nickname": { - "type": "string" - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "type": { - "$ref": "#/definitions/RelationshipType" - }, - "user": { - "$ref": "#/definitions/User" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "RelationshipAddEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "allOf": [ - { - "$ref": "#/definitions/Relationship" - }, - { - "properties": { - "should_notify": { - "type": "boolean" - }, - "user": { - "$ref": "#/definitions/PublicUser" - } - }, - "type": "object" - } - ] - }, - "event": { - "enum": [ - "RELATIONSHIP_ADD" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "RelationshipRemoveEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "$ref": "#/definitions/Omit" - }, - "event": { - "enum": [ - "RELATIONSHIP_REMOVE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "RelationshipType": { - "enum": [ - 1, - 2, - 3, - 4 - ], - "type": "number" - }, - "Role": { - "properties": { - "color": { - "type": "number" - }, - "construct": { - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "guild_id": { - "type": "string" - }, - "hoist": { - "type": "boolean" - }, - "id": { - "type": "string" - }, - "managed": { - "type": "boolean" - }, - "mentionable": { - "type": "boolean" - }, - "metadata": { - }, - "name": { - "type": "string" - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "permissions": { - "type": "bigint" - }, - "position": { - "type": "number" - }, - "tags": { - "properties": { - "bot_id": { - "type": "string" - }, - "integration_id": { - "type": "string" - }, - "premium_subscriber": { - "type": "boolean" - } - }, - "type": "object" - } - }, - "type": "object" - }, - "Snowflake": { - "description": "A container for useful snowflake-related methods.", - "type": "object" - }, - "Status": { - "enum": [ - "dnd", - "idle", - "offline", - "online" - ], - "type": "string" - }, - "Team": { - "properties": { - "construct": { - }, - "icon": { - "type": "string" - }, - "id": { - "type": "string" - }, - "member_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "members": { - "items": { - "$ref": "#/definitions/TeamMember" - }, - "type": "array" - }, - "metadata": { - }, - "name": { - "type": "string" - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "owner_user": { - "$ref": "#/definitions/User" - }, - "owner_user_id": { - "type": "string" - } - }, - "type": "object" - }, - "TeamMember": { - "properties": { - "construct": { - }, - "id": { - "type": "string" - }, - "membership_state": { - "$ref": "#/definitions/TeamMemberState" - }, - "metadata": { - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "permissions": { - "items": { - "type": "string" - }, - "type": "array" - }, - "team": { - "$ref": "#/definitions/Team" - }, - "team_id": { - "type": "string" - }, - "user": { - "$ref": "#/definitions/User" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "TeamMemberState": { - "enum": [ - 1, - 2 - ], - "type": "number" - }, - "Template": { - "properties": { - "code": { - "type": "string" - }, - "construct": { - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "creator": { - "$ref": "#/definitions/User" - }, - "creator_id": { - "type": "string" - }, - "description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "metadata": { - }, - "name": { - "type": "string" - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "serialized_source_guild": { - "$ref": "#/definitions/Guild" - }, - "source_guild": { - "$ref": "#/definitions/Guild" - }, - "source_guild_id": { - "type": "string" - }, - "updated_at": { - "format": "date-time", - "type": "string" - }, - "usage_count": { - "type": "number" - } - }, - "type": "object" - }, - "TypingStartEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "member": { - "$ref": "#/definitions/PublicMember" - }, - "timestamp": { - "type": "number" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "TYPING_START" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "User": { - "properties": { - "accent_color": { - "type": "number" - }, - "avatar": { - "type": "string" - }, - "banner": { - "type": "string" - }, - "bio": { - "type": "string" - }, - "bot": { - "type": "boolean" - }, - "connected_account_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "connected_accounts": { - "items": { - "$ref": "#/definitions/ConnectedAccount" - }, - "type": "array" - }, - "construct": { - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "hash": { - "type": "string" - }, - "valid_tokens_since": { - "format": "date-time", - "type": "string" - } - }, - "type": "object" - }, - "deleted": { - "type": "boolean" - }, - "desktop": { - "type": "boolean" - }, - "disabled": { - "type": "boolean" - }, - "discriminator": { - "type": "string" - }, - "email": { - "type": "string" - }, - "fingerprints": { - "items": { - "type": "string" - }, - "type": "array" - }, - "flags": { - "type": "bigint" - }, - "guild_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "guilds": { - "items": { - "$ref": "#/definitions/Guild" - }, - "type": "array" - }, - "id": { - "type": "string" - }, - "metadata": { - }, - "mfa_enabled": { - "type": "boolean" - }, - "mobile": { - "type": "boolean" - }, - "nsfw_allowed": { - "type": "boolean" - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "phone": { - "type": "string" - }, - "premium": { - "type": "boolean" - }, - "premium_type": { - "type": "number" - }, - "public_flags": { - "type": "bigint" - }, - "relationship_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "relationships": { - "items": { - "$ref": "#/definitions/Relationship" - }, - "type": "array" - }, - "settings": { - "$ref": "#/definitions/UserSettings" - }, - "system": { - "type": "boolean" - }, - "username": { - "type": "string" - }, - "verified": { - "type": "boolean" - } - }, - "type": "object" - }, - "UserFlags": { - "properties": { - "bitfield": { - "type": "bigint" - } - }, - "type": "object" - }, - "UserGuildSettings": { - "properties": { - "channel_overrides": { - "items": { - "properties": { - "channel_id": { - "type": "string" - }, - "message_notifications": { - "type": "number" - }, - "mute_config": { - "$ref": "#/definitions/MuteConfig" - }, - "muted": { - "type": "boolean" - } - }, - "type": "object" - }, - "type": "array" - }, - "message_notifications": { - "type": "number" - }, - "mobile_push": { - "type": "boolean" - }, - "mute_config": { - "$ref": "#/definitions/MuteConfig" - }, - "muted": { - "type": "boolean" - }, - "suppress_everyone": { - "type": "boolean" - }, - "suppress_roles": { - "type": "boolean" - }, - "version": { - "type": "number" - } - }, - "type": "object" - }, - "UserSettings": { - "properties": { - "afk_timeout": { - "type": "number" - }, - "allow_accessibility_detection": { - "type": "boolean" - }, - "animate_emoji": { - "type": "boolean" - }, - "animate_stickers": { - "type": "number" - }, - "contact_sync_enabled": { - "type": "boolean" - }, - "convert_emoticons": { - "type": "boolean" - }, - "custom_status": { - "properties": { - "emoji_id": { - "type": "string" - }, - "emoji_name": { - "type": "string" - }, - "expires_at": { - "type": "number" - }, - "text": { - "type": "string" - } - }, - "type": "object" - }, - "default_guilds_restricted": { - "type": "boolean" - }, - "detect_platform_accounts": { - "type": "boolean" - }, - "developer_mode": { - "type": "boolean" - }, - "disable_games_tab": { - "type": "boolean" - }, - "enable_tts_command": { - "type": "boolean" - }, - "explicit_content_filter": { - "type": "number" - }, - "friend_source_flags": { - "properties": { - "all": { - "type": "boolean" - } - }, - "type": "object" - }, - "gateway_connected": { - "type": "boolean" - }, - "gif_auto_play": { - "type": "boolean" - }, - "guild_folders": { - "items": { - "properties": { - "color": { - "type": "number" - }, - "guild_ids": { - "items": { - "type": "string" - }, - "type": "array" - }, - "id": { - "type": "number" - }, - "name": { - "type": "string" - } - }, - "type": "object" - }, - "type": "array" - }, - "guild_positions": { - "items": { - "type": "string" - }, - "type": "array" - }, - "inline_attachment_media": { - "type": "boolean" - }, - "inline_embed_media": { - "type": "boolean" - }, - "locale": { - "type": "string" - }, - "message_display_compact": { - "type": "boolean" - }, - "native_phone_integration_enabled": { - "type": "boolean" - }, - "render_embeds": { - "type": "boolean" - }, - "render_reactions": { - "type": "boolean" - }, - "restricted_guilds": { - "items": { - "type": "string" - }, - "type": "array" - }, - "show_current_game": { - "type": "boolean" - }, - "status": { - "enum": [ - "dnd", - "idle", - "offline", - "online" - ], - "type": "string" - }, - "stream_notifications_enabled": { - "type": "boolean" - }, - "theme": { - "enum": [ - "dark", - "white" - ], - "type": "string" - }, - "timezone_offset": { - "type": "number" - } - }, - "type": "object" - }, - "UserUpdateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "$ref": "#/definitions/User" - }, - "event": { - "enum": [ - "USER_UPDATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "VoiceServerUpdateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "endpoint": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "token": { - "type": "string" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "VOICE_SERVER_UPDATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "VoiceState": { - "properties": { - "channel": { - "$ref": "#/definitions/Channel" - }, - "channel_id": { - "type": "string" - }, - "construct": { - }, - "deaf": { - "type": "boolean" - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "guild_id": { - "type": "string" - }, - "id": { - "type": "string" - }, - "metadata": { - }, - "mute": { - "type": "boolean" - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "self_deaf": { - "type": "boolean" - }, - "self_mute": { - "type": "boolean" - }, - "self_stream": { - "type": "boolean" - }, - "self_video": { - "type": "boolean" - }, - "session_id": { - "type": "string" - }, - "suppress": { - "type": "boolean" - }, - "user": { - "$ref": "#/definitions/User" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "VoiceStateUpdateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "allOf": [ - { - "$ref": "#/definitions/VoiceState" - }, - { - "properties": { - "member": { - "$ref": "#/definitions/PublicMember" - } - }, - "type": "object" - } - ] - }, - "event": { - "enum": [ - "VOICE_STATE_UPDATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "Webhook": { - "properties": { - "application": { - "$ref": "#/definitions/Application" - }, - "application_id": { - "type": "string" - }, - "avatar": { - "type": "string" - }, - "channel": { - "$ref": "#/definitions/Channel" - }, - "channel_id": { - "type": "string" - }, - "construct": { - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "guild_id": { - "type": "string" - }, - "id": { - "type": "string" - }, - "metadata": { - }, - "name": { - "type": "string" - }, - "opts": { - "properties": { - "id": { - "type": "string" - } - }, - "type": "object" - }, - "source_guild": { - "$ref": "#/definitions/Guild" - }, - "source_guild_id": { - "type": "string" - }, - "token": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/WebhookType" - }, - "user": { - "$ref": "#/definitions/User" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "WebhookType": { - "enum": [ - 1, - 2 - ], - "type": "number" - }, - "WebhooksUpdateEvent": { - "properties": { - "channel_id": { - "type": "string" - }, - "created_at": { - "format": "date-time", - "type": "string" - }, - "data": { - "properties": { - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - } - }, - "type": "object" - }, - "event": { - "enum": [ - "WEBHOOKS_UPDATE" - ], - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - } - } -} - diff --git a/util/src/interfaces/Event.ts b/util/src/interfaces/Event.ts index 1897706e..bab6f4dc 100644 --- a/util/src/interfaces/Event.ts +++ b/util/src/interfaces/Event.ts @@ -259,22 +259,14 @@ export interface InviteDeleteEvent extends Event { }; } -export type MessagePayload = Omit & { - channel_id: string; - guild_id?: string; - author: PublicUser; - member: PublicMember; - mentions: (PublicUser & { member: PublicMember })[]; -}; - export interface MessageCreateEvent extends Event { event: "MESSAGE_CREATE"; - data: MessagePayload; + data: Message; } export interface MessageUpdateEvent extends Event { event: "MESSAGE_UPDATE"; - data: MessagePayload; + data: Message; } export interface MessageDeleteEvent extends Event { diff --git a/util/src/tes.ts b/util/src/tes.ts new file mode 100644 index 00000000..b469f1d8 --- /dev/null +++ b/util/src/tes.ts @@ -0,0 +1,19 @@ +import { performance } from "perf_hooks"; +import { Guild, Relationship, RelationshipType } from "./entities"; +import { User } from "./entities/User"; +import { initDatabase } from "./util"; + +initDatabase().then(async (x) => { + try { + const user = await new User( + { guilds: [], discriminator: "1", username: "test", flags: "0", public_flags: "0" }, + { id: "0" } + ).save(); + + user.relationships = [new Relationship({ type: RelationshipType.friends })]; + + user.save(); + } catch (error) { + console.error(error); + } +}); diff --git a/util/src/util/AutoUpdate.ts b/util/src/util/AutoUpdate.ts index a2ce73c2..cafc7bdb 100644 --- a/util/src/util/AutoUpdate.ts +++ b/util/src/util/AutoUpdate.ts @@ -40,6 +40,7 @@ export function enableAutoUpdate(opts: { console.log(`[Auto update] updating ...`); download(opts.downloadUrl, opts.path); } else { + console.log(`[Auto update] aborted`); } } ); diff --git a/util/src/util/Database.ts b/util/src/util/Database.ts index 8bb8078e..f49fb04c 100644 --- a/util/src/util/Database.ts +++ b/util/src/util/Database.ts @@ -14,11 +14,17 @@ export function initDatabase() { console.log("[Database] connecting ..."); // @ts-ignore promise = createConnection({ - type: "sqlite", - database: "database.db", + // type: "sqlite", + // database: "database.db", + type: "postgres", + url: "postgres://fosscord:wb94SmuURM2Syv&@localhost/fosscord", + // entities: Object.values(Models).filter((x) => x.constructor.name !== "Object"), synchronize: true, - logging: false, + logging: true, + cache: { + duration: 1000 * 3, // cache all find queries for 3 seconds + }, }); promise.then((connection) => { diff --git a/util/src/util/Permissions.ts b/util/src/util/Permissions.ts index 630db36f..5082d8ea 100644 --- a/util/src/util/Permissions.ts +++ b/util/src/util/Permissions.ts @@ -198,50 +198,41 @@ export class Permissions extends BitField { } export type PermissionCache = { - channel?: Channel | null; - member?: Member | null; - guild?: Guild | null; - roles?: Role[] | null; + channel?: Channel | undefined; + member?: Member | undefined; + guild?: Guild | undefined; + roles?: Role[] | undefined; user_id?: string; }; -export async function getPermission( - user_id?: string, - guild_id?: string, - channel_id?: string, - cache: PermissionCache = {} -) { - var { channel, member, guild, roles } = cache; - +export async function getPermission(user_id?: string, guild_id?: string, channel_id?: string) { if (!user_id) throw new HTTPError("User not found"); + var channel: Channel | undefined; + var member: Member | undefined; + var guild: Guild | undefined; - if (channel_id && !channel) { + if (channel_id) { channel = await Channel.findOneOrFail( { id: channel_id }, { select: ["permission_overwrites", "recipients", "owner", "guild"] } ); - if (!channel) throw new HTTPError("Channel not found", 404); - if (channel.guild_id) guild_id = channel.guild_id; + if (channel.guild_id) guild_id = channel.guild_id; // derive guild_id from the channel } if (guild_id) { - if (!guild) guild = await Guild.findOneOrFail({ id: guild_id }, { select: ["owner"] }); - if (!guild) throw new HTTPError("Guild not found"); + guild = await Guild.findOneOrFail({ id: guild_id }, { select: ["owner"] }); if (guild.owner_id === user_id) return new Permissions(Permissions.FLAGS.ADMINISTRATOR); - if (!member) member = await Member.findOneOrFail({ guild_id, id: user_id }, { select: ["roles"] }); - if (!member) throw new HTTPError("Member not found"); - - if (!roles) roles = await Role.find({ guild_id, id: In(member.roles) }); + member = await Member.findOneOrFail({ guild_id, id: user_id }, { select: ["roles"] }); } var permission = Permissions.finalPermission({ user: { id: user_id, - roles: member?.roles || [], + roles: member?.role_ids || [], }, guild: { - roles: roles || [], + roles: member?.roles || [], }, channel: { overwrites: channel?.permission_overwrites, @@ -253,7 +244,7 @@ export async function getPermission( const obj = new Permissions(permission); // pass cache to permission for possible future getPermission calls - obj.cache = { guild, member, channel, roles, user_id }; + obj.cache = { guild, member, channel, roles: member?.roles, user_id }; return obj; } diff --git a/util/tests/User.test.js b/util/tests/User.test.js index b87c753d..c0852ebc 100644 --- a/util/tests/User.test.js +++ b/util/tests/User.test.js @@ -1,10 +1,9 @@ const { initDatabase, closeDatabase } = require("../dist/util/Database"); const { User } = require("../dist/entities/User"); -jest.setTimeout(10000); +jest.setTimeout(20000); beforeAll((done) => { initDatabase().then(() => { - new User().validate(); // warm up schema/model done(); }); }); @@ -28,4 +27,17 @@ describe("User", () => { new User({ discriminator: "0" }).validate(); }).toThrow(); }); + + test("add guild", async () => { + try { + await new User({ guilds: [], discriminator: "1" }, { id: "0" }).save(); + const user = await User.find("0"); + + user.guilds.push(new Guild({ name: "test" })); + + user.save(); + } catch (error) { + console.error(error); + } + }); }); diff --git a/util/tests/setupJest.js b/util/tests/setupJest.js index 551d2be5..35a3cb52 100644 --- a/util/tests/setupJest.js +++ b/util/tests/setupJest.js @@ -1,4 +1,8 @@ const { performance } = require("perf_hooks"); +const fs = require("fs"); +const path = require("path"); + +// fs.unlinkSync(path.join(__dirname, "..", "database.db")); global.expect.extend({ toBeFasterThan: async (func, target) => { diff --git a/util/tests/validate.test.js b/util/tests/validate.test.js deleted file mode 100644 index 434422f8..00000000 --- a/util/tests/validate.test.js +++ /dev/null @@ -1,33 +0,0 @@ -const { initDatabase, closeDatabase } = require("../dist/util/Database"); -const { User } = require("../dist/entities/User"); -jest.setTimeout(10000); - -beforeAll((done) => { - initDatabase().then(() => { - new User().validate(); // warm up schema/model - done(); - }); -}); - -afterAll(() => { - closeDatabase(); -}); - -describe("Validate model class properties", () => { - test("object instead of string", async () => { - expect(() => { - new User({}, { id: {} }).validate(); - }).toThrow(); - }); - - test("validation should be faster than 20ms", () => { - expect(() => { - new User().validate(); - }).toBeFasterThan(20); - }); - - test("should not set opts", () => { - const user = new User({ opts: { id: 0 } }); - expect(user.opts.id).not.toBe(0); - }); -}); diff --git a/util/tsconfig.json b/util/tsconfig.json index 3fe76ee7..ac41cea5 100644 --- a/util/tsconfig.json +++ b/util/tsconfig.json @@ -1,5 +1,5 @@ { - "include": ["src/**/*.ts"], + "include": ["src/**/*.ts", "tests/Test.ts"], "compilerOptions": { /* Visit https://aka.ms/tsconfig.json to read more about this file */ -- cgit 1.5.1 From 681ce2ac47552ae97543fc2df8b444294090edd6 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Sun, 29 Aug 2021 16:58:37 +0200 Subject: fix util --- util/src/entities/Guild.ts | 2 +- util/src/entities/Message.ts | 4 ++-- util/src/entities/RateLimit.ts | 8 ++------ util/src/entities/Role.ts | 1 + util/src/entities/User.ts | 38 +++++++++++++++++++------------------- util/src/entities/index.ts | 1 + util/src/interfaces/Event.ts | 2 +- util/src/util/Config.ts | 4 +++- util/src/util/Database.ts | 12 +++++++----- util/src/util/checkToken.ts | 3 ++- 10 files changed, 39 insertions(+), 36 deletions(-) (limited to 'util/src/entities/Guild.ts') diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts index 9ca4b1e4..e6a93824 100644 --- a/util/src/entities/Guild.ts +++ b/util/src/entities/Guild.ts @@ -158,7 +158,7 @@ export class Guild extends BaseClass { vanity_url_code?: string; @JoinColumn({ name: "vanity_url_code" }) - @OneToOne(() => Invite, (invite: Invite) => invite.code) + @ManyToOne(() => Invite) vanity_url?: Invite; @Column({ nullable: true }) diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts index 0c41a2eb..43d0f9d0 100644 --- a/util/src/entities/Message.ts +++ b/util/src/entities/Message.ts @@ -148,8 +148,8 @@ export class Message extends BaseClass { party_id: string; }; - @Column({ type: "bigint", nullable: true }) - flags?: bigint; + @Column({ nullable: true }) + flags?: string; @RelationId((message: Message) => message.stickers) sticker_ids: string[]; diff --git a/util/src/entities/RateLimit.ts b/util/src/entities/RateLimit.ts index 3ac35df3..49af0416 100644 --- a/util/src/entities/RateLimit.ts +++ b/util/src/entities/RateLimit.ts @@ -7,12 +7,8 @@ export class RateLimit extends BaseClass { @Column() id: "global" | "error" | string; // channel_239842397 | guild_238927349823 | webhook_238923423498 - @RelationId((rate_limit: RateLimit) => rate_limit.user) - user_id: string; - - @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user) => user.id) - user: User; + @Column() // no relation as it also + executor_id: string; @Column() hits: number; diff --git a/util/src/entities/Role.ts b/util/src/entities/Role.ts index 7c6ce64e..ddae7e40 100644 --- a/util/src/entities/Role.ts +++ b/util/src/entities/Role.ts @@ -1,4 +1,5 @@ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; + import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts index c5f870fa..73afba67 100644 --- a/util/src/entities/User.ts +++ b/util/src/entities/User.ts @@ -49,7 +49,7 @@ export class User extends BaseClass { avatar?: string; // hash of the user avatar @Column({ nullable: true }) - accent_color?: number = 0; // banner color of user + accent_color?: number; // banner color of user @Column({ nullable: true }) banner?: string; // hash of the user banner @@ -58,52 +58,52 @@ export class User extends BaseClass { phone?: string; // phone number of the user @Column() - desktop: boolean = false; // if the user has desktop app installed + desktop: boolean; // if the user has desktop app installed @Column() - mobile: boolean = false; // if the user has mobile app installed + mobile: boolean; // if the user has mobile app installed @Column() - premium: boolean = false; // if user bought nitro + premium: boolean; // if user bought nitro @Column() - premium_type: number = 0; // nitro level + premium_type: number; // nitro level @Column() - bot: boolean = false; // if user is bot + bot: boolean; // 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 = false; // shouldn't be used, the api sents this field type true, if the generated message comes from a system generated author + 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 = false; // if the user is older than 18 (resp. Config) + nsfw_allowed: boolean; // if the user is older than 18 (resp. Config) @Column() - mfa_enabled: boolean = false; // if multi factor authentication is enabled + mfa_enabled: boolean; // if multi factor authentication is enabled @Column() created_at: Date = new Date(); // registration date @Column() - verified: boolean = false; // if the user is offically verified + verified: boolean; // if the user is offically verified @Column() - disabled: boolean = false; // if the account is disabled + disabled: boolean; // if the account is disabled @Column() - deleted: boolean = false; // if the user was deleted + deleted: boolean; // if the user was deleted @Column({ nullable: true }) email?: string; // email of the user - @Column({ type: "bigint" }) - flags: bigint = BigInt(0); // UserFlags + @Column() + flags: string; // UserFlags - @Column({ type: "bigint" }) - public_flags: bigint = BigInt(0); + @Column() + public_flags: string; @RelationId((user: User) => user.relationships) relationship_ids: string[]; // array of guild ids the user is part of @@ -123,13 +123,13 @@ export class User extends BaseClass { 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) - } = { valid_tokens_since: new Date() }; + }; @Column({ type: "simple-array" }) fingerprints: string[] = []; // array of fingerprints -> used to prevent multiple accounts @Column({ type: "simple-json" }) - settings: UserSettings = defaultSettings; + settings: UserSettings; static async getPublicUser(user_id: string, opts?: FindOneOptions) { const user = await User.findOne(user_id, { diff --git a/util/src/entities/index.ts b/util/src/entities/index.ts index b9e361c1..e0246a10 100644 --- a/util/src/entities/index.ts +++ b/util/src/entities/index.ts @@ -14,6 +14,7 @@ export * from "./RateLimit"; export * from "./ReadState"; export * from "./Relationship"; export * from "./Role"; +export * from "./Sticker"; export * from "./Team"; export * from "./TeamMember"; export * from "./Template"; diff --git a/util/src/interfaces/Event.ts b/util/src/interfaces/Event.ts index bab6f4dc..e855095c 100644 --- a/util/src/interfaces/Event.ts +++ b/util/src/interfaces/Event.ts @@ -515,4 +515,4 @@ export type EVENT = | "RELATIONSHIP_REMOVE" | CUSTOMEVENTS; -export type CUSTOMEVENTS = "INVALIDATED"; +export type CUSTOMEVENTS = "INVALIDATED" | "RATELIMIT"; diff --git a/util/src/util/Config.ts b/util/src/util/Config.ts index f8574f38..f16921bd 100644 --- a/util/src/util/Config.ts +++ b/util/src/util/Config.ts @@ -6,6 +6,7 @@ var config: ConfigEntity; export const Config = { init: async function init() { + if (config) return config; config = new ConfigEntity({}, { id: "0" }); return this.set((config.value || {}).merge(DefaultConfigOptions)); }, @@ -13,7 +14,8 @@ export const Config = { return config.value as ConfigValue; }, set: function set(val: any) { - config.value = val.merge(config.value); + if (!config) return; + config.value = val.merge(config?.value || {}); return config.save(); }, }; diff --git a/util/src/util/Database.ts b/util/src/util/Database.ts index f49fb04c..c22d8abd 100644 --- a/util/src/util/Database.ts +++ b/util/src/util/Database.ts @@ -1,5 +1,5 @@ import "reflect-metadata"; -import { Connection, createConnection } from "typeorm"; +import { Connection, createConnection, ValueTransformer } from "typeorm"; import * as Models from "../entities"; // UUID extension option is only supported with postgres @@ -14,10 +14,10 @@ export function initDatabase() { console.log("[Database] connecting ..."); // @ts-ignore promise = createConnection({ - // type: "sqlite", - // database: "database.db", - type: "postgres", - url: "postgres://fosscord:wb94SmuURM2Syv&@localhost/fosscord", + type: "sqlite", + database: "database.db", + // type: "postgres", + // url: "postgres://fosscord:wb94SmuURM2Syv&@localhost/fosscord", // entities: Object.values(Models).filter((x) => x.constructor.name !== "Object"), synchronize: true, @@ -25,6 +25,8 @@ export function initDatabase() { cache: { duration: 1000 * 3, // cache all find queries for 3 seconds }, + bigNumberStrings: false, + supportBigNumbers: true, }); promise.then((connection) => { diff --git a/util/src/util/checkToken.ts b/util/src/util/checkToken.ts index 1e203006..8415e8c0 100644 --- a/util/src/util/checkToken.ts +++ b/util/src/util/checkToken.ts @@ -12,7 +12,8 @@ export function checkToken(token: string, jwtSecret: string): Promise { const user = await User.findOne({ id: decoded.id }, { select: ["data", "bot", "disabled", "deleted"] }); if (!user) return rej("Invalid Token"); // we need to round it to seconds as it saved as seconds in jwt iat and valid_tokens_since is stored in milliseconds - if (decoded.iat * 1000 < user.data.valid_tokens_since.setSeconds(0, 0)) return rej("Invalid Token"); + if (decoded.iat * 1000 < new Date(user.data.valid_tokens_since).setSeconds(0, 0)) + return rej("Invalid Token"); if (user.disabled) return rej("User disabled"); if (user.deleted) return rej("User not found"); -- cgit 1.5.1 From 584d4a039c8752f76cc5431b423ff41439794366 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Mon, 30 Aug 2021 12:15:06 +0200 Subject: :construction: typeorm --- api/src/routes/guilds/#guild_id/roles.ts | 2 +- api/src/routes/guilds/#guild_id/templates.ts | 2 +- api/src/routes/guilds/index.ts | 88 +++++++++++++--------------- util/src/entities/BaseClass.ts | 3 +- util/src/entities/Emoji.ts | 2 +- util/src/entities/Guild.ts | 24 ++++---- util/src/entities/Member.ts | 6 +- util/src/entities/Role.ts | 4 +- util/src/util/Config.ts | 3 +- util/src/util/Permissions.ts | 9 +-- 10 files changed, 67 insertions(+), 76 deletions(-) (limited to 'util/src/entities/Guild.ts') diff --git a/api/src/routes/guilds/#guild_id/roles.ts b/api/src/routes/guilds/#guild_id/roles.ts index e9e777b9..796a8eb8 100644 --- a/api/src/routes/guilds/#guild_id/roles.ts +++ b/api/src/routes/guilds/#guild_id/roles.ts @@ -40,7 +40,7 @@ router.post("/", check(RoleModifySchema), async (req: Request, res: Response) => managed: false, position: 0, tags: null, - permissions: perms.bitfield & (body.permissions || 0n) + permissions: String(perms.bitfield & (body.permissions || 0n)) }).save(); await emitEvent({ diff --git a/api/src/routes/guilds/#guild_id/templates.ts b/api/src/routes/guilds/#guild_id/templates.ts index e1d2f5fd..a7613abf 100644 --- a/api/src/routes/guilds/#guild_id/templates.ts +++ b/api/src/routes/guilds/#guild_id/templates.ts @@ -17,7 +17,7 @@ const TemplateGuildProjection: (keyof Guild)[] = [ "preferred_locale", "afk_timeout", "roles", - "channels", + // "channels", "afk_channel_id", "system_channel_id", "system_channel_flags", diff --git a/api/src/routes/guilds/index.ts b/api/src/routes/guilds/index.ts index 1e83cf13..020aba6a 100644 --- a/api/src/routes/guilds/index.ts +++ b/api/src/routes/guilds/index.ts @@ -19,63 +19,55 @@ router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) = } const guild_id = Snowflake.generate(); - const guild = new Guild({ - name: body.name, - region: Config.get().regions.default, - owner_id: req.user_id, - icon: undefined, - afk_channel_id: undefined, - afk_timeout: 300, - application_id: undefined, - banner: undefined, - default_message_notifications: 0, - description: undefined, - splash: undefined, - discovery_splash: undefined, - explicit_content_filter: 0, - features: [], - id: guild_id, - large: undefined, - max_members: 250000, - max_presences: 250000, - max_video_channel_users: 25, - presence_count: 0, - member_count: 0, // will automatically be increased by addMember() - mfa_level: 0, - preferred_locale: "en-US", - premium_subscription_count: 0, - premium_tier: 0, - public_updates_channel_id: undefined, - rules_channel_id: undefined, - system_channel_flags: "0", - system_channel_id: undefined, - unavailable: false, - vanity_url_code: undefined, - verification_level: 0, - welcome_screen: { - enabled: false, - description: "No description", - welcome_channels: [] - }, - widget_channel_id: undefined, - widget_enabled: false - }); - - const [guild_doc, role] = await Promise.all([ - new Guild(guild).save(), - new Role({ + const guild = new Guild( + { + name: body.name, + region: Config.get().regions.default, + owner_id: req.user_id, + afk_timeout: 300, + default_message_notifications: 0, + explicit_content_filter: 0, + features: [], id: guild_id, + max_members: 250000, + max_presences: 250000, + max_video_channel_users: 25, + presence_count: 0, + member_count: 0, // will automatically be increased by addMember() + mfa_level: 0, + preferred_locale: "en-US", + premium_subscription_count: 0, + premium_tier: 0, + system_channel_flags: "0", + unavailable: false, + verification_level: 0, + welcome_screen: { + enabled: false, + description: "No description", + welcome_channels: [] + }, + widget_enabled: false + }, + { id: guild_id } + ); + const role = new Role( + { guild_id: guild_id, color: 0, hoist: false, managed: false, mentionable: false, name: "@everyone", - permissions: 2251804225n, + permissions: String("2251804225"), position: 0, tags: null - }).save() - ]); + }, + { + id: guild_id + } + ); + + await Promise.all([guild.save(), role.save()]); if (!body.channels || !body.channels.length) body.channels = [{ id: "01", type: 0, name: "general" }]; diff --git a/util/src/entities/BaseClass.ts b/util/src/entities/BaseClass.ts index 31338ff6..63ce5836 100644 --- a/util/src/entities/BaseClass.ts +++ b/util/src/entities/BaseClass.ts @@ -51,7 +51,8 @@ export class BaseClass extends BaseEntity { if (setter) { setter.call(this, props[key]); } else { - Object.defineProperty(this, key, { value: props[key] }); + // @ts-ignore + this[key] = props[key]; } } } diff --git a/util/src/entities/Emoji.ts b/util/src/entities/Emoji.ts index 0686d476..4c0fccd3 100644 --- a/util/src/entities/Emoji.ts +++ b/util/src/entities/Emoji.ts @@ -15,7 +15,7 @@ export class Emoji extends BaseClass { guild_id: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild, (guild: Guild) => guild.emojis) guild: Guild; @Column() diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts index e6a93824..3e7e8917 100644 --- a/util/src/entities/Guild.ts +++ b/util/src/entities/Guild.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToOne, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToMany, OneToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Emoji } from "./Emoji"; @@ -68,35 +68,35 @@ export class Guild extends BaseClass { member_ids: string[]; @JoinColumn({ name: "member_ids" }) - @ManyToMany(() => Member, (member: Member) => member.id) + @OneToMany(() => Member, (member: Member) => member.guild) members: Member[]; @RelationId((guild: Guild) => guild.roles) role_ids: string[]; @JoinColumn({ name: "role_ids" }) - @ManyToMany(() => Role, (role: Role) => role.id) + @OneToMany(() => Role, (role: Role) => role.guild) roles: Role[]; @RelationId((guild: Guild) => guild.channels) channel_ids: string[]; @JoinColumn({ name: "channel_ids" }) - @ManyToMany(() => Channel, (channel: Channel) => channel.id) + @OneToMany(() => Channel, (channel: Channel) => channel.guild) channels: Channel[]; @RelationId((guild: Guild) => guild.emojis) emoji_ids: string[]; @JoinColumn({ name: "emoji_ids" }) - @ManyToMany(() => Emoji, (emoji: Emoji) => emoji.id) + @OneToMany(() => Emoji, (emoji: Emoji) => emoji.guild) emojis: Emoji[]; @RelationId((guild: Guild) => guild.voice_states) voice_state_ids: string[]; @JoinColumn({ name: "voice_state_ids" }) - @ManyToMany(() => VoiceState, (voicestate: VoiceState) => voicestate.id) + @OneToMany(() => VoiceState, (voicestate: VoiceState) => voicestate.guild) voice_states: VoiceState[]; @Column({ nullable: true }) @@ -109,7 +109,7 @@ export class Guild extends BaseClass { owner_id: string; @JoinColumn({ name: "owner_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @OneToOne(() => User) owner: User; @Column({ nullable: true }) @@ -125,14 +125,14 @@ export class Guild extends BaseClass { public_updates_channel_id: string; @JoinColumn({ name: "public_updates_channel_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @OneToOne(() => Channel, (channel: Channel) => channel.id) public_updates_channel?: Channel; @RelationId((guild: Guild) => guild.rules_channel) rules_channel_id?: string; @JoinColumn({ name: "rules_channel_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @OneToOne(() => Channel, (channel: Channel) => channel.id) rules_channel?: string; @Column({ nullable: true }) @@ -145,7 +145,7 @@ export class Guild extends BaseClass { system_channel_id?: string; @JoinColumn({ name: "system_channel_id" }) - @ManyToMany(() => Channel, (channel: Channel) => channel.id) + @OneToOne(() => Channel, (channel: Channel) => channel.id) system_channel?: Channel; @Column({ nullable: true }) @@ -158,7 +158,7 @@ export class Guild extends BaseClass { vanity_url_code?: string; @JoinColumn({ name: "vanity_url_code" }) - @ManyToOne(() => Invite) + @OneToOne(() => Invite) vanity_url?: Invite; @Column({ nullable: true }) @@ -180,7 +180,7 @@ export class Guild extends BaseClass { widget_channel_id?: string; @JoinColumn({ name: "widget_channel_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @OneToOne(() => Channel, (channel: Channel) => channel.id) widget_channel?: Channel; @Column({ nullable: true }) diff --git a/util/src/entities/Member.ts b/util/src/entities/Member.ts index 5b588d70..c5d289ef 100644 --- a/util/src/entities/Member.ts +++ b/util/src/entities/Member.ts @@ -1,6 +1,6 @@ import { PublicUser, User } from "./User"; import { BaseClass } from "./BaseClass"; -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToMany, RelationId } from "typeorm"; import { Guild } from "./Guild"; import { Config, emitEvent } from "../util"; import { @@ -26,7 +26,7 @@ export class Member extends BaseClass { guild_id: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild, (guild: Guild) => guild.members) guild: Guild; @Column({ nullable: true }) @@ -35,7 +35,7 @@ export class Member extends BaseClass { @RelationId((member: Member) => member.roles) role_ids: string[]; - @JoinColumn({ name: "role_ids" }) + @JoinTable() @ManyToMany(() => Role) roles: Role[]; diff --git a/util/src/entities/Role.ts b/util/src/entities/Role.ts index ddae7e40..be8c731d 100644 --- a/util/src/entities/Role.ts +++ b/util/src/entities/Role.ts @@ -27,8 +27,8 @@ export class Role extends BaseClass { @Column() name: string; - @Column({ type: "bigint" }) - permissions: bigint; + @Column() + permissions: string; @Column() position: number; diff --git a/util/src/util/Config.ts b/util/src/util/Config.ts index f16921bd..508a8901 100644 --- a/util/src/util/Config.ts +++ b/util/src/util/Config.ts @@ -7,7 +7,8 @@ var config: ConfigEntity; export const Config = { init: async function init() { if (config) return config; - config = new ConfigEntity({}, { id: "0" }); + config = (await ConfigEntity.findOne({ id: "0" })) || new ConfigEntity({}, { id: "0" }); + return this.set((config.value || {}).merge(DefaultConfigOptions)); }, get: function get() { diff --git a/util/src/util/Permissions.ts b/util/src/util/Permissions.ts index 5082d8ea..ab6aa795 100644 --- a/util/src/util/Permissions.ts +++ b/util/src/util/Permissions.ts @@ -212,18 +212,15 @@ export async function getPermission(user_id?: string, guild_id?: string, channel var guild: Guild | undefined; if (channel_id) { - channel = await Channel.findOneOrFail( - { id: channel_id }, - { select: ["permission_overwrites", "recipients", "owner", "guild"] } - ); + channel = await Channel.findOneOrFail({ id: channel_id }); if (channel.guild_id) guild_id = channel.guild_id; // derive guild_id from the channel } if (guild_id) { - guild = await Guild.findOneOrFail({ id: guild_id }, { select: ["owner"] }); + guild = await Guild.findOneOrFail({ id: guild_id }); if (guild.owner_id === user_id) return new Permissions(Permissions.FLAGS.ADMINISTRATOR); - member = await Member.findOneOrFail({ guild_id, id: user_id }, { select: ["roles"] }); + member = await Member.findOneOrFail({ where: { guild_id, id: user_id }, relations: ["roles"] }); } var permission = Permissions.finalPermission({ -- cgit 1.5.1 From 89304ff47ba03df876dc795cdb9fd17f5bca67f2 Mon Sep 17 00:00:00 2001 From: AlTech98 Date: Mon, 30 Aug 2021 21:12:13 +0200 Subject: Implemented voice apis #127 and #78 --- api/src/routes/guilds/#guild_id/regions.ts | 9 +++++++-- api/src/routes/voice/regions.ts | 11 ++++++++++ api/src/util/Voice.ts | 32 ++++++++++++++++++++++++++++++ api/src/util/ipAddress.ts | 17 ++++++++++++++++ util/src/entities/Config.ts | 10 ++++++++-- util/src/entities/Guild.ts | 2 +- 6 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 api/src/routes/voice/regions.ts create mode 100644 api/src/util/Voice.ts (limited to 'util/src/entities/Guild.ts') diff --git a/api/src/routes/guilds/#guild_id/regions.ts b/api/src/routes/guilds/#guild_id/regions.ts index 5ec649ee..212c9bcd 100644 --- a/api/src/routes/guilds/#guild_id/regions.ts +++ b/api/src/routes/guilds/#guild_id/regions.ts @@ -1,10 +1,15 @@ -import { Config } from "@fosscord/util"; +import {Config, Guild, Member} from "@fosscord/util"; import { Request, Response, Router } from "express"; +import {getVoiceRegions} from "../../../util/Voice"; +import {getIpAdress} from "../../../util/ipAddress"; const router = Router(); router.get("/", async (req: Request, res: Response) => { - return res.json(Config.get().regions.available); + const { guild_id } = req.params; + const guild = await Guild.findOneOrFail({ id: guild_id }); + //TODO we should use an enum for guild's features and not hardcoded strings + return res.json(await getVoiceRegions(getIpAdress(req), guild.features.includes("VIP_REGIONS"))); }); export default router; diff --git a/api/src/routes/voice/regions.ts b/api/src/routes/voice/regions.ts new file mode 100644 index 00000000..812aa8f6 --- /dev/null +++ b/api/src/routes/voice/regions.ts @@ -0,0 +1,11 @@ +import { Router, Request, Response } from "express"; +import {getIpAdress} from "../../util/ipAddress"; +import {getVoiceRegions} from "../../util/Voice"; + +const router: Router = Router(); + +router.get("/", async (req: Request, res: Response) => { + res.json(await getVoiceRegions(getIpAdress(req), true))//vip true? +}); + +export default router; diff --git a/api/src/util/Voice.ts b/api/src/util/Voice.ts new file mode 100644 index 00000000..087bdfa8 --- /dev/null +++ b/api/src/util/Voice.ts @@ -0,0 +1,32 @@ +import {Config} from "@fosscord/util"; +import {distanceBetweenLocations, IPAnalysis} from "./ipAddress"; + +export async function getVoiceRegions(ipAddress: string, vip: boolean) { + const regions = Config.get().regions; + const availableRegions = regions.available.filter(ar => vip ? true : !ar.vip); + let optimalId = regions.default + + if(!regions.useDefaultAsOptimal) { + const clientIpAnalysis = await IPAnalysis(ipAddress) + + let min = Number.POSITIVE_INFINITY + + for (let ar of availableRegions) { + //TODO the endpoint location should be saved in the database if not already present to prevent IPAnalysis call + const dist = distanceBetweenLocations(clientIpAnalysis, ar.location || (await IPAnalysis(ar.endpoint))) + + if(dist < min) { + min = dist + optimalId = ar.id + } + } + } + + return availableRegions.map(ar => ({ + id: ar.id, + name: ar.name, + custom: ar.custom, + deprecated: ar.deprecated, + optimal: ar.id === optimalId + })) +} \ No newline at end of file diff --git a/api/src/util/ipAddress.ts b/api/src/util/ipAddress.ts index 0a724daa..c6239426 100644 --- a/api/src/util/ipAddress.ts +++ b/api/src/util/ipAddress.ts @@ -60,6 +60,7 @@ const exampleData = { status: 200 }; +//TODO add function that support both ip and domain names export async function IPAnalysis(ip: string): Promise { const { ipdataApiKey } = Config.get().security; if (!ipdataApiKey) return { ...exampleData, ip }; @@ -79,3 +80,19 @@ export function getIpAdress(req: Request): string { // @ts-ignore return req.headers[Config.get().security.forwadedFor] || req.socket.remoteAddress; } + + +export function distanceBetweenLocations(loc1: any, loc2: any): number { + return distanceBetweenCoords(loc1.latitude, loc1.longitude, loc2.latitude, loc2.longitude); +} + +//Haversine function +function distanceBetweenCoords(lat1: number, lon1: number, lat2: number, lon2: number) { + const p = 0.017453292519943295; // Math.PI / 180 + const c = Math.cos; + const a = 0.5 - c((lat2 - lat1) * p) / 2 + + c(lat1 * p) * c(lat2 * p) * + (1 - c((lon2 - lon1) * p)) / 2; + + return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km +} \ No newline at end of file diff --git a/util/src/entities/Config.ts b/util/src/entities/Config.ts index 320a729c..f030b167 100644 --- a/util/src/entities/Config.ts +++ b/util/src/entities/Config.ts @@ -19,10 +19,14 @@ export interface RateLimitOptions { export interface Region { id: string; name: string; + endpoint: string; + location?: { + latitude: number; + longitude: number; + }; vip: boolean; custom: boolean; deprecated: boolean; - optimal: boolean; } export interface KafkaBroker { @@ -128,6 +132,7 @@ export interface ConfigValue { }; regions: { default: string; + useDefaultAsOptimal: boolean; available: Region[]; }; rabbitmq: { @@ -263,7 +268,8 @@ export const DefaultConfigOptions: ConfigValue = { }, regions: { default: "fosscord", - available: [{ id: "fosscord", name: "Fosscord", vip: false, custom: false, deprecated: false, optimal: false }], + useDefaultAsOptimal: true, + available: [{ id: "fosscord", name: "Fosscord", endpoint: "127.0.0.1", vip: false, custom: false, deprecated: false }], }, rabbitmq: { host: null, diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts index 3e7e8917..21b059a3 100644 --- a/util/src/entities/Guild.ts +++ b/util/src/entities/Guild.ts @@ -41,7 +41,7 @@ export class Guild extends BaseClass { explicit_content_filter?: number; @Column({ type: "simple-array" }) - features: string[]; + features: string[]; //TODO use enum @Column({ nullable: true }) icon?: string; -- cgit 1.5.1 From 3d929755fbbfea4aa5119ce2a1bb087269fb69e8 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Tue, 31 Aug 2021 17:54:09 +0200 Subject: :sparkles: delete _ids from entities --- util/package-lock.json | 14 ++--- util/package.json | 2 +- util/src/entities/Application.ts | 16 +++--- util/src/entities/Attachment.ts | 34 ++++++++++++ util/src/entities/AuditLog.ts | 8 ++- util/src/entities/Ban.ts | 9 ++-- util/src/entities/BaseClass.ts | 34 ++++++------ util/src/entities/Channel.ts | 98 +++++++++++++++++++++++++++++------ util/src/entities/Config.ts | 8 +-- util/src/entities/ConnectedAccount.ts | 3 +- util/src/entities/Emoji.ts | 11 +--- util/src/entities/Guild.ts | 68 ++++++++++++++++-------- util/src/entities/Invite.ts | 12 +++-- util/src/entities/Member.ts | 86 ++++++++++++++++-------------- util/src/entities/Message.ts | 78 ++++++++++++---------------- util/src/entities/ReadState.ts | 9 ++-- util/src/entities/Recipient.ts | 19 +++++++ util/src/entities/Relationship.ts | 3 +- util/src/entities/Role.ts | 3 +- util/src/entities/Sticker.ts | 2 +- util/src/entities/Team.ts | 10 ++-- util/src/entities/TeamMember.ts | 6 ++- util/src/entities/Template.ts | 6 ++- util/src/entities/User.ts | 22 ++++---- util/src/entities/VoiceState.ts | 11 ++-- util/src/entities/Webhook.ts | 15 ++++-- util/src/entities/index.ts | 2 + util/src/interfaces/Event.ts | 13 ++--- util/src/tes.ts | 12 +++-- util/src/util/Config.ts | 2 +- util/src/util/Permissions.ts | 7 +-- 31 files changed, 382 insertions(+), 241 deletions(-) create mode 100644 util/src/entities/Attachment.ts create mode 100644 util/src/entities/Recipient.ts (limited to 'util/src/entities/Guild.ts') diff --git a/util/package-lock.json b/util/package-lock.json index bb04d0bc..47aca2d1 100644 --- a/util/package-lock.json +++ b/util/package-lock.json @@ -24,7 +24,7 @@ "reflect-metadata": "^0.1.13", "sqlite3": "^5.0.2", "typeorm": "^0.2.37", - "typescript": "^4.3.5", + "typescript": "^4.4.2", "typescript-json-schema": "^0.50.1" }, "devDependencies": { @@ -8235,9 +8235,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", + "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15150,9 +15150,9 @@ } }, "typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==" + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.2.tgz", + "integrity": "sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==" }, "typescript-json-schema": { "version": "0.50.1", diff --git a/util/package.json b/util/package.json index af14a521..39af7526 100644 --- a/util/package.json +++ b/util/package.json @@ -51,7 +51,7 @@ "reflect-metadata": "^0.1.13", "sqlite3": "^5.0.2", "typeorm": "^0.2.37", - "typescript": "^4.3.5", + "typescript": "^4.4.2", "typescript-json-schema": "^0.50.1" }, "jest": { diff --git a/util/src/entities/Application.ts b/util/src/entities/Application.ts index b179d171..2092cd4e 100644 --- a/util/src/entities/Application.ts +++ b/util/src/entities/Application.ts @@ -2,6 +2,7 @@ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Team } from "./Team"; +import { User } from "./User"; @Entity("applications") export class Application extends BaseClass { @@ -29,8 +30,9 @@ export class Application extends BaseClass { @Column({ nullable: true }) privacy_policy_url?: string; - @Column() - owner_id: string; + @JoinColumn({ name: "owner_id" }) + @ManyToOne(() => User) + owner?: User; @Column({ nullable: true }) summary?: string; @@ -38,18 +40,12 @@ export class Application extends BaseClass { @Column() verify_key: string; - @RelationId((application: Application) => application.team) - team_id: string; - @JoinColumn({ name: "team_id" }) - @ManyToOne(() => Team, (team: Team) => team.id) + @ManyToOne(() => Team) team?: Team; - @RelationId((application: Application) => application.guild) - guild_id: string; - @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild: Guild; // if this application is a game sold, this field will be the guild to which it has been linked @Column({ nullable: true }) diff --git a/util/src/entities/Attachment.ts b/util/src/entities/Attachment.ts new file mode 100644 index 00000000..ca893400 --- /dev/null +++ b/util/src/entities/Attachment.ts @@ -0,0 +1,34 @@ +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; +import { BaseClass } from "./BaseClass"; + +@Entity("attachments") +export class Attachment extends BaseClass { + @Column() + filename: string; // name of file attached + + @Column() + size: number; // size of file in bytes + + @Column() + url: string; // source url of file + + @Column() + proxy_url: string; // a proxied url of file + + @Column({ nullable: true }) + height?: number; // height of file (if image) + + @Column({ nullable: true }) + width?: number; // width of file (if image) + + @Column({ nullable: true }) + content_type?: string; + + @Column({ nullable: true }) + @RelationId((attachment: Attachment) => attachment.message) + message_id: string; + + @JoinColumn({ name: "message_id" }) + @ManyToOne(() => require("./Message").Message, (message: import("./Message").Message) => message.attachments) + message: import("./Message").Message; +} diff --git a/util/src/entities/AuditLog.ts b/util/src/entities/AuditLog.ts index ea8aa641..ceeb21fd 100644 --- a/util/src/entities/AuditLog.ts +++ b/util/src/entities/AuditLog.ts @@ -43,18 +43,16 @@ export enum AuditLogEvents { @Entity("audit_logs") export class AuditLogEntry extends BaseClass { - @RelationId((auditlog: AuditLogEntry) => auditlog.target) - target_id: string; - @JoinColumn({ name: "target_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) target?: User; + @Column({ nullable: true }) @RelationId((auditlog: AuditLogEntry) => auditlog.user) user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) user: User; @Column({ diff --git a/util/src/entities/Ban.ts b/util/src/entities/Ban.ts index f1cbd849..e8a6d648 100644 --- a/util/src/entities/Ban.ts +++ b/util/src/entities/Ban.ts @@ -5,25 +5,28 @@ import { User } from "./User"; @Entity("bans") export class Ban extends BaseClass { + @Column({ nullable: true }) @RelationId((ban: Ban) => ban.user) user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) user: User; + @Column({ nullable: true }) @RelationId((ban: Ban) => ban.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild: Guild; + @Column({ nullable: true }) @RelationId((ban: Ban) => ban.executor) executor_id: string; @JoinColumn({ name: "executor_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) executor: User; @Column() diff --git a/util/src/entities/BaseClass.ts b/util/src/entities/BaseClass.ts index 63ce5836..0856ccd1 100644 --- a/util/src/entities/BaseClass.ts +++ b/util/src/entities/BaseClass.ts @@ -1,13 +1,5 @@ import "reflect-metadata"; -import { - BaseEntity, - BeforeInsert, - BeforeUpdate, - EntityMetadata, - FindConditions, - FindManyOptions, - PrimaryColumn, -} from "typeorm"; +import { BaseEntity, BeforeInsert, BeforeUpdate, EntityMetadata, FindConditions, PrimaryColumn } from "typeorm"; import { Snowflake } from "../util/Snowflake"; import "missing-native-js-functions"; @@ -16,13 +8,12 @@ import "missing-native-js-functions"; export class BaseClass extends BaseEntity { @PrimaryColumn() - id: string; + id: string = Snowflake.generate(); // @ts-ignore - constructor(public props?: any, public opts: { id?: string } = {}) { + constructor(public props?: any) { super(); - - this.id = this.opts.id || Snowflake.generate(); + this.assign(props); } get construct(): any { @@ -35,13 +26,15 @@ export class BaseClass extends BaseEntity { assign(props: any) { if (!props || typeof props !== "object") return; - - delete props.id; delete props.opts; delete props.props; - const properties = new Set(this.metadata.columns.map((x: any) => x.propertyName)); - // will not include relational properties (e.g. @RelationId @ManyToMany) + const properties = new Set( + this.metadata.columns + .map((x: any) => x.propertyName) + .concat(this.metadata.relations.map((x) => x.propertyName)) + ); + // will not include relational properties for (const key in props) { if (!properties.has(key)) continue; @@ -65,8 +58,11 @@ export class BaseClass extends BaseEntity { } toJSON(): any { - // @ts-ignore - return Object.fromEntries(this.metadata.columns.map((x) => [x.propertyName, this[x.propertyName]])); + return Object.fromEntries( + this.metadata.columns // @ts-ignore + .map((x) => [x.propertyName, this[x.propertyName]]) // @ts-ignore + .concat(this.metadata.relations.map((x) => [x.propertyName, this[x.propertyName]])) + ); } static increment(conditions: FindConditions, propertyPath: string, value: number | string) { diff --git a/util/src/entities/Channel.ts b/util/src/entities/Channel.ts index 4900f485..e3586dfc 100644 --- a/util/src/entities/Channel.ts +++ b/util/src/entities/Channel.ts @@ -1,8 +1,12 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToMany, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Message } from "./Message"; import { User } from "./User"; +import { HTTPError } from "lambert-server"; +import { emitEvent, getPermission, Snowflake } from "../util"; +import { ChannelCreateEvent } from "../interfaces"; +import { Recipient } from "./Recipient"; export enum ChannelType { GUILD_TEXT = 0, // a text channel within a server @@ -25,40 +29,39 @@ export class Channel extends BaseClass { @Column({ type: "simple-enum", enum: ChannelType }) type: ChannelType; - @Column("simple-array") - @RelationId((channel: Channel) => channel.recipients) - recipient_ids: string[]; - - @JoinColumn({ name: "recipient_ids" }) - @ManyToMany(() => User, (user: User) => user.id) - recipients?: User[]; + @OneToMany(() => Recipient, (recipient: Recipient) => recipient.channel, { cascade: true }) + recipients?: Recipient[]; + @Column({ nullable: true }) @RelationId((channel: Channel) => channel.last_message) last_message_id: string; @JoinColumn({ name: "last_message_id" }) - @ManyToOne(() => Message, (message: Message) => message.id) + @ManyToOne(() => Message) last_message?: Message; + @Column({ nullable: true }) @RelationId((channel: Channel) => channel.guild) guild_id?: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild: Guild; + @Column({ nullable: true }) @RelationId((channel: Channel) => channel.parent) parent_id: string; @JoinColumn({ name: "parent_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) parent?: Channel; + @Column({ nullable: true }) @RelationId((channel: Channel) => channel.owner) owner_id: string; @JoinColumn({ name: "owner_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) owner: User; @Column({ nullable: true }) @@ -82,14 +85,77 @@ export class Channel extends BaseClass { @Column({ nullable: true }) user_limit?: number; - @Column() - nsfw: boolean; + @Column({ nullable: true }) + nsfw?: boolean; - @Column() - rate_limit_per_user: number; + @Column({ nullable: true }) + rate_limit_per_user?: number; @Column({ nullable: true }) topic?: string; + + // TODO: DM channel + static async createChannel( + channel: Partial, + user_id: string = "0", + opts?: { + keepId?: boolean; + skipExistsCheck?: boolean; + skipPermissionCheck?: boolean; + skipEventEmit?: boolean; + } + ) { + if (!opts?.skipPermissionCheck) { + // Always check if user has permission first + const permissions = await getPermission(user_id, channel.guild_id); + permissions.hasThrow("MANAGE_CHANNELS"); + } + + switch (channel.type) { + case ChannelType.GUILD_TEXT: + case ChannelType.GUILD_VOICE: + if (channel.parent_id && !opts?.skipExistsCheck) { + const exists = await Channel.findOneOrFail({ id: channel.parent_id }); + if (!exists) throw new HTTPError("Parent id channel doesn't exist", 400); + if (exists.guild_id !== channel.guild_id) + throw new HTTPError("The category channel needs to be in the guild"); + } + break; + case ChannelType.GUILD_CATEGORY: + break; + case ChannelType.DM: + case ChannelType.GROUP_DM: + throw new HTTPError("You can't create a dm channel in a guild"); + // TODO: check if guild is community server + case ChannelType.GUILD_STORE: + case ChannelType.GUILD_NEWS: + default: + throw new HTTPError("Not yet supported"); + } + + if (!channel.permission_overwrites) channel.permission_overwrites = []; + // TODO: auto generate position + + channel = { + ...channel, + ...(!opts?.keepId && { id: Snowflake.generate() }), + created_at: new Date(), + position: channel.position || 0, + }; + + await Promise.all([ + Channel.insert(channel), + !opts?.skipEventEmit + ? emitEvent({ + event: "CHANNEL_CREATE", + data: channel, + guild_id: channel.guild_id, + } as ChannelCreateEvent) + : Promise.resolve(), + ]); + + return channel; + } } export interface ChannelPermissionOverwrite { diff --git a/util/src/entities/Config.ts b/util/src/entities/Config.ts index 320a729c..04dc3c36 100644 --- a/util/src/entities/Config.ts +++ b/util/src/entities/Config.ts @@ -199,12 +199,12 @@ export const DefaultConfigOptions: ConfigValue = { window: 5, }, webhook: { - count: 5, - window: 20, + count: 10, + window: 5, }, channel: { - count: 5, - window: 20, + count: 10, + window: 5, }, auth: { login: { diff --git a/util/src/entities/ConnectedAccount.ts b/util/src/entities/ConnectedAccount.ts index e48bc322..75982d01 100644 --- a/util/src/entities/ConnectedAccount.ts +++ b/util/src/entities/ConnectedAccount.ts @@ -4,11 +4,12 @@ import { User } from "./User"; @Entity("connected_accounts") export class ConnectedAccount extends BaseClass { + @Column({ nullable: true }) @RelationId((account: ConnectedAccount) => account.user) user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.connected_accounts) + @ManyToOne(() => User) user: User; @Column({ select: false }) diff --git a/util/src/entities/Emoji.ts b/util/src/entities/Emoji.ts index 4c0fccd3..181aff2c 100644 --- a/util/src/entities/Emoji.ts +++ b/util/src/entities/Emoji.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Role } from "./Role"; @@ -15,7 +15,7 @@ export class Emoji extends BaseClass { guild_id: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.emojis) + @ManyToOne(() => Guild) guild: Guild; @Column() @@ -26,11 +26,4 @@ export class Emoji extends BaseClass { @Column() require_colons: boolean; - - @RelationId((emoji: Emoji) => emoji.roles) - role_ids: string[]; - - @JoinColumn({ name: "role_ids" }) - @ManyToMany(() => Role, (role: Role) => role.id) - roles: Role[]; // roles this emoji is whitelisted to (new discord feature?) } diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts index 3e7e8917..c1ef00ac 100644 --- a/util/src/entities/Guild.ts +++ b/util/src/entities/Guild.ts @@ -1,20 +1,30 @@ import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToMany, OneToOne, RelationId } from "typeorm"; +import { Ban } from "./Ban"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Emoji } from "./Emoji"; import { Invite } from "./Invite"; import { Member } from "./Member"; import { Role } from "./Role"; +import { Sticker } from "./Sticker"; +import { Template } from "./Template"; import { User } from "./User"; import { VoiceState } from "./VoiceState"; +import { Webhook } from "./Webhook"; + +// TODO: application_command_count, application_command_counts: {1: 0, 2: 0, 3: 0} +// TODO: guild_scheduled_events +// TODO: stage_instances +// TODO: threads @Entity("guilds") export class Guild extends BaseClass { + @Column({ nullable: true }) @RelationId((guild: Guild) => guild.afk_channel) afk_channel_id?: string; @JoinColumn({ name: "afk_channel_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) afk_channel?: Channel; @Column({ nullable: true }) @@ -25,6 +35,10 @@ export class Guild extends BaseClass { // @Column({ nullable: true }) // application?: string; + @JoinColumn({ name: "ban_ids" }) + @OneToMany(() => Ban, (ban: Ban) => ban.guild) + bans: Ban[]; + @Column({ nullable: true }) banner?: string; @@ -64,52 +78,57 @@ export class Guild extends BaseClass { @Column({ nullable: true }) presence_count?: number; // users online - @RelationId((guild: Guild) => guild.members) - member_ids: string[]; - - @JoinColumn({ name: "member_ids" }) @OneToMany(() => Member, (member: Member) => member.guild) members: Member[]; - @RelationId((guild: Guild) => guild.roles) - role_ids: string[]; - @JoinColumn({ name: "role_ids" }) @OneToMany(() => Role, (role: Role) => role.guild) roles: Role[]; - @RelationId((guild: Guild) => guild.channels) - channel_ids: string[]; - @JoinColumn({ name: "channel_ids" }) @OneToMany(() => Channel, (channel: Channel) => channel.guild) channels: Channel[]; - @RelationId((guild: Guild) => guild.emojis) - emoji_ids: string[]; + @Column({ nullable: true }) + @RelationId((guild: Guild) => guild.template) + template_id: string; + + @JoinColumn({ name: "template_id" }) + @ManyToOne(() => Template) + template: Template; @JoinColumn({ name: "emoji_ids" }) @OneToMany(() => Emoji, (emoji: Emoji) => emoji.guild) emojis: Emoji[]; - @RelationId((guild: Guild) => guild.voice_states) - voice_state_ids: string[]; + @JoinColumn({ name: "sticker_ids" }) + @OneToMany(() => Sticker, (sticker: Sticker) => sticker.guild) + stickers: Sticker[]; + + @JoinColumn({ name: "invite_ids" }) + @OneToMany(() => Invite, (invite: Invite) => invite.guild) + invites: Invite[]; @JoinColumn({ name: "voice_state_ids" }) @OneToMany(() => VoiceState, (voicestate: VoiceState) => voicestate.guild) voice_states: VoiceState[]; + @JoinColumn({ name: "webhook_ids" }) + @OneToMany(() => Webhook, (webhook: Webhook) => webhook.guild) + webhooks: Webhook[]; + @Column({ nullable: true }) mfa_level?: number; @Column() name: string; + @Column({ nullable: true }) @RelationId((guild: Guild) => guild.owner) owner_id: string; - @JoinColumn({ name: "owner_id" }) - @OneToOne(() => User) + @JoinColumn([{ name: "owner_id", referencedColumnName: "id" }]) + @ManyToOne(() => User) owner: User; @Column({ nullable: true }) @@ -121,18 +140,20 @@ export class Guild extends BaseClass { @Column({ nullable: true }) premium_tier?: number; // nitro boost level + @Column({ nullable: true }) @RelationId((guild: Guild) => guild.public_updates_channel) public_updates_channel_id: string; @JoinColumn({ name: "public_updates_channel_id" }) - @OneToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) public_updates_channel?: Channel; + @Column({ nullable: true }) @RelationId((guild: Guild) => guild.rules_channel) rules_channel_id?: string; @JoinColumn({ name: "rules_channel_id" }) - @OneToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) rules_channel?: string; @Column({ nullable: true }) @@ -141,11 +162,12 @@ export class Guild extends BaseClass { @Column({ nullable: true }) splash?: string; + @Column({ nullable: true }) @RelationId((guild: Guild) => guild.system_channel) system_channel_id?: string; @JoinColumn({ name: "system_channel_id" }) - @OneToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) system_channel?: Channel; @Column({ nullable: true }) @@ -154,11 +176,12 @@ export class Guild extends BaseClass { @Column({ nullable: true }) unavailable?: boolean; + @Column({ nullable: true }) @RelationId((guild: Guild) => guild.vanity_url) vanity_url_code?: string; @JoinColumn({ name: "vanity_url_code" }) - @OneToOne(() => Invite) + @ManyToOne(() => Invite) vanity_url?: Invite; @Column({ nullable: true }) @@ -176,11 +199,12 @@ export class Guild extends BaseClass { }[]; }; + @Column({ nullable: true }) @RelationId((guild: Guild) => guild.widget_channel) widget_channel_id?: string; @JoinColumn({ name: "widget_channel_id" }) - @OneToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) widget_channel?: Channel; @Column({ nullable: true }) diff --git a/util/src/entities/Invite.ts b/util/src/entities/Invite.ts index d8c6d69c..01e22294 100644 --- a/util/src/entities/Invite.ts +++ b/util/src/entities/Invite.ts @@ -27,32 +27,36 @@ export class Invite extends BaseClass { @Column() expires_at: Date; + @Column({ nullable: true }) @RelationId((invite: Invite) => invite.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild: Guild; + @Column({ nullable: true }) @RelationId((invite: Invite) => invite.channel) channel_id: string; @JoinColumn({ name: "channel_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) channel: Channel; + @Column({ nullable: true }) @RelationId((invite: Invite) => invite.inviter) inviter_id: string; @JoinColumn({ name: "inviter_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) inviter: User; + @Column({ nullable: true }) @RelationId((invite: Invite) => invite.target_user) target_user_id: string; @JoinColumn({ name: "target_user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) target_user?: string; // could be used for "User specific invites" https://github.com/fosscord/fosscord/issues/62 @Column({ nullable: true }) diff --git a/util/src/entities/Member.ts b/util/src/entities/Member.ts index c5d289ef..d2d78bb9 100644 --- a/util/src/entities/Member.ts +++ b/util/src/entities/Member.ts @@ -15,27 +15,22 @@ import { Role } from "./Role"; @Entity("members") export class Member extends BaseClass { - @RelationId((member: Member) => member.user) - user_id: string; - - @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @JoinColumn({ name: "id" }) + @ManyToOne(() => User) user: User; + @Column({ nullable: true }) @RelationId((member: Member) => member.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.members) + @ManyToOne(() => Guild) guild: Guild; @Column({ nullable: true }) nick?: string; - @RelationId((member: Member) => member.roles) - role_ids: string[]; - - @JoinTable() + @JoinTable({ name: "member_roles" }) @ManyToMany(() => Role) roles: Role[]; @@ -62,7 +57,7 @@ export class Member extends BaseClass { read_state: Record; static async IsInGuildOrFail(user_id: string, guild_id: string) { - if (await Member.count({ id: user_id, guild_id })) return true; + if (await Member.count({ id: user_id, guild: { id: guild_id } })) return true; throw new HTTPError("You are not member of this guild", 403); } @@ -99,46 +94,54 @@ export class Member extends BaseClass { static async addRole(user_id: string, guild_id: string, role_id: string) { const [member] = await Promise.all([ + // @ts-ignore Member.findOneOrFail({ where: { id: user_id, guild_id: guild_id }, - relations: ["user"], // we don't want to load the role objects just the ids + relations: ["user", "roles"], // we don't want to load the role objects just the ids + select: ["roles.id"], }), await Role.findOneOrFail({ id: role_id, guild_id: guild_id }), ]); - member.role_ids.push(role_id); - member.save(); + member.roles.push(new Role({ id: role_id })); - await emitEvent({ - event: "GUILD_MEMBER_UPDATE", - data: { + await Promise.all([ + member.save(), + emitEvent({ + event: "GUILD_MEMBER_UPDATE", + data: { + guild_id: guild_id, + user: member.user, + roles: member.roles.map((x) => x.id), + }, guild_id: guild_id, - user: member.user, - roles: member.role_ids, - }, - guild_id: guild_id, - } as GuildMemberUpdateEvent); + } as GuildMemberUpdateEvent), + ]); } static async removeRole(user_id: string, guild_id: string, role_id: string) { const [member] = await Promise.all([ + // @ts-ignore Member.findOneOrFail({ where: { id: user_id, guild_id: guild_id }, - relations: ["user"], // we don't want to load the role objects just the ids + relations: ["user", "roles"], // we don't want to load the role objects just the ids + select: ["roles.id"], }), await Role.findOneOrFail({ id: role_id, guild_id: guild_id }), ]); - member.role_ids.remove(role_id); - member.save(); + member.roles = member.roles.filter((x) => x.id == role_id); - await emitEvent({ - event: "GUILD_MEMBER_UPDATE", - data: { + await Promise.all([ + member.save(), + emitEvent({ + event: "GUILD_MEMBER_UPDATE", + data: { + guild_id: guild_id, + user: member.user, + roles: member.roles.map((x) => x.id), + }, guild_id: guild_id, - user: member.user, - roles: member.role_ids, - }, - guild_id: guild_id, - } as GuildMemberUpdateEvent); + } as GuildMemberUpdateEvent), + ]); } static async changeNickname(user_id: string, guild_id: string, nickname: string) { @@ -175,11 +178,14 @@ export class Member extends BaseClass { throw new HTTPError(`You are at the ${maxGuilds} server limit.`, 403); } - const guild = await Guild.findOneOrFail(guild_id, { + const guild = await Guild.findOneOrFail({ + where: { + id: guild_id, + }, relations: ["channels", "emojis", "members", "roles", "stickers"], }); - if (await Member.count({ id: user.id, guild_id })) + if (await Member.count({ id: user.id, guild: { id: guild_id } })) throw new HTTPError("You are already a member of this guild", 400); const member = { @@ -194,23 +200,23 @@ export class Member extends BaseClass { pending: false, }; // @ts-ignore - guild.joined_at = member.joined_at; + guild.joined_at = member.joined_at.toISOString(); await Promise.all([ - new Member({ + Member.insert({ ...member, + roles: undefined, read_state: {}, settings: { channel_overrides: [], message_notifications: 0, mobile_push: true, - mute_config: null, muted: false, suppress_everyone: false, suppress_roles: false, version: 0, }, - }).save(), + }), Guild.increment({ id: guild_id }, "member_count", 1), emitEvent({ event: "GUILD_MEMBER_ADD", @@ -223,7 +229,7 @@ export class Member extends BaseClass { } as GuildMemberAddEvent), emitEvent({ event: "GUILD_CREATE", - data: guild, + data: { ...guild, members: [...guild.members, member] }, user_id, } as GuildCreateEvent), ]); diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts index 43d0f9d0..542b2b55 100644 --- a/util/src/entities/Message.ts +++ b/util/src/entities/Message.ts @@ -9,8 +9,10 @@ import { CreateDateColumn, Entity, JoinColumn, + JoinTable, ManyToMany, ManyToOne, + OneToMany, RelationId, UpdateDateColumn, } from "typeorm"; @@ -18,6 +20,7 @@ import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Webhook } from "./Webhook"; import { Sticker } from "./Sticker"; +import { Attachment } from "./Attachment"; export enum MessageType { DEFAULT = 0, @@ -44,46 +47,52 @@ export class Message extends BaseClass { @Column() id: string; + @Column({ nullable: true }) @RelationId((message: Message) => message.channel) channel_id: string; @JoinColumn({ name: "channel_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) channel: Channel; + @Column({ nullable: true }) @RelationId((message: Message) => message.guild) guild_id?: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild?: Guild; + @Column({ nullable: true }) @RelationId((message: Message) => message.author) author_id: string; - @JoinColumn({ name: "author_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @JoinColumn({ name: "author_id", referencedColumnName: "id" }) + @ManyToOne(() => User) author?: User; + @Column({ nullable: true }) @RelationId((message: Message) => message.member) member_id: string; @JoinColumn({ name: "member_id" }) - @ManyToOne(() => Member, (member: Member) => member.id) + @ManyToOne(() => Member) member?: Member; + @Column({ nullable: true }) @RelationId((message: Message) => message.webhook) webhook_id: string; @JoinColumn({ name: "webhook_id" }) - @ManyToOne(() => Webhook, (webhook: Webhook) => webhook.id) + @ManyToOne(() => Webhook) webhook?: Webhook; + @Column({ nullable: true }) @RelationId((message: Message) => message.application) application_id: string; @JoinColumn({ name: "application_id" }) - @ManyToOne(() => Application, (application: Application) => application.id) + @ManyToOne(() => Application) application?: Application; @Column({ nullable: true }) @@ -103,29 +112,25 @@ export class Message extends BaseClass { @Column({ nullable: true }) mention_everyone?: boolean; - @RelationId((message: Message) => message.mentions) - mention_user_ids: string[]; - - @JoinColumn({ name: "mention_user_ids" }) - @ManyToMany(() => User, (user: User) => user.id) + @JoinTable({ name: "message_user_mentions" }) + @ManyToMany(() => User) mentions: User[]; - @RelationId((message: Message) => message.mention_roles) - mention_role_ids: string[]; - - @JoinColumn({ name: "mention_role_ids" }) - @ManyToMany(() => Role, (role: Role) => role.id) + @JoinTable({ name: "message_role_mentions" }) + @ManyToMany(() => Role) mention_roles: Role[]; - @RelationId((message: Message) => message.mention_channels) - mention_channel_ids: string[]; - - @JoinColumn({ name: "mention_channel_ids" }) - @ManyToMany(() => Channel, (channel: Channel) => channel.id) + @JoinTable({ name: "message_channel_mentions" }) + @ManyToMany(() => Channel) mention_channels: Channel[]; - @Column({ type: "simple-json" }) - attachments: Attachment[]; + @JoinTable({ name: "message_stickers" }) + @ManyToMany(() => Sticker) + sticker_items?: Sticker[]; + + @JoinColumn({ name: "attachment_ids" }) + @OneToMany(() => Attachment, (attachment: Attachment) => attachment.message) + attachments?: Attachment[]; @Column({ type: "simple-json" }) embeds: Embed[]; @@ -150,14 +155,6 @@ export class Message extends BaseClass { @Column({ nullable: true }) flags?: string; - - @RelationId((message: Message) => message.stickers) - sticker_ids: string[]; - - @JoinColumn({ name: "sticker_ids" }) - @ManyToMany(() => Sticker, (sticker: Sticker) => sticker.id) - stickers?: Sticker[]; - @Column({ type: "simple-json", nullable: true }) message_reference?: { message_id: string; @@ -166,7 +163,7 @@ export class Message extends BaseClass { }; @JoinColumn({ name: "message_reference_id" }) - @ManyToOne(() => Message, (message: Message) => message.id) + @ManyToOne(() => Message) referenced_message?: Message; @Column({ type: "simple-json", nullable: true }) @@ -178,8 +175,8 @@ export class Message extends BaseClass { // user: User; // TODO: autopopulate user }; - @Column({ type: "simple-json" }) - components: MessageComponent[]; + @Column({ type: "simple-json", nullable: true }) + components?: MessageComponent[]; } export interface MessageComponent { @@ -198,17 +195,6 @@ export enum MessageComponentType { Button = 2, } -export interface Attachment { - id: string; // attachment id - filename: string; // name of file attached - size: number; // size of file in bytes - url: string; // source url of file - proxy_url: string; // a proxied url of file - height?: number; // height of file (if image) - width?: number; // width of file (if image) - content_type?: string; -} - export interface Embed { title?: string; //title of embed type?: EmbedType; // type of embed (always "rich" for webhook embeds) diff --git a/util/src/entities/ReadState.ts b/util/src/entities/ReadState.ts index 650ee4c3..8dd05b21 100644 --- a/util/src/entities/ReadState.ts +++ b/util/src/entities/ReadState.ts @@ -10,25 +10,28 @@ import { User } from "./User"; @Entity("read_states") export class ReadState extends BaseClass { + @Column({ nullable: true }) @RelationId((read_state: ReadState) => read_state.channel) channel_id: string; @JoinColumn({ name: "channel_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) channel: Channel; + @Column({ nullable: true }) @RelationId((read_state: ReadState) => read_state.user) user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) user: User; + @Column({ nullable: true }) @RelationId((read_state: ReadState) => read_state.last_message) last_message_id: string; @JoinColumn({ name: "last_message_id" }) - @ManyToOne(() => Message, (message: Message) => message.id) + @ManyToOne(() => Message) last_message?: Message; @Column({ nullable: true }) diff --git a/util/src/entities/Recipient.ts b/util/src/entities/Recipient.ts new file mode 100644 index 00000000..75d5b94d --- /dev/null +++ b/util/src/entities/Recipient.ts @@ -0,0 +1,19 @@ +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; +import { BaseClass } from "./BaseClass"; + +@Entity("recipients") +export class Recipient extends BaseClass { + @Column() + @RelationId((recipient: Recipient) => recipient.channel) + channel_id: string; + + @JoinColumn({ name: "channel_id" }) + @ManyToOne(() => require("./Channel").Channel) + channel: import("./Channel").Channel; + + @JoinColumn({ name: "id" }) + @ManyToOne(() => require("./User").User) + user: import("./User").User; + + // TODO: settings/mute/nick/added at/encryption keys/read_state +} diff --git a/util/src/entities/Relationship.ts b/util/src/entities/Relationship.ts index cf3624bf..5935f5b6 100644 --- a/util/src/entities/Relationship.ts +++ b/util/src/entities/Relationship.ts @@ -11,11 +11,12 @@ export enum RelationshipType { @Entity("relationships") export class Relationship extends BaseClass { + @Column({ nullable: true }) @RelationId((relationship: Relationship) => relationship.user) user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.relationships) + @ManyToOne(() => User) user: User; @Column({ nullable: true }) diff --git a/util/src/entities/Role.ts b/util/src/entities/Role.ts index be8c731d..33c8d272 100644 --- a/util/src/entities/Role.ts +++ b/util/src/entities/Role.ts @@ -5,11 +5,12 @@ import { Guild } from "./Guild"; @Entity("roles") export class Role extends BaseClass { + @Column({ nullable: true }) @RelationId((role: Role) => role.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild: Guild; @Column() diff --git a/util/src/entities/Sticker.ts b/util/src/entities/Sticker.ts index 12394207..7730a86a 100644 --- a/util/src/entities/Sticker.ts +++ b/util/src/entities/Sticker.ts @@ -31,7 +31,7 @@ export class Sticker extends BaseClass { guild_id?: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild?: Guild; @Column({ type: "simple-enum", enum: StickerType }) diff --git a/util/src/entities/Team.ts b/util/src/entities/Team.ts index b37f368c..beb8bf68 100644 --- a/util/src/entities/Team.ts +++ b/util/src/entities/Team.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToMany, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { TeamMember } from "./TeamMember"; import { User } from "./User"; @@ -8,20 +8,18 @@ export class Team extends BaseClass { @Column({ nullable: true }) icon?: string; - @RelationId((team: Team) => team.members) - member_ids: string[]; - @JoinColumn({ name: "member_ids" }) - @ManyToMany(() => TeamMember, (member: TeamMember) => member.id) + @OneToMany(() => TeamMember, (member: TeamMember) => member.team) members: TeamMember[]; @Column() name: string; + @Column({ nullable: true }) @RelationId((team: Team) => team.owner_user) owner_user_id: string; @JoinColumn({ name: "owner_user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) owner_user: User; } diff --git a/util/src/entities/TeamMember.ts b/util/src/entities/TeamMember.ts index d4c95397..6b184d08 100644 --- a/util/src/entities/TeamMember.ts +++ b/util/src/entities/TeamMember.ts @@ -15,17 +15,19 @@ export class TeamMember extends BaseClass { @Column({ type: "simple-array" }) permissions: string[]; + @Column({ nullable: true }) @RelationId((member: TeamMember) => member.team) team_id: string; @JoinColumn({ name: "team_id" }) - @ManyToOne(() => require("./Team").Team, (team: import("./Team").Team) => team.id) + @ManyToOne(() => require("./Team").Team, (team: import("./Team").Team) => team.members) team: import("./Team").Team; + @Column({ nullable: true }) @RelationId((member: TeamMember) => member.user) user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) user: User; } diff --git a/util/src/entities/Template.ts b/util/src/entities/Template.ts index 2d8c9b6c..76f77ba6 100644 --- a/util/src/entities/Template.ts +++ b/util/src/entities/Template.ts @@ -17,11 +17,12 @@ export class Template extends BaseClass { @Column({ nullable: true }) usage_count?: number; + @Column({ nullable: true }) @RelationId((template: Template) => template.creator) creator_id: string; @JoinColumn({ name: "creator_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) creator: User; @Column() @@ -30,11 +31,12 @@ export class Template extends BaseClass { @Column() updated_at: Date; + @Column({ nullable: true }) @RelationId((template: Template) => template.source_guild) source_guild_id: string; @JoinColumn({ name: "source_guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) source_guild: Guild; @Column({ type: "simple-json" }) diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts index 73afba67..39f654be 100644 --- a/util/src/entities/User.ts +++ b/util/src/entities/User.ts @@ -1,9 +1,10 @@ -import { Column, Entity, FindOneOptions, JoinColumn, OneToMany, RelationId } from "typeorm"; +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" @@ -105,16 +106,10 @@ export class User extends BaseClass { @Column() public_flags: string; - @RelationId((user: User) => user.relationships) - relationship_ids: string[]; // array of guild ids the user is part of - @JoinColumn({ name: "relationship_ids" }) @OneToMany(() => Relationship, (relationship: Relationship) => relationship.user, { cascade: true }) relationships: Relationship[]; - @RelationId((user: User) => user.connected_accounts) - connected_account_ids: string[]; // array of guild ids the user is part of - @JoinColumn({ name: "connected_account_ids" }) @OneToMany(() => ConnectedAccount, (account: ConnectedAccount) => account.user) connected_accounts: ConnectedAccount[]; @@ -126,16 +121,19 @@ export class User extends BaseClass { }; @Column({ type: "simple-array" }) - fingerprints: string[] = []; // array of fingerprints -> used to prevent multiple accounts + fingerprints: string[]; // array of fingerprints -> used to prevent multiple accounts @Column({ type: "simple-json" }) settings: UserSettings; static async getPublicUser(user_id: string, opts?: FindOneOptions) { - const user = await User.findOne(user_id, { - ...opts, - select: [...PublicUserProjection, ...(opts?.select || [])], - }); + const user = await User.findOne( + { id: user_id }, + { + ...opts, + select: [...PublicUserProjection, ...(opts?.select || [])], + } + ); if (!user) throw new HTTPError("User not found", 404); return user; } diff --git a/util/src/entities/VoiceState.ts b/util/src/entities/VoiceState.ts index e9d3dfa2..c5040cf1 100644 --- a/util/src/entities/VoiceState.ts +++ b/util/src/entities/VoiceState.ts @@ -1,4 +1,4 @@ -import { Column, Entity, JoinColumn, ManyToOne, OneToOne, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { Guild } from "./Guild"; @@ -6,25 +6,28 @@ import { User } from "./User"; @Entity("voice_states") export class VoiceState extends BaseClass { + @Column({ nullable: true }) @RelationId((voice_state: VoiceState) => voice_state.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild?: Guild; + @Column({ nullable: true }) @RelationId((voice_state: VoiceState) => voice_state.channel) channel_id: string; @JoinColumn({ name: "channel_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) channel: Channel; + @Column({ nullable: true }) @RelationId((voice_state: VoiceState) => voice_state.user) user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) user: User; @Column() diff --git a/util/src/entities/Webhook.ts b/util/src/entities/Webhook.ts index a75cb959..12ba0d08 100644 --- a/util/src/entities/Webhook.ts +++ b/util/src/entities/Webhook.ts @@ -27,38 +27,43 @@ export class Webhook extends BaseClass { @Column({ nullable: true }) token?: string; + @Column({ nullable: true }) @RelationId((webhook: Webhook) => webhook.guild) guild_id: string; @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) guild: Guild; + @Column({ nullable: true }) @RelationId((webhook: Webhook) => webhook.channel) channel_id: string; @JoinColumn({ name: "channel_id" }) - @ManyToOne(() => Channel, (channel: Channel) => channel.id) + @ManyToOne(() => Channel) channel: Channel; + @Column({ nullable: true }) @RelationId((webhook: Webhook) => webhook.application) application_id: string; @JoinColumn({ name: "application_id" }) - @ManyToOne(() => Application, (application: Application) => application.id) + @ManyToOne(() => Application) application: Application; + @Column({ nullable: true }) @RelationId((webhook: Webhook) => webhook.user) user_id: string; @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.id) + @ManyToOne(() => User) user: User; + @Column({ nullable: true }) @RelationId((webhook: Webhook) => webhook.guild) source_guild_id: string; @JoinColumn({ name: "source_guild_id" }) - @ManyToOne(() => Guild, (guild: Guild) => guild.id) + @ManyToOne(() => Guild) source_guild: Guild; } diff --git a/util/src/entities/index.ts b/util/src/entities/index.ts index e0246a10..aa37ae2e 100644 --- a/util/src/entities/index.ts +++ b/util/src/entities/index.ts @@ -1,4 +1,5 @@ export * from "./Application"; +export * from "./Attachment"; export * from "./AuditLog"; export * from "./Ban"; export * from "./BaseClass"; @@ -12,6 +13,7 @@ export * from "./Member"; export * from "./Message"; export * from "./RateLimit"; export * from "./ReadState"; +export * from "./Recipient"; export * from "./Relationship"; export * from "./Role"; export * from "./Sticker"; diff --git a/util/src/interfaces/Event.ts b/util/src/interfaces/Event.ts index 814a8beb..7ea1bd49 100644 --- a/util/src/interfaces/Event.ts +++ b/util/src/interfaces/Event.ts @@ -89,14 +89,7 @@ export interface ReadyEventData { }; merged_members?: Omit[][]; // probably all users who the user is in contact with - users?: { - avatar: string | null; - discriminator: string; - id: string; - username: string; - bot: boolean; - public_flags: string; - }[]; + users?: PublicUser[]; } export interface ReadyEvent extends Event { @@ -130,7 +123,9 @@ export interface ChannelPinsUpdateEvent extends Event { export interface GuildCreateEvent extends Event { event: "GUILD_CREATE"; - data: Guild; + data: Guild & { + joined_at: Date; + }; } export interface GuildUpdateEvent extends Event { diff --git a/util/src/tes.ts b/util/src/tes.ts index b469f1d8..e326dee1 100644 --- a/util/src/tes.ts +++ b/util/src/tes.ts @@ -5,10 +5,14 @@ import { initDatabase } from "./util"; initDatabase().then(async (x) => { try { - const user = await new User( - { guilds: [], discriminator: "1", username: "test", flags: "0", public_flags: "0" }, - { id: "0" } - ).save(); + const user = await new User({ + guilds: [], + discriminator: "1", + username: "test", + flags: "0", + public_flags: "0", + id: "0", + }).save(); user.relationships = [new Relationship({ type: RelationshipType.friends })]; diff --git a/util/src/util/Config.ts b/util/src/util/Config.ts index 508a8901..1ec71ad0 100644 --- a/util/src/util/Config.ts +++ b/util/src/util/Config.ts @@ -7,7 +7,7 @@ var config: ConfigEntity; export const Config = { init: async function init() { if (config) return config; - config = (await ConfigEntity.findOne({ id: "0" })) || new ConfigEntity({}, { id: "0" }); + config = (await ConfigEntity.findOne({ id: "0" })) || new ConfigEntity({ id: "0" }); return this.set((config.value || {}).merge(DefaultConfigOptions)); }, diff --git a/util/src/util/Permissions.ts b/util/src/util/Permissions.ts index ab6aa795..89b316ee 100644 --- a/util/src/util/Permissions.ts +++ b/util/src/util/Permissions.ts @@ -220,13 +220,14 @@ export async function getPermission(user_id?: string, guild_id?: string, channel guild = await Guild.findOneOrFail({ id: guild_id }); if (guild.owner_id === user_id) return new Permissions(Permissions.FLAGS.ADMINISTRATOR); - member = await Member.findOneOrFail({ where: { guild_id, id: user_id }, relations: ["roles"] }); + member = await Member.findOneOrFail({ where: { guild: guild_id, id: user_id }, relations: ["roles"] }); } + // TODO: remove guild.roles and convert recipient_ids to recipients var permission = Permissions.finalPermission({ user: { id: user_id, - roles: member?.role_ids || [], + roles: member?.roles.map((x) => x.id) || [], }, guild: { roles: member?.roles || [], @@ -234,7 +235,7 @@ export async function getPermission(user_id?: string, guild_id?: string, channel channel: { overwrites: channel?.permission_overwrites, owner_id: channel?.owner_id, - recipient_ids: channel?.recipient_ids, + recipient_ids: channel?.recipients?.map((x) => x.id), }, }); -- cgit 1.5.1