diff options
Diffstat (limited to 'util/src/entities')
34 files changed, 0 insertions, 3647 deletions
diff --git a/util/src/entities/Application.ts b/util/src/entities/Application.ts deleted file mode 100644 index fab3d93f..00000000 --- a/util/src/entities/Application.ts +++ /dev/null @@ -1,109 +0,0 @@ -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 { - @Column() - name: string; - - @Column({ nullable: true }) - icon?: string; - - @Column() - description: string; - - @Column({ type: "simple-array", nullable: true }) - rpc_origins?: string[]; - - @Column() - bot_public: boolean; - - @Column() - bot_require_code_grant: boolean; - - @Column({ nullable: true }) - terms_of_service_url?: string; - - @Column({ nullable: true }) - privacy_policy_url?: string; - - @JoinColumn({ name: "owner_id" }) - @ManyToOne(() => User) - owner?: User; - - @Column({ nullable: true }) - summary?: string; - - @Column() - verify_key: string; - - @JoinColumn({ name: "team_id" }) - @ManyToOne(() => Team, { - onDelete: "CASCADE", - }) - team?: Team; - - @JoinColumn({ name: "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 }) - 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({ 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({ nullable: true }) - cover_image?: string; // the application's default rich presence invite cover image hash - - @Column() - flags: string; // the application's public flags -} - -export interface ApplicationCommand { - id: string; - application_id: string; - name: string; - description: string; - options?: ApplicationCommandOption[]; -} - -export interface ApplicationCommandOption { - type: ApplicationCommandOptionType; - name: string; - description: string; - required?: boolean; - choices?: ApplicationCommandOptionChoice[]; - options?: ApplicationCommandOption[]; -} - -export interface ApplicationCommandOptionChoice { - name: string; - value: string | number; -} - -export enum ApplicationCommandOptionType { - SUB_COMMAND = 1, - SUB_COMMAND_GROUP = 2, - STRING = 3, - INTEGER = 4, - BOOLEAN = 5, - USER = 6, - CHANNEL = 7, - ROLE = 8, -} - -export interface ApplicationCommandInteractionData { - id: string; - name: string; - options?: ApplicationCommandInteractionDataOption[]; -} - -export interface ApplicationCommandInteractionDataOption { - name: string; - value?: any; - options?: ApplicationCommandInteractionDataOption[]; -} diff --git a/util/src/entities/Attachment.ts b/util/src/entities/Attachment.ts deleted file mode 100644 index 7b4b17eb..00000000 --- a/util/src/entities/Attachment.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { BeforeRemove, Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { URL } from "url"; -import { deleteFile } from "../util/cdn"; -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, { - onDelete: "CASCADE", - }) - message: import("./Message").Message; - - @BeforeRemove() - onDelete() { - return deleteFile(new URL(this.url).pathname); - } -} diff --git a/util/src/entities/AuditLog.ts b/util/src/entities/AuditLog.ts deleted file mode 100644 index b003e7ba..00000000 --- a/util/src/entities/AuditLog.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { BaseClass } from "./BaseClass"; -import { ChannelPermissionOverwrite } from "./Channel"; -import { User } from "./User"; - -export enum AuditLogEvents { - // guild level - GUILD_UPDATE = 1, - GUILD_IMPORT = 2, - GUILD_EXPORTED = 3, - GUILD_ARCHIVE = 4, - GUILD_UNARCHIVE = 5, - // join-leave - USER_JOIN = 6, - USER_LEAVE = 7, - // channels - CHANNEL_CREATE = 10, - CHANNEL_UPDATE = 11, - CHANNEL_DELETE = 12, - // permission overrides - CHANNEL_OVERWRITE_CREATE = 13, - CHANNEL_OVERWRITE_UPDATE = 14, - CHANNEL_OVERWRITE_DELETE = 15, - // kick and ban - MEMBER_KICK = 20, - MEMBER_PRUNE = 21, - MEMBER_BAN_ADD = 22, - MEMBER_BAN_REMOVE = 23, - // member updates - MEMBER_UPDATE = 24, - MEMBER_ROLE_UPDATE = 25, - MEMBER_MOVE = 26, - MEMBER_DISCONNECT = 27, - BOT_ADD = 28, - // roles - ROLE_CREATE = 30, - ROLE_UPDATE = 31, - ROLE_DELETE = 32, - ROLE_SWAP = 33, - // invites - INVITE_CREATE = 40, - INVITE_UPDATE = 41, - INVITE_DELETE = 42, - // webhooks - WEBHOOK_CREATE = 50, - WEBHOOK_UPDATE = 51, - WEBHOOK_DELETE = 52, - WEBHOOK_SWAP = 53, - // custom emojis - EMOJI_CREATE = 60, - EMOJI_UPDATE = 61, - EMOJI_DELETE = 62, - EMOJI_SWAP = 63, - // deletion - MESSAGE_CREATE = 70, // messages sent using non-primary seat of the user only - MESSAGE_EDIT = 71, // non-self edits only - MESSAGE_DELETE = 72, - MESSAGE_BULK_DELETE = 73, - // pinning - MESSAGE_PIN = 74, - MESSAGE_UNPIN = 75, - // integrations - INTEGRATION_CREATE = 80, - INTEGRATION_UPDATE = 81, - INTEGRATION_DELETE = 82, - // stage actions - STAGE_INSTANCE_CREATE = 83, - STAGE_INSTANCE_UPDATE = 84, - STAGE_INSTANCE_DELETE = 85, - // stickers - STICKER_CREATE = 90, - STICKER_UPDATE = 91, - STICKER_DELETE = 92, - STICKER_SWAP = 93, - // threads - THREAD_CREATE = 110, - THREAD_UPDATE = 111, - THREAD_DELETE = 112, - // application commands - APPLICATION_COMMAND_PERMISSION_UPDATE = 121, - // automod - POLICY_CREATE = 140, - POLICY_UPDATE = 141, - POLICY_DELETE = 142, - MESSAGE_BLOCKED_BY_POLICIES = 143, // in fosscord, blocked messages are stealth-dropped - // instance policies affecting the guild - GUILD_AFFECTED_BY_POLICIES = 216, - // message moves - IN_GUILD_MESSAGE_MOVE = 223, - CROSS_GUILD_MESSAGE_MOVE = 224, - // message routing - ROUTE_CREATE = 225, - ROUTE_UPDATE = 226, -} - -@Entity("audit_logs") -export class AuditLog extends BaseClass { - @JoinColumn({ name: "target_id" }) - @ManyToOne(() => User) - target?: User; - - @Column({ nullable: true }) - @RelationId((auditlog: AuditLog) => auditlog.user) - user_id: string; - - @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, (user: User) => user.id) - user: User; - - @Column({ type: "int" }) - action_type: AuditLogEvents; - - @Column({ type: "simple-json", nullable: true }) - options?: { - delete_member_days?: string; - members_removed?: string; - channel_id?: string; - messaged_id?: string; - count?: string; - id?: string; - type?: string; - role_name?: string; - }; - - @Column() - @Column({ type: "simple-json" }) - changes: AuditLogChange[]; - - @Column({ nullable: true }) - reason?: string; -} - -export interface AuditLogChange { - new_value?: AuditLogChangeValue; - old_value?: AuditLogChangeValue; - key: string; -} - -export interface AuditLogChangeValue { - name?: string; - description?: string; - icon_hash?: string; - splash_hash?: string; - discovery_splash_hash?: string; - banner_hash?: string; - owner_id?: string; - region?: string; - preferred_locale?: string; - afk_channel_id?: string; - afk_timeout?: number; - rules_channel_id?: string; - public_updates_channel_id?: string; - mfa_level?: number; - verification_level?: number; - explicit_content_filter?: number; - default_message_notifications?: number; - vanity_url_code?: string; - $add?: {}[]; - $remove?: {}[]; - prune_delete_days?: number; - widget_enabled?: boolean; - widget_channel_id?: string; - system_channel_id?: string; - position?: number; - topic?: string; - bitrate?: number; - permission_overwrites?: ChannelPermissionOverwrite[]; - nsfw?: boolean; - application_id?: string; - rate_limit_per_user?: number; - permissions?: string; - color?: number; - hoist?: boolean; - mentionable?: boolean; - allow?: string; - deny?: string; - code?: string; - channel_id?: string; - inviter_id?: string; - max_uses?: number; - uses?: number; - max_age?: number; - temporary?: boolean; - deaf?: boolean; - mute?: boolean; - nick?: string; - avatar_hash?: string; - id?: string; - type?: number; - enable_emoticons?: boolean; - expire_behavior?: number; - expire_grace_period?: number; - user_limit?: number; -} diff --git a/util/src/entities/Ban.ts b/util/src/entities/Ban.ts deleted file mode 100644 index 9504bd8e..00000000 --- a/util/src/entities/Ban.ts +++ /dev/null @@ -1,41 +0,0 @@ -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({ nullable: true }) - @RelationId((ban: Ban) => ban.user) - user_id: string; - - @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, { - onDelete: "CASCADE", - }) - user: User; - - @Column({ nullable: true }) - @RelationId((ban: Ban) => ban.guild) - guild_id: string; - - @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, { - onDelete: "CASCADE", - }) - guild: Guild; - - @Column({ nullable: true }) - @RelationId((ban: Ban) => ban.executor) - executor_id: string; - - @JoinColumn({ name: "executor_id" }) - @ManyToOne(() => User) - executor: User; - - @Column() - ip: string; - - @Column({ nullable: true }) - reason?: string; -} diff --git a/util/src/entities/BaseClass.ts b/util/src/entities/BaseClass.ts deleted file mode 100644 index aabca016..00000000 --- a/util/src/entities/BaseClass.ts +++ /dev/null @@ -1,75 +0,0 @@ -import "reflect-metadata"; -import { BaseEntity, EntityMetadata, FindConditions, ObjectIdColumn, PrimaryColumn } from "typeorm"; -import { Snowflake } from "../util/Snowflake"; -import "missing-native-js-functions"; - -export class BaseClassWithoutId extends BaseEntity { - constructor(props?: any) { - super(); - this.assign(props); - } - - private get construct(): any { - return this.constructor; - } - - private get metadata() { - return this.construct.getRepository().metadata as EntityMetadata; - } - - assign(props: any = {}) { - delete props.opts; - delete props.props; - - 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; - // @ts-ignore - const setter = this[`set${key.capitalize()}`]; // use setter function if it exists - - if (setter) { - setter.call(this, props[key]); - } else { - // @ts-ignore - this[key] = props[key]; - } - } - } - - toJSON(): any { - 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<T extends BaseClass>(conditions: FindConditions<T>, propertyPath: string, value: number | string) { - const repository = this.getRepository(); - return repository.increment(conditions as T, propertyPath, value); - } - - static decrement<T extends BaseClass>(conditions: FindConditions<T>, propertyPath: string, value: number | string) { - const repository = this.getRepository(); - return repository.decrement(conditions as T, propertyPath, value); - } -} - -export const PrimaryIdColumn = process.env.DATABASE?.startsWith("mongodb") ? ObjectIdColumn : PrimaryColumn; - -export class BaseClass extends BaseClassWithoutId { - @PrimaryIdColumn() - id: string; - - assign(props: any = {}) { - super.assign(props); - if (!this.id) this.id = Snowflake.generate(); - return this; - } -} diff --git a/util/src/entities/Categories.ts b/util/src/entities/Categories.ts deleted file mode 100644 index 81fbc303..00000000 --- a/util/src/entities/Categories.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { PrimaryColumn, Column, Entity} from "typeorm"; -import { BaseClassWithoutId } from "./BaseClass"; - -// TODO: categories: -// [{ -// "id": 16, -// "default": "Anime & Manga", -// "localizations": { -// "de": "Anime & Manga", -// "fr": "Anim\u00e9s et mangas", -// "ru": "\u0410\u043d\u0438\u043c\u0435 \u0438 \u043c\u0430\u043d\u0433\u0430" -// } -// }, -// "is_primary": false/true -// }] -// Also populate discord default categories - -@Entity("categories") -export class Categories extends BaseClassWithoutId { // Not using snowflake - - @PrimaryColumn() - id: number; - - @Column({ nullable: true }) - name: string; - - @Column({ type: "simple-json" }) - localizations: string; - - @Column({ nullable: true }) - is_primary: boolean; - -} \ No newline at end of file diff --git a/util/src/entities/Channel.ts b/util/src/entities/Channel.ts deleted file mode 100644 index 69c08be7..00000000 --- a/util/src/entities/Channel.ts +++ /dev/null @@ -1,380 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm"; -import { BaseClass } from "./BaseClass"; -import { Guild } from "./Guild"; -import { PublicUserProjection, User } from "./User"; -import { HTTPError } from "lambert-server"; -import { containsAll, emitEvent, getPermission, Snowflake, trimSpecial, InvisibleCharacters } from "../util"; -import { ChannelCreateEvent, ChannelRecipientRemoveEvent } from "../interfaces"; -import { Recipient } from "./Recipient"; -import { Message } from "./Message"; -import { ReadState } from "./ReadState"; -import { Invite } from "./Invite"; -import { VoiceState } from "./VoiceState"; -import { Webhook } from "./Webhook"; -import { DmChannelDTO } from "../dtos"; - -export enum ChannelType { - GUILD_TEXT = 0, // a text channel within a guild - DM = 1, // a direct message between users - GUILD_VOICE = 2, // a voice channel within a guild - GROUP_DM = 3, // a direct message between multiple users - GUILD_CATEGORY = 4, // an organizational category that contains zero or more channels - GUILD_NEWS = 5, // a channel that users can follow and crosspost into a guild or route - GUILD_STORE = 6, // a channel in which game developers can sell their things - ENCRYPTED = 7, // end-to-end encrypted channel - ENCRYPTED_THREAD = 8, // end-to-end encrypted thread channel - TRANSACTIONAL = 9, // event chain style transactional channel - GUILD_NEWS_THREAD = 10, // a temporary sub-channel within a GUILD_NEWS channel - GUILD_PUBLIC_THREAD = 11, // a temporary sub-channel within a GUILD_TEXT channel - GUILD_PRIVATE_THREAD = 12, // a temporary sub-channel within a GUILD_TEXT channel that is only viewable by those invited and those with the MANAGE_THREADS permission - GUILD_STAGE_VOICE = 13, // a voice channel for hosting events with an audience - DIRECTORY = 14, // guild directory listing channel - GUILD_FORUM = 15, // forum composed of IM threads - TICKET_TRACKER = 33, // ticket tracker, individual ticket items shall have type 12 - KANBAN = 34, // confluence like kanban board - VOICELESS_WHITEBOARD = 35, // whiteboard but without voice (whiteboard + voice is the same as stage) - CUSTOM_START = 64, // start custom channel types from here - UNHANDLED = 255 // unhandled unowned pass-through channel type -} - -@Entity("channels") -export class Channel extends BaseClass { - @Column() - created_at: Date; - - @Column({ nullable: true }) - name?: string; - - @Column({ type: "text", nullable: true }) - icon?: string | null; - - @Column({ type: "int" }) - type: ChannelType; - - @OneToMany(() => Recipient, (recipient: Recipient) => recipient.channel, { - cascade: true, - orphanedRowAction: "delete", - }) - recipients?: Recipient[]; - - @Column({ nullable: true }) - last_message_id: string; - - @Column({ nullable: true }) - @RelationId((channel: Channel) => channel.guild) - guild_id?: string; - - @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, { - onDelete: "CASCADE", - }) - guild: Guild; - - @Column({ nullable: true }) - @RelationId((channel: Channel) => channel.parent) - parent_id: string; - - @JoinColumn({ name: "parent_id" }) - @ManyToOne(() => Channel) - parent?: Channel; - - // for group DMs and owned custom channel types - @Column({ nullable: true }) - @RelationId((channel: Channel) => channel.owner) - owner_id: string; - - @JoinColumn({ name: "owner_id" }) - @ManyToOne(() => User) - owner: User; - - @Column({ nullable: true }) - last_pin_timestamp?: number; - - @Column({ nullable: true }) - default_auto_archive_duration?: number; - - @Column({ nullable: true }) - position?: number; - - @Column({ type: "simple-json", nullable: true }) - permission_overwrites?: ChannelPermissionOverwrite[]; - - @Column({ nullable: true }) - video_quality_mode?: number; - - @Column({ nullable: true }) - bitrate?: number; - - @Column({ nullable: true }) - user_limit?: number; - - @Column({ nullable: true }) - nsfw?: boolean; - - @Column({ nullable: true }) - rate_limit_per_user?: number; - - @Column({ nullable: true }) - topic?: string; - - @OneToMany(() => Invite, (invite: Invite) => invite.channel, { - cascade: true, - orphanedRowAction: "delete", - }) - invites?: Invite[]; - - @Column({ nullable: true }) - retention_policy_id?: string; - - @OneToMany(() => Message, (message: Message) => message.channel, { - cascade: true, - orphanedRowAction: "delete", - }) - messages?: Message[]; - - @OneToMany(() => VoiceState, (voice_state: VoiceState) => voice_state.channel, { - cascade: true, - orphanedRowAction: "delete", - }) - voice_states?: VoiceState[]; - - @OneToMany(() => ReadState, (read_state: ReadState) => read_state.channel, { - cascade: true, - orphanedRowAction: "delete", - }) - read_states?: ReadState[]; - - @OneToMany(() => Webhook, (webhook: Webhook) => webhook.channel, { - cascade: true, - orphanedRowAction: "delete", - }) - webhooks?: Webhook[]; - - // TODO: DM channel - static async createChannel( - channel: Partial<Channel>, - user_id: string = "0", - opts?: { - keepId?: boolean; - skipExistsCheck?: boolean; - skipPermissionCheck?: boolean; - skipEventEmit?: boolean; - skipNameChecks?: boolean; - } - ) { - if (!opts?.skipPermissionCheck) { - // Always check if user has permission first - const permissions = await getPermission(user_id, channel.guild_id); - permissions.hasThrow("MANAGE_CHANNELS"); - } - - if (!opts?.skipNameChecks) { - const guild = await Guild.findOneOrFail({ id: channel.guild_id }); - if (!guild.features.includes("ALLOW_INVALID_CHANNEL_NAMES") && channel.name) { - for (var character of InvisibleCharacters) - if (channel.name.includes(character)) - throw new HTTPError("Channel name cannot include invalid characters", 403); - - if (channel.name.match(/\-\-+/g)) - throw new HTTPError("Channel name cannot include multiple adjacent dashes.", 403) - - if (channel.name.charAt(0) === "-" || - channel.name.charAt(channel.name.length - 1) === "-") - throw new HTTPError("Channel name cannot start/end with dash.", 403) - } - - if (!guild.features.includes("ALLOW_UNNAMED_CHANNELS")) { - if (!channel.name) - throw new HTTPError("Channel name cannot be empty.", 403); - } - } - - switch (channel.type) { - case ChannelType.GUILD_TEXT: - case ChannelType.GUILD_NEWS: - 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: - case ChannelType.UNHANDLED: - break; - case ChannelType.DM: - case ChannelType.GROUP_DM: - throw new HTTPError("You can't create a dm channel in a guild"); - case ChannelType.GUILD_STORE: - default: - throw new HTTPError("Not yet supported"); - } - - if (!channel.permission_overwrites) channel.permission_overwrites = []; - // TODO: eagerly auto generate position of all guild channels - - channel = { - ...channel, - ...(!opts?.keepId && { id: Snowflake.generate() }), - created_at: new Date(), - position: (channel.type === ChannelType.UNHANDLED ? 0 : channel.position) || 0, - }; - - await Promise.all([ - new Channel(channel).save(), - !opts?.skipEventEmit - ? emitEvent({ - event: "CHANNEL_CREATE", - data: channel, - guild_id: channel.guild_id, - } as ChannelCreateEvent) - : Promise.resolve(), - ]); - - return channel; - } - - static async createDMChannel(recipients: string[], creator_user_id: string, name?: string) { - recipients = recipients.unique().filter((x) => x !== creator_user_id); - const otherRecipientsUsers = await User.find({ where: recipients.map((x) => ({ id: x })) }); - - // TODO: check config for max number of recipients - /** if you want to disallow note to self channels, uncomment the conditional below - if (otherRecipientsUsers.length !== recipients.length) { - throw new HTTPError("Recipient/s not found"); - } - **/ - - const type = recipients.length > 1 ? ChannelType.DM : ChannelType.GROUP_DM; - - let channel = null; - - const channelRecipients = [...recipients, creator_user_id]; - - const userRecipients = await Recipient.find({ - where: { user_id: creator_user_id }, - relations: ["channel", "channel.recipients"], - }); - - for (let ur of userRecipients) { - let re = ur.channel.recipients!.map((r) => r.user_id); - if (re.length === channelRecipients.length) { - if (containsAll(re, channelRecipients)) { - if (channel == null) { - channel = ur.channel; - await ur.assign({ closed: false }).save(); - } - } - } - } - - if (channel == null) { - name = trimSpecial(name); - - channel = await new Channel({ - name, - type, - owner_id: type === ChannelType.DM ? undefined : null, // 1:1 DMs are ownerless in fosscord-server - created_at: new Date(), - last_message_id: null, - recipients: channelRecipients.map( - (x) => - new Recipient({ user_id: x, closed: !(type === ChannelType.GROUP_DM || x === creator_user_id) }) - ), - }).save(); - } - - const channel_dto = await DmChannelDTO.from(channel); - - if (type === ChannelType.GROUP_DM) { - for (let recipient of channel.recipients!) { - await emitEvent({ - event: "CHANNEL_CREATE", - data: channel_dto.excludedRecipients([recipient.user_id]), - user_id: recipient.user_id, - }); - } - } else { - await emitEvent({ event: "CHANNEL_CREATE", data: channel_dto, user_id: creator_user_id }); - } - - if (recipients.length === 1) return channel_dto; - else return channel_dto.excludedRecipients([creator_user_id]); - } - - static async removeRecipientFromChannel(channel: Channel, user_id: string) { - await Recipient.delete({ channel_id: channel.id, user_id: user_id }); - channel.recipients = channel.recipients?.filter((r) => r.user_id !== user_id); - - if (channel.recipients?.length === 0) { - await Channel.deleteChannel(channel); - await emitEvent({ - event: "CHANNEL_DELETE", - data: await DmChannelDTO.from(channel, [user_id]), - user_id: user_id, - }); - return; - } - - await emitEvent({ - event: "CHANNEL_DELETE", - data: await DmChannelDTO.from(channel, [user_id]), - user_id: user_id, - }); - - //If the owner leave the server user is the new owner - if (channel.owner_id === user_id) { - channel.owner_id = "1"; // The channel is now owned by the server user - await emitEvent({ - event: "CHANNEL_UPDATE", - data: await DmChannelDTO.from(channel, [user_id]), - channel_id: channel.id, - }); - } - - await channel.save(); - - await emitEvent({ - event: "CHANNEL_RECIPIENT_REMOVE", - data: { - channel_id: channel.id, - user: await User.findOneOrFail({ where: { id: user_id }, select: PublicUserProjection }), - }, - channel_id: channel.id, - } as ChannelRecipientRemoveEvent); - } - - static async deleteChannel(channel: Channel) { - await Message.delete({ channel_id: channel.id }); //TODO we should also delete the attachments from the cdn but to do that we need to move cdn.ts in util - //TODO before deleting the channel we should check and delete other relations - await Channel.delete({ id: channel.id }); - } - - isDm() { - return this.type === ChannelType.DM || this.type === ChannelType.GROUP_DM; - } - - // Does the channel support sending messages ( eg categories do not ) - isWritable() { - const disallowedChannelTypes = [ - ChannelType.GUILD_CATEGORY, - ChannelType.GUILD_STAGE_VOICE, - ChannelType.VOICELESS_WHITEBOARD, - ]; - return disallowedChannelTypes.indexOf(this.type) == -1; - } -} - -export interface ChannelPermissionOverwrite { - allow: string; - deny: string; - id: string; - type: ChannelPermissionOverwriteType; -} - -export enum ChannelPermissionOverwriteType { - role = 0, - member = 1, - group = 2, -} diff --git a/util/src/entities/ClientRelease.ts b/util/src/entities/ClientRelease.ts deleted file mode 100644 index c5afd307..00000000 --- a/util/src/entities/ClientRelease.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Column, Entity} from "typeorm"; -import { BaseClass } from "./BaseClass"; - -@Entity("client_release") -export class Release extends BaseClass { - @Column() - name: string; - - @Column() - pub_date: string; - - @Column() - url: string; - - @Column() - deb_url: string; - - @Column() - osx_url: string; - - @Column() - win_url: string; - - @Column({ nullable: true }) - notes?: string; -} diff --git a/util/src/entities/Config.ts b/util/src/entities/Config.ts deleted file mode 100644 index 3756d686..00000000 --- a/util/src/entities/Config.ts +++ /dev/null @@ -1,407 +0,0 @@ -import { Column, Entity } from "typeorm"; -import { BaseClassWithoutId, PrimaryIdColumn } from "./BaseClass"; -import crypto from "crypto"; -import { Snowflake } from "../util/Snowflake"; -import { SessionsReplace } from ".."; -import { hostname } from "os"; - -@Entity("config") -export class ConfigEntity extends BaseClassWithoutId { - @PrimaryIdColumn() - key: string; - - @Column({ type: "simple-json", nullable: true }) - value: number | boolean | null | string | undefined; -} - -export interface RateLimitOptions { - bot?: number; - count: number; - window: number; - onyIp?: boolean; -} - -export interface Region { - id: string; - name: string; - endpoint: string; - location?: { - latitude: number; - longitude: number; - }; - vip: boolean; - custom: boolean; - deprecated: boolean; -} - -export interface KafkaBroker { - ip: string; - port: number; -} - -export interface ConfigValue { - gateway: { - endpointClient: string | null; - endpointPrivate: string | null; - endpointPublic: string | null; - }; - cdn: { - endpointClient: string | null; - endpointPublic: string | null; - endpointPrivate: string | null; - }; - api: { - defaultVersion: string; - activeVersions: string[]; - useFosscordEnhancements: boolean; - }; - general: { - instanceName: string; - instanceDescription: string | null; - frontPage: string | null; - tosPage: string | null; - correspondenceEmail: string | null; - correspondenceUserID: string | null; - image: string | null; - instanceId: string; - }; - limits: { - user: { - maxGuilds: number; - maxUsername: number; - maxFriends: number; - }; - guild: { - maxRoles: number; - maxEmojis: number; - maxMembers: number; - maxChannels: number; - maxChannelsInCategory: number; - hideOfflineMember: number; - }; - message: { - maxCharacters: number; - maxTTSCharacters: number; - maxReactions: number; - maxAttachmentSize: number; - maxBulkDelete: number; - maxEmbedDownloadSize: number; - }; - channel: { - maxPins: number; - maxTopic: number; - maxWebhooks: number; - }; - rate: { - disabled: boolean; - ip: Omit<RateLimitOptions, "bot_count">; - 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: { - required: boolean; - allowlist: boolean; - blocklist: boolean; - domains: string[]; - }; - dateOfBirth: { - required: boolean; - minimum: number; // in years - }; - disabled: boolean; - requireCaptcha: boolean; - requireInvite: boolean; - guestsRequireInvite: boolean; - allowNewRegistration: boolean; - allowMultipleAccounts: boolean; - blockProxies: boolean; - password: { - required: boolean; - minLength: number; - minNumbers: number; - minUpperCase: number; - minSymbols: number; - }; - incrementingDiscriminators: boolean; // random otherwise - }; - regions: { - default: string; - useDefaultAsOptimal: boolean; - available: Region[]; - }; - guild: { - discovery: { - showAllGuilds: boolean; - useRecommendation: boolean; // TODO: Recommendation, privacy concern? - offset: number; - limit: number; - }; - autoJoin: { - enabled: boolean; - guilds: string[]; - canLeave: boolean; - }; - }; - gif: { - enabled: boolean; - provider: "tenor"; // more coming soon - apiKey?: string; - }; - rabbitmq: { - host: string | null; - }; - kafka: { - brokers: KafkaBroker[] | null; - }; - templates: { - enabled: Boolean; - allowTemplateCreation: Boolean; - allowDiscordTemplates: Boolean; - allowRaws: Boolean; - }, - client: { - useTestClient: Boolean; - releases: { - useLocalRelease: Boolean; //TODO - upstreamVersion: string; - } - }, - metrics: { - timeout: number; - }, - sentry: { - enabled: boolean; - endpoint: string; - traceSampleRate: number; - environment: string; - } -} - -export const DefaultConfigOptions: ConfigValue = { - gateway: { - endpointClient: null, - endpointPrivate: null, - endpointPublic: null, - }, - cdn: { - endpointClient: null, - endpointPrivate: null, - endpointPublic: null, - }, - api: { - defaultVersion: "9", - activeVersions: ["6", "7", "8", "9"], - useFosscordEnhancements: true, - }, - general: { - instanceName: "Fosscord Instance", - instanceDescription: "This is a Fosscord instance made in pre-release days", - frontPage: null, - tosPage: null, - correspondenceEmail: "noreply@localhost.local", - correspondenceUserID: null, - image: null, - instanceId: Snowflake.generate(), - }, - limits: { - user: { - maxGuilds: 1048576, - maxUsername: 127, - maxFriends: 5000, - }, - guild: { - maxRoles: 1000, - maxEmojis: 2000, - maxMembers: 25000000, - maxChannels: 65535, - maxChannelsInCategory: 65535, - hideOfflineMember: 3, - }, - message: { - maxCharacters: 1048576, - maxTTSCharacters: 160, - maxReactions: 2048, - maxAttachmentSize: 1024 * 1024 * 1024, - maxEmbedDownloadSize: 1024 * 1024 * 5, - maxBulkDelete: 1000, - }, - channel: { - maxPins: 500, - maxTopic: 1024, - maxWebhooks: 100, - }, - rate: { - disabled: true, - ip: { - count: 500, - window: 5, - }, - global: { - count: 250, - window: 5, - }, - error: { - count: 10, - window: 5, - }, - routes: { - guild: { - count: 5, - window: 5, - }, - webhook: { - count: 10, - window: 5, - }, - channel: { - count: 10, - window: 5, - }, - 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: { - required: false, - allowlist: false, - blocklist: true, - domains: [], // TODO: efficiently save domain blocklist in database - // domains: fs.readFileSync(__dirname + "/blockedEmailDomains.txt", { encoding: "utf8" }).split("\n"), - }, - dateOfBirth: { - required: true, - minimum: 13, - }, - disabled: false, - requireInvite: false, - guestsRequireInvite: true, - requireCaptcha: true, - allowNewRegistration: true, - allowMultipleAccounts: true, - blockProxies: true, - password: { - required: false, - minLength: 8, - minNumbers: 2, - minUpperCase: 2, - minSymbols: 0, - }, - incrementingDiscriminators: false, - }, - regions: { - default: "fosscord", - useDefaultAsOptimal: true, - available: [ - { - id: "fosscord", - name: "Fosscord", - endpoint: "127.0.0.1:3004", - vip: false, - custom: false, - deprecated: false, - }, - ], - }, - guild: { - discovery: { - showAllGuilds: false, - useRecommendation: false, - offset: 0, - limit: 24, - }, - autoJoin: { - enabled: true, - canLeave: true, - guilds: [], - }, - }, - gif: { - enabled: true, - provider: "tenor", - apiKey: "LIVDSRZULELA", - }, - rabbitmq: { - host: null, - }, - kafka: { - brokers: null, - }, - templates: { - enabled: true, - allowTemplateCreation: true, - allowDiscordTemplates: true, - allowRaws: false - }, - client: { - useTestClient: true, - releases: { - useLocalRelease: true, - upstreamVersion: "0.0.264" - } - }, - metrics: { - timeout: 30000 - }, - sentry: { - enabled: false, - endpoint: "https://05e8e3d005f34b7d97e920ae5870a5e5@sentry.thearcanebrony.net/6", - traceSampleRate: 1.0, - environment: hostname() - } -}; diff --git a/util/src/entities/ConnectedAccount.ts b/util/src/entities/ConnectedAccount.ts deleted file mode 100644 index 09ae30ab..00000000 --- a/util/src/entities/ConnectedAccount.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { BaseClass } from "./BaseClass"; -import { User } from "./User"; - -export interface PublicConnectedAccount extends Pick<ConnectedAccount, "name" | "type" | "verified"> {} - -@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, { - onDelete: "CASCADE", - }) - user: User; - - @Column({ select: false }) - access_token: string; - - @Column({ select: false }) - friend_sync: boolean; - - @Column() - name: string; - - @Column({ select: false }) - revoked: boolean; - - @Column({ select: false }) - show_activity: boolean; - - @Column() - type: string; - - @Column() - verified: boolean; - - @Column({ select: false }) - visibility: number; -} diff --git a/util/src/entities/Emoji.ts b/util/src/entities/Emoji.ts deleted file mode 100644 index a3615b7d..00000000 --- a/util/src/entities/Emoji.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { User } from "."; -import { BaseClass } from "./BaseClass"; -import { Guild } from "./Guild"; -import { Role } from "./Role"; - -@Entity("emojis") -export class Emoji extends BaseClass { - @Column() - animated: boolean; - - @Column() - available: boolean; // whether this emoji can be used, may be false due to various reasons - - @Column() - guild_id: string; - - @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, { - onDelete: "CASCADE", - }) - guild: Guild; - - @Column({ nullable: true }) - @RelationId((emoji: Emoji) => emoji.user) - user_id: string; - - @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User) - user: User; - - @Column() - managed: boolean; - - @Column() - name: string; - - @Column() - require_colons: boolean; - - @Column({ type: "simple-array" }) - roles: string[]; // roles this emoji is whitelisted to (new discord feature?) - - @Column({ type: "simple-array", nullable: true }) - groups: string[]; // user groups this emoji is whitelisted to (Fosscord extension) -} diff --git a/util/src/entities/Encryption.ts b/util/src/entities/Encryption.ts deleted file mode 100644 index 3b82ff84..00000000 --- a/util/src/entities/Encryption.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm"; -import { BaseClass } from "./BaseClass"; -import { Guild } from "./Guild"; -import { PublicUserProjection, User } from "./User"; -import { HTTPError } from "lambert-server"; -import { containsAll, emitEvent, getPermission, Snowflake, trimSpecial, InvisibleCharacters } from "../util"; -import { BitField, BitFieldResolvable, BitFlag } from "../util/BitField"; -import { Recipient } from "./Recipient"; -import { Message } from "./Message"; -import { ReadState } from "./ReadState"; -import { Invite } from "./Invite"; -import { DmChannelDTO } from "../dtos"; - -@Entity("security_settings") -export class SecuritySettings extends BaseClass { - - @Column({nullable: true}) - guild_id: Snowflake; - - @Column({nullable: true}) - channel_id: Snowflake; - - @Column() - encryption_permission_mask: BitField; - - @Column() - allowed_algorithms: string[]; - - @Column() - current_algorithm: string; - - @Column({nullable: true}) - used_since_message: Snowflake; - -} diff --git a/util/src/entities/Group.ts b/util/src/entities/Group.ts deleted file mode 100644 index b24d38cf..00000000 --- a/util/src/entities/Group.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; - -import { BaseClass } from "./BaseClass"; - -@Entity("groups") -export class UserGroup extends BaseClass { - @Column({ nullable: true }) - parent?: BigInt; - - @Column() - color: number; - - @Column() - hoist: boolean; - - @Column() - mentionable: boolean; - - @Column() - name: string; - - @Column() - rights: BigInt; - - @Column() - position: number; - - @Column({ nullable: true }) - icon: BigInt; - - @Column({ nullable: true }) - unicode_emoji: BigInt; -} diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts deleted file mode 100644 index 70bb41c5..00000000 --- a/util/src/entities/Guild.ts +++ /dev/null @@ -1,363 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToMany, OneToOne, RelationId } from "typeorm"; -import { Config, handleFile, Snowflake } from ".."; -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 -// TODO: -// "keywords": [ -// "Genshin Impact", -// "Paimon", -// "Honkai Impact", -// "ARPG", -// "Open-World", -// "Waifu", -// "Anime", -// "Genshin", -// "miHoYo", -// "Gacha" -// ], - -export const PublicGuildRelations = [ - "channels", - "emojis", - "members", - "roles", - "stickers", - "voice_states", - "members.user", -]; - -@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) - afk_channel?: Channel; - - @Column({ nullable: true }) - afk_timeout?: number; - - // * commented out -> use owner instead - // application id of the guild creator if it is bot-created - // @Column({ nullable: true }) - // application?: string; - - @JoinColumn({ name: "ban_ids" }) - @OneToMany(() => Ban, (ban: Ban) => ban.guild, { - cascade: true, - orphanedRowAction: "delete", - }) - bans: Ban[]; - - @Column({ nullable: true }) - banner?: string; - - @Column({ nullable: true }) - default_message_notifications?: number; - - @Column({ nullable: true }) - description?: string; - - @Column({ nullable: true }) - discovery_splash?: string; - - @Column({ nullable: true }) - explicit_content_filter?: number; - - @Column({ type: "simple-array" }) - features: string[]; //TODO use enum - //TODO: https://discord.com/developers/docs/resources/guild#guild-object-guild-features - - @Column({ nullable: true }) - primary_category_id: number; - - @Column({ nullable: true }) - icon?: string; - - @Column({ nullable: true }) - large?: boolean; - - @Column({ nullable: true }) - max_members?: number; // e.g. default 100.000 - - @Column({ nullable: true }) - max_presences?: number; - - @Column({ nullable: true }) - max_video_channel_users?: number; // ? default: 25, is this max 25 streaming or watching - - @Column({ nullable: true }) - member_count?: number; - - @Column({ nullable: true }) - presence_count?: number; // users online - - @OneToMany(() => Member, (member: Member) => member.guild, { - cascade: true, - orphanedRowAction: "delete", - onDelete: "CASCADE", - }) - members: Member[]; - - @JoinColumn({ name: "role_ids" }) - @OneToMany(() => Role, (role: Role) => role.guild, { - cascade: true, - orphanedRowAction: "delete", - onDelete: "CASCADE", - }) - roles: Role[]; - - @JoinColumn({ name: "channel_ids" }) - @OneToMany(() => Channel, (channel: Channel) => channel.guild, { - cascade: true, - orphanedRowAction: "delete", - }) - channels: Channel[]; - - @Column({ nullable: true }) - @RelationId((guild: Guild) => guild.template) - template_id?: string; - - @JoinColumn({ name: "template_id", referencedColumnName: "id" }) - @ManyToOne(() => Template) - template: Template; - - @JoinColumn({ name: "emoji_ids" }) - @OneToMany(() => Emoji, (emoji: Emoji) => emoji.guild, { - cascade: true, - orphanedRowAction: "delete", - onDelete: "CASCADE", - }) - emojis: Emoji[]; - - @JoinColumn({ name: "sticker_ids" }) - @OneToMany(() => Sticker, (sticker: Sticker) => sticker.guild, { - cascade: true, - orphanedRowAction: "delete", - onDelete: "CASCADE", - }) - stickers: Sticker[]; - - @JoinColumn({ name: "invite_ids" }) - @OneToMany(() => Invite, (invite: Invite) => invite.guild, { - cascade: true, - orphanedRowAction: "delete", - onDelete: "CASCADE", - }) - invites: Invite[]; - - @JoinColumn({ name: "voice_state_ids" }) - @OneToMany(() => VoiceState, (voicestate: VoiceState) => voicestate.guild, { - cascade: true, - orphanedRowAction: "delete", - onDelete: "CASCADE", - }) - voice_states: VoiceState[]; - - @JoinColumn({ name: "webhook_ids" }) - @OneToMany(() => Webhook, (webhook: Webhook) => webhook.guild, { - cascade: true, - orphanedRowAction: "delete", - onDelete: "CASCADE", - }) - webhooks: Webhook[]; - - @Column({ nullable: true }) - mfa_level?: number; - - @Column() - name: string; - - @Column({ nullable: true }) - @RelationId((guild: Guild) => guild.owner) - owner_id?: string; // optional to allow for ownerless guilds - - @JoinColumn({ name: "owner_id", referencedColumnName: "id" }) - @ManyToOne(() => User) - owner?: User; // optional to allow for ownerless guilds - - @Column({ nullable: true }) - preferred_locale?: string; - - @Column({ nullable: true }) - premium_subscription_count?: number; - - @Column({ nullable: true }) - premium_tier?: number; // crowd premium level - - @Column({ nullable: true }) - @RelationId((guild: Guild) => guild.public_updates_channel) - public_updates_channel_id: string; - - @JoinColumn({ name: "public_updates_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" }) - @ManyToOne(() => Channel) - rules_channel?: string; - - @Column({ nullable: true }) - region?: string; - - @Column({ nullable: true }) - splash?: string; - - @Column({ nullable: true }) - @RelationId((guild: Guild) => guild.system_channel) - system_channel_id?: string; - - @JoinColumn({ name: "system_channel_id" }) - @ManyToOne(() => Channel) - system_channel?: Channel; - - @Column({ nullable: true }) - system_channel_flags?: number; - - @Column({ nullable: true }) - unavailable?: boolean; - - @Column({ nullable: true }) - verification_level?: number; - - @Column({ type: "simple-json" }) - welcome_screen: { - enabled: boolean; - description: string; - welcome_channels: { - description: string; - emoji_id?: string; - emoji_name: string; - channel_id: string; - }[]; - }; - - @Column({ nullable: true }) - @RelationId((guild: Guild) => guild.widget_channel) - widget_channel_id?: string; - - @JoinColumn({ name: "widget_channel_id" }) - @ManyToOne(() => Channel) - widget_channel?: Channel; - - @Column({ nullable: true }) - widget_enabled?: boolean; - - @Column({ nullable: true }) - nsfw_level?: number; - - @Column({ nullable: true }) - nsfw?: boolean; - - // TODO: nested guilds - @Column({ nullable: true }) - parent?: string; - - // only for developer portal - permissions?: number; - - static async createGuild(body: { - name?: string; - icon?: string | null; - owner_id?: string; - channels?: Partial<Channel>[]; - }) { - const guild_id = Snowflake.generate(); - - const guild = await new Guild({ - name: body.name || "Fosscord", - icon: await handleFile(`/icons/${guild_id}`, body.icon as string), - region: Config.get().regions.default, - owner_id: body.owner_id, // TODO: need to figure out a way for ownerless guilds and multiply-owned guilds - afk_timeout: 300, - default_message_notifications: 1, // defaults effect: setting the push default at mentions-only will save a lot - explicit_content_filter: 0, - features: [], - primary_category_id: null, - id: guild_id, - max_members: 250000, - max_presences: 250000, - max_video_channel_users: 200, - 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: 4, // defaults effect: suppress the setup tips to save performance - unavailable: false, - nsfw: false, - nsfw_level: 0, - verification_level: 0, - welcome_screen: { - enabled: false, - description: "Fill in your description", - welcome_channels: [], - }, - widget_enabled: true, // NB: don't set it as false to prevent artificial restrictions - }).save(); - - // we have to create the role _after_ the guild because else we would get a "SQLITE_CONSTRAINT: FOREIGN KEY constraint failed" error - // TODO: make the @everyone a pseudorole that is dynamically generated at runtime so we can save storage - await new Role({ - id: guild_id, - guild_id: guild_id, - color: 0, - hoist: false, - managed: false, - // NB: in Fosscord, every role will be non-managed, as we use user-groups instead of roles for managed groups - mentionable: false, - name: "@everyone", - permissions: String("2251804225"), - position: 0, - icon: null, - unicode_emoji: null - }).save(); - - if (!body.channels || !body.channels.length) body.channels = [{ id: "01", type: 0, name: "general" }]; - - const ids = new Map(); - - body.channels.forEach((x) => { - if (x.id) { - ids.set(x.id, Snowflake.generate()); - } - }); - - for (const channel of body.channels?.sort((a, b) => (a.parent_id ? 1 : -1))) { - var id = ids.get(channel.id) || Snowflake.generate(); - - var parent_id = ids.get(channel.parent_id); - - await Channel.createChannel({ ...channel, guild_id, id, parent_id }, body.owner_id, { - keepId: true, - skipExistsCheck: true, - skipPermissionCheck: true, - skipEventEmit: true, - }); - } - - return guild; - } -} diff --git a/util/src/entities/Invite.ts b/util/src/entities/Invite.ts deleted file mode 100644 index 6ac64ddc..00000000 --- a/util/src/entities/Invite.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToOne, RelationId, PrimaryColumn } from "typeorm"; -import { Member } from "./Member"; -import { BaseClassWithoutId } from "./BaseClass"; -import { Channel } from "./Channel"; -import { Guild } from "./Guild"; -import { User } from "./User"; - -export const PublicInviteRelation = ["inviter", "guild", "channel"]; - -@Entity("invites") -export class Invite extends BaseClassWithoutId { - @PrimaryColumn() - code: string; - - @Column() - temporary: boolean; - - @Column() - uses: number; - - @Column() - max_uses: number; - - @Column() - max_age: number; - - @Column() - created_at: Date; - - @Column() - expires_at: Date; - - @Column({ nullable: true }) - @RelationId((invite: Invite) => invite.guild) - guild_id: string; - - @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, { - onDelete: "CASCADE", - }) - guild: Guild; - - @Column({ nullable: true }) - @RelationId((invite: Invite) => invite.channel) - channel_id: string; - - @JoinColumn({ name: "channel_id" }) - @ManyToOne(() => Channel, { - onDelete: "CASCADE", - }) - channel: Channel; - - @Column({ nullable: true }) - @RelationId((invite: Invite) => invite.inviter) - inviter_id: string; - - @JoinColumn({ name: "inviter_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, { - onDelete: "CASCADE", - }) - target_user?: string; // could be used for "User specific invites" https://github.com/fosscord/fosscord/issues/62 - - @Column({ nullable: true }) - target_user_type?: number; - - @Column({ nullable: true }) - vanity_url?: boolean; - - static async joinGuild(user_id: string, code: string) { - const invite = await Invite.findOneOrFail({ code }); - if (invite.uses++ >= invite.max_uses && invite.max_uses !== 0) await Invite.delete({ code }); - else await invite.save(); - - await Member.addToGuild(user_id, invite.guild_id); - return invite; - } -} diff --git a/util/src/entities/Member.ts b/util/src/entities/Member.ts deleted file mode 100644 index fe2d5590..00000000 --- a/util/src/entities/Member.ts +++ /dev/null @@ -1,356 +0,0 @@ -import { PublicUser, User } from "./User"; -import { BaseClass } from "./BaseClass"; -import { - Column, - Entity, - Index, - JoinColumn, - JoinTable, - ManyToMany, - ManyToOne, - PrimaryGeneratedColumn, - 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"; -import { BaseClassWithoutId } from "./BaseClass"; -import { Ban, PublicGuildRelations } from "."; -import { DiscordApiErrors } from "../util/Constants"; - -export const MemberPrivateProjection: (keyof Member)[] = [ - "id", - "guild", - "guild_id", - "deaf", - "joined_at", - "last_message_id", - "mute", - "nick", - "pending", - "premium_since", - "roles", - "settings", - "user", -]; - -@Entity("members") -@Index(["id", "guild_id"], { unique: true }) -export class Member extends BaseClassWithoutId { - @PrimaryGeneratedColumn() - index: string; - - @Column() - @RelationId((member: Member) => member.user) - id: string; - - @JoinColumn({ name: "id" }) - @ManyToOne(() => User, { - onDelete: "CASCADE", - }) - user: User; - - @Column() - @RelationId((member: Member) => member.guild) - guild_id: string; - - @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, { - onDelete: "CASCADE", - }) - guild: Guild; - - @Column({ nullable: true }) - nick?: string; - - @JoinTable({ - name: "member_roles", - joinColumn: { name: "index", referencedColumnName: "index" }, - inverseJoinColumn: { - name: "role_id", - referencedColumnName: "id", - }, - }) - @ManyToMany(() => Role, { cascade: true }) - roles: Role[]; - - @Column() - joined_at: Date; - - @Column({ type: "bigint", nullable: true }) - premium_since?: number; - - @Column() - deaf: boolean; - - @Column() - mute: boolean; - - @Column() - pending: boolean; - - @Column({ type: "simple-json", select: false }) - settings: UserGuildSettings; - - @Column({ nullable: true }) - last_message_id?: string; - - /** - @JoinColumn({ name: "id" }) - @ManyToOne(() => User, { - onDelete: "DO NOTHING", - // do not auto-kick force-joined members just because their joiners left the server - }) **/ - @Column({ nullable: true}) - joined_by?: string; - - // TODO: add this when we have proper read receipts - // @Column({ type: "simple-json" }) - // read_state: ReadState; - - static async IsInGuildOrFail(user_id: string, guild_id: string) { - if (await Member.count({ id: user_id, guild: { 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.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, user: member.user }, - guild_id, - } as GuildMemberRemoveEvent), - ]); - } - - static async addRole(user_id: string, guild_id: string, role_id: string) { - const [member, role] = await Promise.all([ - // @ts-ignore - Member.findOneOrFail({ - where: { id: user_id, guild_id }, - relations: ["user", "roles"], // we don't want to load the role objects just the ids - select: ["index", "roles.id"], - }), - Role.findOneOrFail({ where: { id: role_id, guild_id }, select: ["id"] }), - ]); - member.roles.push(new Role({ id: role_id })); - - await Promise.all([ - member.save(), - emitEvent({ - event: "GUILD_MEMBER_UPDATE", - data: { - guild_id, - user: member.user, - roles: member.roles.map((x) => x.id), - }, - guild_id, - } 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 }, - relations: ["user", "roles"], // we don't want to load the role objects just the ids - select: ["roles.id", "index"], - }), - await Role.findOneOrFail({ id: role_id, guild_id }), - ]); - member.roles = member.roles.filter((x) => x.id == role_id); - - await Promise.all([ - member.save(), - emitEvent({ - event: "GUILD_MEMBER_UPDATE", - data: { - guild_id, - user: member.user, - roles: member.roles.map((x) => x.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, - }, - relations: ["user"], - }); - member.nick = nickname; - - await Promise.all([ - member.save(), - - emitEvent({ - event: "GUILD_MEMBER_UPDATE", - data: { - guild_id, - user: member.user, - nick: nickname, - }, - guild_id, - } as GuildMemberUpdateEvent), - ]); - } - - static async addToGuild(user_id: string, guild_id: string) { - const user = await User.getPublicUser(user_id); - const isBanned = await Ban.count({ where: { guild_id, user_id } }); - if (isBanned) { - throw DiscordApiErrors.USER_BANNED; - } - 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({ - where: { - id: guild_id, - }, - relations: PublicGuildRelations, - }); - - 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 = { - id: user_id, - guild_id, - nick: undefined, - roles: [guild_id], // @everyone role - joined_at: new Date(), - premium_since: (new Date()).getTime(), - deaf: false, - mute: false, - pending: false, - }; - - await Promise.all([ - new Member({ - ...member, - roles: [new Role({ id: guild_id })], - // read_state: {}, - settings: { - channel_overrides: [], - message_notifications: 0, - mobile_push: true, - muted: false, - suppress_everyone: false, - suppress_roles: false, - version: 0, - }, - // Member.save is needed because else the roles relations wouldn't be updated - }).save(), - Guild.increment({ id: guild_id }, "member_count", 1), - emitEvent({ - event: "GUILD_MEMBER_ADD", - data: { - ...member, - user, - guild_id, - }, - guild_id, - } as GuildMemberAddEvent), - emitEvent({ - event: "GUILD_CREATE", - data: { - ...guild, - members: [...guild.members, { ...member, user }], - member_count: (guild.member_count || 0) + 1, - guild_hashes: {}, - guild_scheduled_events: [], - joined_at: member.joined_at, - presences: [], - stage_instances: [], - threads: [], - }, - user_id, - } as GuildCreateEvent), - ]); - } -} - -export interface UserGuildSettings { - channel_overrides: { - channel_id: string; - message_notifications: number; - mute_config: MuteConfig; - muted: boolean; - }[]; - message_notifications: number; - mobile_push: boolean; - mute_config: MuteConfig; - muted: boolean; - suppress_everyone: boolean; - suppress_roles: boolean; - version: number; -} - -export interface MuteConfig { - end_time: number; - 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 type PublicMember = Pick<Member, Omit<PublicMemberKeys, "roles">> & { - user: PublicUser; - roles: string[]; // only role ids not objects -}; diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts deleted file mode 100644 index e18cf691..00000000 --- a/util/src/entities/Message.ts +++ /dev/null @@ -1,285 +0,0 @@ -import { User } from "./User"; -import { Member } from "./Member"; -import { Role } from "./Role"; -import { Channel } from "./Channel"; -import { InteractionType } from "../interfaces/Interaction"; -import { Application } from "./Application"; -import { - Column, - CreateDateColumn, - Entity, - FindConditions, - Index, - JoinColumn, - JoinTable, - ManyToMany, - ManyToOne, - OneToMany, - RelationId, - RemoveOptions, - UpdateDateColumn, -} from "typeorm"; -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, - RECIPIENT_ADD = 1, - RECIPIENT_REMOVE = 2, - CALL = 3, - CHANNEL_NAME_CHANGE = 4, - CHANNEL_ICON_CHANGE = 5, - CHANNEL_PINNED_MESSAGE = 6, - GUILD_MEMBER_JOIN = 7, - USER_PREMIUM_GUILD_SUBSCRIPTION = 8, - USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1 = 9, - USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2 = 10, - USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3 = 11, - CHANNEL_FOLLOW_ADD = 12, - ACTION = 13, // /me messages - GUILD_DISCOVERY_DISQUALIFIED = 14, - GUILD_DISCOVERY_REQUALIFIED = 15, - ENCRYPTED = 16, - REPLY = 19, - APPLICATION_COMMAND = 20, // application command or self command invocation - ROUTE_ADDED = 41, // custom message routing: new route affecting that channel - ROUTE_DISABLED = 42, // custom message routing: given route no longer affecting that channel - SELF_COMMAND_SCRIPT = 43, // self command scripts - ENCRYPTION = 50, - CUSTOM_START = 63, - UNHANDLED = 255 -} - -@Entity("messages") -@Index(["channel_id", "id"], { unique: true }) -export class Message extends BaseClass { - @Column({ nullable: true }) - @RelationId((message: Message) => message.channel) - @Index() - channel_id: string; - - @JoinColumn({ name: "channel_id" }) - @ManyToOne(() => Channel, { - onDelete: "CASCADE", - }) - channel: Channel; - - @Column({ nullable: true }) - @RelationId((message: Message) => message.guild) - guild_id?: string; - - @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, { - onDelete: "CASCADE", - }) - guild?: Guild; - - @Column({ nullable: true }) - @RelationId((message: Message) => message.author) - @Index() - author_id: string; - - @JoinColumn({ name: "author_id", referencedColumnName: "id" }) - @ManyToOne(() => User, { - onDelete: "CASCADE", - }) - author?: User; - - @Column({ nullable: true }) - @RelationId((message: Message) => message.member) - member_id: string; - - @JoinColumn({ name: "member_id", referencedColumnName: "id" }) - @ManyToOne(() => User, { - onDelete: "CASCADE", - }) - member?: Member; - - @Column({ nullable: true }) - @RelationId((message: Message) => message.webhook) - webhook_id: string; - - @JoinColumn({ name: "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; - - @Column({ nullable: true }) - content?: string; - - @Column() - @CreateDateColumn() - timestamp: Date; - - @Column({ nullable: true }) - edited_timestamp?: Date; - - @Column({ nullable: true }) - tts?: boolean; - - @Column({ nullable: true }) - mention_everyone?: boolean; - - @JoinTable({ name: "message_user_mentions" }) - @ManyToMany(() => User) - mentions: User[]; - - @JoinTable({ name: "message_role_mentions" }) - @ManyToMany(() => Role) - mention_roles: Role[]; - - @JoinTable({ name: "message_channel_mentions" }) - @ManyToMany(() => Channel) - mention_channels: Channel[]; - - @JoinTable({ name: "message_stickers" }) - @ManyToMany(() => Sticker, { cascade: true, onDelete: "CASCADE" }) - sticker_items?: Sticker[]; - - @OneToMany(() => Attachment, (attachment: Attachment) => attachment.message, { - cascade: true, - orphanedRowAction: "delete", - }) - attachments?: Attachment[]; - - @Column({ type: "simple-json" }) - embeds: Embed[]; - - @Column({ type: "simple-json" }) - reactions: Reaction[]; - - @Column({ type: "text", nullable: true }) - nonce?: string; - - @Column({ nullable: true }) - pinned?: boolean; - - @Column({ type: "int" }) - type: MessageType; - - @Column({ type: "simple-json", nullable: true }) - activity?: { - type: number; - party_id: string; - }; - - @Column({ nullable: true }) - flags?: string; - @Column({ type: "simple-json", nullable: true }) - message_reference?: { - message_id: string; - channel_id?: string; - guild_id?: string; - }; - - @JoinColumn({ name: "message_reference_id" }) - @ManyToOne(() => Message) - referenced_message?: Message; - - @Column({ type: "simple-json", nullable: true }) - interaction?: { - id: string; - type: InteractionType; - name: string; - user_id: string; // the user who invoked the interaction - // user: User; // TODO: autopopulate user - }; - - @Column({ type: "simple-json", nullable: true }) - components?: MessageComponent[]; -} - -export interface MessageComponent { - type: number; - style?: number; - label?: string; - emoji?: PartialEmoji; - custom_id?: string; - url?: string; - disabled?: boolean; - components: MessageComponent[]; -} - -export enum MessageComponentType { - Script = 0, // self command script - ActionRow = 1, - Button = 2, -} - -export interface Embed { - title?: string; //title of embed - type?: EmbedType; // type of embed (always "rich" for webhook embeds) - description?: string; // description of embed - url?: string; // url of embed - timestamp?: Date; // timestamp of embed content - color?: number; // color code of the embed - footer?: { - text: string; - icon_url?: string; - proxy_icon_url?: string; - }; // footer object footer information - image?: EmbedImage; // image object image information - thumbnail?: EmbedImage; // thumbnail object thumbnail information - video?: EmbedImage; // video object video information - provider?: { - name?: string; - url?: string; - }; // provider object provider information - author?: { - name?: string; - url?: string; - icon_url?: string; - proxy_icon_url?: string; - }; // author object author information - fields?: { - name: string; - value: string; - inline?: boolean; - }[]; -} - -export enum EmbedType { - rich = "rich", - image = "image", - video = "video", - gifv = "gifv", - article = "article", - link = "link", -} - -export interface EmbedImage { - url?: string; - proxy_url?: string; - height?: number; - width?: number; -} - -export interface Reaction { - count: number; - //// not saved in the database // me: boolean; // whether the current user reacted using this emoji - emoji: PartialEmoji; - user_ids: string[]; -} - -export interface PartialEmoji { - id?: string; - name: string; - animated?: boolean; -} - -export interface AllowedMentions { - parse?: ("users" | "roles" | "everyone")[]; - roles?: string[]; - users?: string[]; - replied_user?: boolean; -} diff --git a/util/src/entities/Migration.ts b/util/src/entities/Migration.ts deleted file mode 100644 index 3f39ae72..00000000 --- a/util/src/entities/Migration.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Column, Entity, ObjectIdColumn, PrimaryGeneratedColumn } from "typeorm"; -import { BaseClassWithoutId } from "."; - -export const PrimaryIdAutoGenerated = process.env.DATABASE?.startsWith("mongodb") - ? ObjectIdColumn - : PrimaryGeneratedColumn; - -@Entity("migrations") -export class Migration extends BaseClassWithoutId { - @PrimaryIdAutoGenerated() - id: number; - - @Column({ type: "bigint" }) - timestamp: number; - - @Column() - name: string; -} diff --git a/util/src/entities/RateLimit.ts b/util/src/entities/RateLimit.ts deleted file mode 100644 index f5916f6b..00000000 --- a/util/src/entities/RateLimit.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Column, Entity } from "typeorm"; -import { BaseClass } from "./BaseClass"; - -@Entity("rate_limits") -export class RateLimit extends BaseClass { - @Column() // no relation as it also - executor_id: string; - - @Column() - hits: number; - - @Column() - blocked: boolean; - - @Column() - expires_at: Date; -} diff --git a/util/src/entities/ReadState.ts b/util/src/entities/ReadState.ts deleted file mode 100644 index b915573b..00000000 --- a/util/src/entities/ReadState.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Column, Entity, Index, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { BaseClass } from "./BaseClass"; -import { Channel } from "./Channel"; -import { Message } from "./Message"; -import { User } from "./User"; - -// for read receipts -// notification cursor and public read receipt need to be forwards-only (the former to prevent re-pinging when marked as unread, and the latter to be acceptable as a legal acknowledgement in criminal proceedings), and private read marker needs to be advance-rewind capable -// public read receipt ≥ notification cursor ≥ private fully read marker - -@Entity("read_states") -@Index(["channel_id", "user_id"], { unique: true }) -export class ReadState extends BaseClass { - @Column() - @RelationId((read_state: ReadState) => read_state.channel) - channel_id: string; - - @JoinColumn({ name: "channel_id" }) - @ManyToOne(() => Channel, { - onDelete: "CASCADE", - }) - channel: Channel; - - @Column() - @RelationId((read_state: ReadState) => read_state.user) - user_id: string; - - @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, { - onDelete: "CASCADE", - }) - user: User; - - // fully read marker - @Column({ nullable: true }) - last_message_id: string; - - // public read receipt - @Column({ nullable: true }) - public_ack: string; - - // notification cursor / private read receipt - @Column({ nullable: true }) - notifications_cursor: string; - - @Column({ nullable: true }) - last_pin_timestamp?: Date; - - @Column({ nullable: true }) - mention_count: number; - - // @Column({ nullable: true }) - // TODO: derive this from (last_message_id=notifications_cursor=public_ack)=true - manual: boolean; -} diff --git a/util/src/entities/Recipient.ts b/util/src/entities/Recipient.ts deleted file mode 100644 index a945f938..00000000 --- a/util/src/entities/Recipient.ts +++ /dev/null @@ -1,30 +0,0 @@ -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, { - onDelete: "CASCADE", - }) - channel: import("./Channel").Channel; - - @Column() - @RelationId((recipient: Recipient) => recipient.user) - user_id: string; - - @JoinColumn({ name: "user_id" }) - @ManyToOne(() => require("./User").User, { - onDelete: "CASCADE", - }) - user: import("./User").User; - - @Column({ default: false }) - closed: boolean; - - // TODO: settings/mute/nick/added at/encryption keys/read_state -} diff --git a/util/src/entities/Relationship.ts b/util/src/entities/Relationship.ts deleted file mode 100644 index c3592c76..00000000 --- a/util/src/entities/Relationship.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Column, Entity, Index, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { BaseClass } from "./BaseClass"; -import { User } from "./User"; - -export enum RelationshipType { - outgoing = 4, - incoming = 3, - blocked = 2, - friends = 1, -} - -@Entity("relationships") -@Index(["from_id", "to_id"], { unique: true }) -export class Relationship extends BaseClass { - @Column({}) - @RelationId((relationship: Relationship) => relationship.from) - from_id: string; - - @JoinColumn({ name: "from_id" }) - @ManyToOne(() => User, { - onDelete: "CASCADE", - }) - from: User; - - @Column({}) - @RelationId((relationship: Relationship) => relationship.to) - to_id: string; - - @JoinColumn({ name: "to_id" }) - @ManyToOne(() => User, { - onDelete: "CASCADE", - }) - to: User; - - @Column({ nullable: true }) - nickname?: string; - - @Column({ type: "int" }) - type: RelationshipType; - - toPublicRelationship() { - return { - id: this.to?.id || this.to_id, - type: this.type, - nickname: this.nickname, - user: this.to?.toPublicUser(), - }; - } -} diff --git a/util/src/entities/Role.ts b/util/src/entities/Role.ts deleted file mode 100644 index 4b721b5b..00000000 --- a/util/src/entities/Role.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; - -import { BaseClass } from "./BaseClass"; -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, { - onDelete: "CASCADE", - }) - guild: Guild; - - @Column() - color: number; - - @Column() - hoist: boolean; - - @Column() - managed: boolean; - - @Column() - mentionable: boolean; - - @Column() - name: string; - - @Column() - permissions: string; - - @Column() - position: number; - - @Column({ nullable: true }) - icon: string; - - @Column({ nullable: true }) - unicode_emoji: string; - - @Column({ type: "simple-json", nullable: true }) - tags?: { - bot_id?: string; - integration_id?: string; - premium_subscriber?: boolean; - }; -} diff --git a/util/src/entities/Session.ts b/util/src/entities/Session.ts deleted file mode 100644 index 969efa89..00000000 --- a/util/src/entities/Session.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { User } from "./User"; -import { BaseClass } from "./BaseClass"; -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { Status } from "../interfaces/Status"; -import { Activity } from "../interfaces/Activity"; - -//TODO we need to remove all sessions on server start because if the server crashes without closing websockets it won't delete them - -@Entity("sessions") -export class Session extends BaseClass { - @Column({ nullable: true }) - @RelationId((session: Session) => session.user) - user_id: string; - - @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, { - onDelete: "CASCADE", - }) - user: User; - - //TODO check, should be 32 char long hex string - @Column({ nullable: false, select: false }) - session_id: string; - - @Column({ type: "simple-json", nullable: true }) - activities: Activity[]; - - // TODO client_status - @Column({ type: "simple-json", select: false }) - client_info: { - client: string; - os: string; - version: number; - }; - - @Column({ nullable: false, type: "varchar" }) - status: Status; //TODO enum -} - -export const PrivateSessionProjection: (keyof Session)[] = [ - "user_id", - "session_id", - "activities", - "client_info", - "status", -]; diff --git a/util/src/entities/Sticker.ts b/util/src/entities/Sticker.ts deleted file mode 100644 index 37bc6fbe..00000000 --- a/util/src/entities/Sticker.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { User } from "./User"; -import { BaseClass } from "./BaseClass"; -import { Guild } from "./Guild"; - -export enum StickerType { - STANDARD = 1, - GUILD = 2, -} - -export enum StickerFormatType { - GIF = 0, // gif is a custom format type and not in discord spec - PNG = 1, - APNG = 2, - LOTTIE = 3, -} - -@Entity("stickers") -export class Sticker extends BaseClass { - @Column() - name: string; - - @Column({ nullable: true }) - description?: string; - - @Column({ nullable: true }) - available?: boolean; - - @Column({ nullable: true }) - tags?: string; - - @Column({ nullable: true }) - @RelationId((sticker: Sticker) => sticker.pack) - pack_id?: string; - - @JoinColumn({ name: "pack_id" }) - @ManyToOne(() => require("./StickerPack").StickerPack, { - onDelete: "CASCADE", - nullable: true, - }) - pack: import("./StickerPack").StickerPack; - - @Column({ nullable: true }) - guild_id?: string; - - @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, { - onDelete: "CASCADE", - }) - guild?: Guild; - - @Column({ nullable: true }) - user_id?: string; - - @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, { - onDelete: "CASCADE", - }) - user?: User; - - @Column({ type: "int" }) - type: StickerType; - - @Column({ type: "int" }) - format_type: StickerFormatType; -} diff --git a/util/src/entities/StickerPack.ts b/util/src/entities/StickerPack.ts deleted file mode 100644 index ec8c69a2..00000000 --- a/util/src/entities/StickerPack.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToOne, OneToMany, OneToOne, RelationId } from "typeorm"; -import { Sticker } from "."; -import { BaseClass } from "./BaseClass"; - -@Entity("sticker_packs") -export class StickerPack extends BaseClass { - @Column() - name: string; - - @Column({ nullable: true }) - description?: string; - - @Column({ nullable: true }) - banner_asset_id?: string; - - @OneToMany(() => Sticker, (sticker: Sticker) => sticker.pack, { - cascade: true, - orphanedRowAction: "delete", - }) - stickers: Sticker[]; - - // sku_id: string - - @Column({ nullable: true }) - @RelationId((pack: StickerPack) => pack.cover_sticker) - cover_sticker_id?: string; - - @ManyToOne(() => Sticker, { nullable: true }) - @JoinColumn() - cover_sticker?: Sticker; -} diff --git a/util/src/entities/Team.ts b/util/src/entities/Team.ts deleted file mode 100644 index 22140b7f..00000000 --- a/util/src/entities/Team.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToMany, RelationId } from "typeorm"; -import { BaseClass } from "./BaseClass"; -import { TeamMember } from "./TeamMember"; -import { User } from "./User"; - -@Entity("teams") -export class Team extends BaseClass { - @Column({ nullable: true }) - icon?: string; - - @JoinColumn({ name: "member_ids" }) - @OneToMany(() => TeamMember, (member: TeamMember) => member.team, { - orphanedRowAction: "delete", - }) - 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) - owner_user: User; -} diff --git a/util/src/entities/TeamMember.ts b/util/src/entities/TeamMember.ts deleted file mode 100644 index b726e1e8..00000000 --- a/util/src/entities/TeamMember.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { BaseClass } from "./BaseClass"; -import { User } from "./User"; - -export enum TeamMemberState { - INVITED = 1, - ACCEPTED = 2, -} - -@Entity("team_members") -export class TeamMember extends BaseClass { - @Column({ type: "int" }) - membership_state: TeamMemberState; - - @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.members, { - onDelete: "CASCADE", - }) - team: import("./Team").Team; - - @Column({ nullable: true }) - @RelationId((member: TeamMember) => member.user) - user_id: string; - - @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, { - onDelete: "CASCADE", - }) - user: User; -} diff --git a/util/src/entities/Template.ts b/util/src/entities/Template.ts deleted file mode 100644 index 1d952283..00000000 --- a/util/src/entities/Template.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { BaseClass } from "./BaseClass"; -import { Guild } from "./Guild"; -import { User } from "./User"; - -@Entity("templates") -export class Template extends BaseClass { - @Column({ unique: true }) - code: string; - - @Column() - name: string; - - @Column({ nullable: true }) - description?: string; - - @Column({ nullable: true }) - usage_count?: number; - - @Column({ nullable: true }) - @RelationId((template: Template) => template.creator) - creator_id: string; - - @JoinColumn({ name: "creator_id" }) - @ManyToOne(() => User) - creator: User; - - @Column() - created_at: Date; - - @Column() - updated_at: Date; - - @Column({ nullable: true }) - @RelationId((template: Template) => template.source_guild) - source_guild_id: string; - - @JoinColumn({ name: "source_guild_id" }) - @ManyToOne(() => Guild) - source_guild: Guild; - - @Column({ type: "simple-json" }) - serialized_source_guild: Guild; -} diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts deleted file mode 100644 index 9b1c494e..00000000 --- a/util/src/entities/User.ts +++ /dev/null @@ -1,403 +0,0 @@ -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 { Config, FieldErrors, Snowflake, trimSpecial } from ".."; -import { Member, Session } from "."; - -export enum PublicUserEnum { - username, - discriminator, - id, - public_flags, - avatar, - accent_color, - banner, - bio, - bot, - premium_since, -} -export type PublicUserKeys = keyof typeof PublicUserEnum; - -export enum PrivateUserEnum { - flags, - mfa_enabled, - email, - phone, - verified, - nsfw_allowed, - premium, - premium_type, - disabled, - settings, - // locale -} -export type PrivateUserKeys = keyof typeof PrivateUserEnum | PublicUserKeys; - -export const PublicUserProjection = Object.values(PublicUserEnum).filter( - (x) => typeof x === "string" -) as PublicUserKeys[]; -export const PrivateUserProjection = [ - ...PublicUserProjection, - ...Object.values(PrivateUserEnum).filter((x) => typeof x === "string"), -] as PrivateUserKeys[]; - -// Private user data that should never get sent to the client -export type PublicUser = Pick<User, PublicUserKeys>; - -export interface UserPublic extends Pick<User, PublicUserKeys> {} - -export interface UserPrivate extends Pick<User, PrivateUserKeys> { - locale: string; -} - -// TODO: add purchased_flags, premium_usage_flags - -@Entity("users") -export class User extends BaseClass { - @Column() - username: string; // username max length 32, min 2 (should be configurable) - - @Column() - discriminator: string; // opaque string: 4 digits on discord.com - - setDiscriminator(val: string) { - const number = Number(val); - if (isNaN(number)) throw new Error("invalid discriminator"); - if (number <= 0 || number >= 10000) throw new Error("discriminator must be between 1 and 9999"); - this.discriminator = val.toString().padStart(4, "0"); - } - - @Column({ nullable: true }) - avatar?: string; // hash of the user avatar - - @Column({ nullable: true }) - accent_color?: number; // banner color of user - - @Column({ nullable: true }) - banner?: string; // hash of the user banner - - @Column({ nullable: true, select: false }) - phone?: string; // phone number of the user - - @Column({ select: false }) - desktop: boolean; // if the user has desktop app installed - - @Column({ select: false }) - mobile: boolean; // if the user has mobile app installed - - @Column() - premium: boolean; // if user bought individual premium - - @Column() - premium_type: number; // individual premium level - - @Column() - bot: boolean; // if user is bot - - @Column() - bio: string; // short description of the user (max 190 chars -> should be configurable) - - @Column() - system: boolean; // shouldn't be used, the api sends this field type true, if the generated message comes from a system generated author - - @Column({ select: false }) - nsfw_allowed: boolean; // if the user can do age-restricted actions (NSFW channels/guilds/commands) - - @Column({ select: false }) - mfa_enabled: boolean; // if multi factor authentication is enabled - - @Column() - created_at: Date; // registration date - - @Column({ nullable: true }) - premium_since: Date; // premium date - - @Column({ select: false }) - verified: boolean; // if the user is offically verified - - @Column() - disabled: boolean; // if the account is disabled - - @Column() - deleted: boolean; // if the user was deleted - - @Column({ nullable: true, select: false }) - email?: string; // email of the user - - @Column() - flags: string; // UserFlags - - @Column() - public_flags: number; - - @Column({ type: "bigint" }) - rights: string; // Rights - - @OneToMany(() => Session, (session: Session) => session.user) - sessions: Session[]; - - @JoinColumn({ name: "relationship_ids" }) - @OneToMany(() => Relationship, (relationship: Relationship) => relationship.from, { - cascade: true, - orphanedRowAction: "delete", - }) - relationships: Relationship[]; - - @JoinColumn({ name: "connected_account_ids" }) - @OneToMany(() => ConnectedAccount, (account: ConnectedAccount) => account.user, { - cascade: true, - orphanedRowAction: "delete", - }) - connected_accounts: ConnectedAccount[]; - - @Column({ type: "simple-json", select: false }) - data: { - valid_tokens_since: Date; // all tokens with a previous issue date are invalid - hash?: string; // hash of the password, salt is saved in password (bcrypt) - }; - - @Column({ type: "simple-array", select: false }) - fingerprints: string[]; // array of fingerprints -> used to prevent multiple accounts - - @Column({ type: "simple-json", select: false }) - settings: UserSettings; - - // workaround to prevent fossord-unaware clients from deleting settings not used by them - @Column({ type: "simple-json", select: false }) - extended_settings: string; - - @Column({ type: "simple-json" }) - notes: { [key: string]: string }; //key is ID of user - - toPublicUser() { - const user: any = {}; - PublicUserProjection.forEach((x) => { - user[x] = this[x]; - }); - return user as PublicUser; - } - - static async getPublicUser(user_id: string, opts?: FindOneOptions<User>) { - return await User.findOneOrFail( - { id: user_id }, - { - ...opts, - select: [...PublicUserProjection, ...(opts?.select || [])], - } - ); - } - - private static async generateDiscriminator(username: string): Promise<string | undefined> { - if (Config.get().register.incrementingDiscriminators) { - // discriminator will be incrementally generated - - // First we need to figure out the currently highest discrimnator for the given username and then increment it - const users = await User.find({ where: { username }, select: ["discriminator"] }); - const highestDiscriminator = Math.max(0, ...users.map((u) => Number(u.discriminator))); - - const discriminator = highestDiscriminator + 1; - if (discriminator >= 10000) { - return undefined; - } - - return discriminator.toString().padStart(4, "0"); - } else { - // discriminator will be randomly generated - - // randomly generates a discriminator between 1 and 9999 and checks max five times if it already exists - // TODO: is there any better way to generate a random discriminator only once, without checking if it already exists in the database? - for (let tries = 0; tries < 5; tries++) { - const discriminator = Math.randomIntBetween(1, 9999).toString().padStart(4, "0"); - const exists = await User.findOne({ where: { discriminator, username: username }, select: ["id"] }); - if (!exists) return discriminator; - } - - return undefined; - } - } - - static async register({ - email, - username, - password, - date_of_birth, - req, - }: { - username: string; - password?: string; - email?: string; - date_of_birth?: Date; // "2000-04-03" - req?: any; - }) { - // trim special uf8 control characters -> Backspace, Newline, ... - username = trimSpecial(username); - - const discriminator = await User.generateDiscriminator(username); - if (!discriminator) { - // We've failed to generate a valid and unused discriminator - throw FieldErrors({ - username: { - code: "USERNAME_TOO_MANY_USERS", - message: req.t("auth:register.USERNAME_TOO_MANY_USERS"), - }, - }); - } - - // TODO: save date_of_birth - // appearently discord doesn't save the date of birth and just calculate if nsfw is allowed - // if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false - const language = req.language === "en" ? "en-US" : req.language || "en-US"; - - const user = new User({ - created_at: new Date(), - username: username, - discriminator, - id: Snowflake.generate(), - bot: false, - system: false, - premium_since: new Date(), - desktop: false, - mobile: false, - premium: true, - premium_type: 2, - bio: "", - mfa_enabled: false, - verified: true, - disabled: false, - deleted: false, - email: email, - rights: "0", // TODO: grant rights correctly, as 0 actually stands for no rights at all - nsfw_allowed: true, // TODO: depending on age - public_flags: "0", - flags: "0", // TODO: generate - data: { - hash: password, - valid_tokens_since: new Date(), - }, - settings: { ...defaultSettings, locale: language }, - extended_settings: {}, - fingerprints: [], - notes: {}, - }); - - await user.save(); - - setImmediate(async () => { - if (Config.get().guild.autoJoin.enabled) { - for (const guild of Config.get().guild.autoJoin.guilds || []) { - await Member.addToGuild(user.id, guild).catch((e) => {}); - } - } - }); - - return user; - } -} - -export const defaultSettings: UserSettings = { - afk_timeout: 3600, - allow_accessibility_detection: true, - animate_emoji: true, - animate_stickers: 0, - contact_sync_enabled: false, - convert_emoticons: false, - custom_status: null, - default_guilds_restricted: false, - detect_platform_accounts: false, - developer_mode: true, - disable_games_tab: true, - enable_tts_command: false, - explicit_content_filter: 0, - friend_source_flags: { all: true }, - gateway_connected: false, - gif_auto_play: true, - guild_folders: [], - guild_positions: [], - inline_attachment_media: true, - inline_embed_media: true, - locale: "en-US", - message_display_compact: true, - native_phone_integration_enabled: true, - render_embeds: true, - render_reactions: true, - restricted_guilds: [], - show_current_game: true, - status: "online", - stream_notifications_enabled: false, - theme: "dark", - timezone_offset: 0, // TODO: timezone from request -}; - -export interface UserSettings { - afk_timeout: number; - allow_accessibility_detection: boolean; - animate_emoji: boolean; - animate_stickers: number; - contact_sync_enabled: boolean; - convert_emoticons: boolean; - custom_status: { - emoji_id?: string; - emoji_name?: string; - expires_at?: number; - text?: string; - } | null; - default_guilds_restricted: boolean; - detect_platform_accounts: boolean; - developer_mode: boolean; - disable_games_tab: boolean; - enable_tts_command: boolean; - explicit_content_filter: number; - friend_source_flags: { all: boolean }; - gateway_connected: boolean; - gif_auto_play: boolean; - // every top guild is displayed as a "folder" - guild_folders: { - color: number; - guild_ids: string[]; - id: number; - name: string; - }[]; - guild_positions: string[]; // guild ids ordered by position - inline_attachment_media: boolean; - inline_embed_media: boolean; - locale: string; // en_US - message_display_compact: boolean; - native_phone_integration_enabled: boolean; - render_embeds: boolean; - render_reactions: boolean; - restricted_guilds: string[]; - show_current_game: boolean; - status: "online" | "offline" | "dnd" | "idle" | "invisible"; - stream_notifications_enabled: boolean; - theme: "dark" | "white"; // dark - timezone_offset: number; // e.g -60 -} - -export const CUSTOM_USER_FLAG_OFFSET = BigInt(1) << BigInt(32); - -export class UserFlags extends BitField { - static FLAGS = { - DISCORD_EMPLOYEE: BigInt(1) << BigInt(0), - PARTNERED_SERVER_OWNER: BigInt(1) << BigInt(1), - HYPESQUAD_EVENTS: BigInt(1) << BigInt(2), - BUGHUNTER_LEVEL_1: BigInt(1) << BigInt(3), - MFA_SMS: BigInt(1) << BigInt(4), - PREMIUM_PROMO_DISMISSED: BigInt(1) << BigInt(5), - HOUSE_BRAVERY: BigInt(1) << BigInt(6), - HOUSE_BRILLIANCE: BigInt(1) << BigInt(7), - HOUSE_BALANCE: BigInt(1) << BigInt(8), - EARLY_SUPPORTER: BigInt(1) << BigInt(9), - TEAM_USER: BigInt(1) << BigInt(10), - TRUST_AND_SAFETY: BigInt(1) << BigInt(11), - SYSTEM: BigInt(1) << BigInt(12), - HAS_UNREAD_URGENT_MESSAGES: BigInt(1) << BigInt(13), - BUGHUNTER_LEVEL_2: BigInt(1) << BigInt(14), - UNDERAGE_DELETED: BigInt(1) << BigInt(15), - VERIFIED_BOT: BigInt(1) << BigInt(16), - EARLY_VERIFIED_BOT_DEVELOPER: BigInt(1) << BigInt(17), - CERTIFIED_MODERATOR: BigInt(1) << BigInt(18), - BOT_HTTP_INTERACTIONS: BigInt(1) << BigInt(19), - }; -} diff --git a/util/src/entities/UserGroup.ts b/util/src/entities/UserGroup.ts deleted file mode 100644 index 709b9d0b..00000000 --- a/util/src/entities/UserGroup.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; - -import { BaseClass } from "./BaseClass"; -import { Guild } from "./Guild"; -import { User } from "./User"; - -@Entity("groups") -export class UserGroup extends BaseClass { - @Column() - color: number; - - @Column() - hoist: boolean; - - @JoinColumn({ name: "controller", referencedColumnName: "id" }) - @ManyToOne(() => User) - controller?: User; - - @Column() - mentionable_by?: string; - - @Column() - name: string; - - @Column() - rights: string; - - @Column({ nullable: true }) - icon: string; - - @Column({ nullable: true }) - parent?: string; - - @Column({ type: "simple-array", nullable: true}) - associciations: string[]; - -} diff --git a/util/src/entities/VoiceState.ts b/util/src/entities/VoiceState.ts deleted file mode 100644 index 75748a01..00000000 --- a/util/src/entities/VoiceState.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { BaseClass } from "./BaseClass"; -import { Channel } from "./Channel"; -import { Guild } from "./Guild"; -import { User } from "./User"; -import { Member } from "./Member"; - -//https://gist.github.com/vassjozsef/e482c65df6ee1facaace8b3c9ff66145#file-voice_state-ex -@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, { - onDelete: "CASCADE", - }) - guild?: Guild; - - @Column({ nullable: true }) - @RelationId((voice_state: VoiceState) => voice_state.channel) - channel_id: string; - - @JoinColumn({ name: "channel_id" }) - @ManyToOne(() => Channel, { - onDelete: "CASCADE", - }) - channel: Channel; - - @Column({ nullable: true }) - @RelationId((voice_state: VoiceState) => voice_state.user) - user_id: string; - - @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, { - onDelete: "CASCADE", - }) - user: User; - - // @JoinColumn([{ name: "user_id", referencedColumnName: "id" },{ name: "guild_id", referencedColumnName: "guild_id" }]) - // @ManyToOne(() => Member, { - // onDelete: "CASCADE", - // }) - //TODO find a way to make it work without breaking Guild.voice_states - member: Member; - - @Column() - session_id: string; - - @Column({ nullable: true }) - token: string; - - @Column() - deaf: boolean; - - @Column() - mute: boolean; - - @Column() - self_deaf: boolean; - - @Column() - self_mute: boolean; - - @Column({ nullable: true }) - self_stream?: boolean; - - @Column() - self_video: boolean; - - @Column() - suppress: boolean; // whether this user is muted by the current user - - @Column({ nullable: true, default: null }) - request_to_speak_timestamp?: Date; -} diff --git a/util/src/entities/Webhook.ts b/util/src/entities/Webhook.ts deleted file mode 100644 index 89538417..00000000 --- a/util/src/entities/Webhook.ts +++ /dev/null @@ -1,76 +0,0 @@ -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, - ChannelFollower = 2, -} - -@Entity("webhooks") -export class Webhook extends BaseClass { - @Column({ type: "int" }) - type: WebhookType; - - @Column({ nullable: true }) - name?: string; - - @Column({ nullable: true }) - avatar?: string; - - @Column({ nullable: true }) - token?: string; - - @Column({ nullable: true }) - @RelationId((webhook: Webhook) => webhook.guild) - guild_id: string; - - @JoinColumn({ name: "guild_id" }) - @ManyToOne(() => Guild, { - onDelete: "CASCADE", - }) - guild: Guild; - - @Column({ nullable: true }) - @RelationId((webhook: Webhook) => webhook.channel) - channel_id: string; - - @JoinColumn({ name: "channel_id" }) - @ManyToOne(() => Channel, { - onDelete: "CASCADE", - }) - channel: Channel; - - @Column({ nullable: true }) - @RelationId((webhook: Webhook) => webhook.application) - application_id: string; - - @JoinColumn({ name: "application_id" }) - @ManyToOne(() => Application, { - onDelete: "CASCADE", - }) - application: Application; - - @Column({ nullable: true }) - @RelationId((webhook: Webhook) => webhook.user) - user_id: string; - - @JoinColumn({ name: "user_id" }) - @ManyToOne(() => User, { - onDelete: "CASCADE", - }) - user: User; - - @Column({ nullable: true }) - @RelationId((webhook: Webhook) => webhook.guild) - source_guild_id: string; - - @JoinColumn({ name: "source_guild_id" }) - @ManyToOne(() => Guild, { - onDelete: "CASCADE", - }) - source_guild: Guild; -} diff --git a/util/src/entities/index.ts b/util/src/entities/index.ts deleted file mode 100644 index f023d5a6..00000000 --- a/util/src/entities/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -export * from "./Application"; -export * from "./Attachment"; -export * from "./AuditLog"; -export * from "./Ban"; -export * from "./BaseClass"; -export * from "./Categories"; -export * from "./Channel"; -export * from "./Config"; -export * from "./ConnectedAccount"; -export * from "./Emoji"; -export * from "./Guild"; -export * from "./Invite"; -export * from "./Member"; -export * from "./Message"; -export * from "./Migration"; -export * from "./RateLimit"; -export * from "./ReadState"; -export * from "./Recipient"; -export * from "./Relationship"; -export * from "./Role"; -export * from "./Session"; -export * from "./Sticker"; -export * from "./StickerPack"; -export * from "./Team"; -export * from "./TeamMember"; -export * from "./Template"; -export * from "./User"; -export * from "./VoiceState"; -export * from "./Webhook"; -export * from "./ClientRelease"; \ No newline at end of file |