diff --git a/util/src/dtos/DmChannelDTO.ts b/src/util/dtos/DmChannelDTO.ts
index 226b2f9d..93b1adfa 100644
--- a/util/src/dtos/DmChannelDTO.ts
+++ b/src/util/dtos/DmChannelDTO.ts
@@ -1,5 +1,5 @@
-import { MinimalPublicUserDTO } from "./UserDTO";
import { Channel, PublicUserProjection, User } from "../entities";
+import { MinimalPublicUserDTO } from "./UserDTO";
export class DmChannelDTO {
icon: string | null;
@@ -35,7 +35,7 @@ export class DmChannelDTO {
excludedRecipients(excluded_recipients: string[]): DmChannelDTO {
return {
...this,
- recipients: this.recipients.filter((r) => !excluded_recipients.includes(r.id)),
+ recipients: this.recipients.filter((r) => !excluded_recipients.includes(r.id))
};
}
}
diff --git a/util/src/dtos/UserDTO.ts b/src/util/dtos/UserDTO.ts
index ee2752a4..ee2752a4 100644
--- a/util/src/dtos/UserDTO.ts
+++ b/src/util/dtos/UserDTO.ts
diff --git a/util/src/dtos/index.ts b/src/util/dtos/index.ts
index 0e8f8459..0e8f8459 100644
--- a/util/src/dtos/index.ts
+++ b/src/util/dtos/index.ts
diff --git a/util/src/entities/Application.ts b/src/util/entities/Application.ts
index fab3d93f..35fc496d 100644
--- a/util/src/entities/Application.ts
+++ b/src/util/entities/Application.ts
@@ -1,6 +1,5 @@
-import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
+import { Column, Entity, JoinColumn, ManyToOne, OneToOne } from "typeorm";
import { BaseClass } from "./BaseClass";
-import { Guild } from "./Guild";
import { Team } from "./Team";
import { User } from "./User";
@@ -12,55 +11,101 @@ export class Application extends BaseClass {
@Column({ nullable: true })
icon?: string;
- @Column()
+ @Column({ nullable: true })
description: string;
- @Column({ type: "simple-array", nullable: true })
- rpc_origins?: string[];
+ @Column({ nullable: true })
+ summary: string = "";
+
+ @Column({ type: "simple-json", nullable: true })
+ type?: any;
@Column()
- bot_public: boolean;
+ hook: boolean = true;
@Column()
- bot_require_code_grant: boolean;
+ bot_public?: boolean = true;
- @Column({ nullable: true })
- terms_of_service_url?: string;
+ @Column()
+ bot_require_code_grant?: boolean = false;
- @Column({ nullable: true })
- privacy_policy_url?: string;
+ @Column()
+ verify_key: string;
@JoinColumn({ name: "owner_id" })
@ManyToOne(() => User)
- owner?: User;
+ owner: User;
+
+ @Column()
+ flags: number = 0;
+
+ @Column({ type: "simple-array", nullable: true })
+ redirect_uris: string[] = [];
@Column({ nullable: true })
- summary?: string;
+ rpc_application_state: number = 0;
- @Column()
- verify_key: string;
+ @Column({ nullable: true })
+ store_application_state: number = 1;
- @JoinColumn({ name: "team_id" })
- @ManyToOne(() => Team, {
- onDelete: "CASCADE",
- })
- team?: Team;
+ @Column({ nullable: true })
+ verification_state: number = 1;
- @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 })
+ interactions_endpoint_url?: string;
@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,
+ integration_public: boolean = true;
@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
+ integration_require_code_grant: boolean = false;
+
+ @Column({ nullable: true })
+ discoverability_state: number = 1;
+
+ @Column({ nullable: true })
+ discovery_eligibility_flags: number = 2240;
+
+ @JoinColumn({ name: "bot_user_id" })
+ @OneToOne(() => User)
+ bot?: User;
+
+ @Column({ type: "simple-array", nullable: true })
+ tags?: string[];
@Column({ nullable: true })
cover_image?: string; // the application's default rich presence invite cover image hash
- @Column()
- flags: string; // the application's public flags
+ @Column({ type: "simple-json", nullable: true })
+ install_params?: { scopes: string[]; permissions: string };
+
+ @Column({ nullable: true })
+ terms_of_service_url?: string;
+
+ @Column({ nullable: true })
+ privacy_policy_url?: string;
+
+ //just for us
+
+ //@Column({ type: "simple-array", nullable: true })
+ //rpc_origins?: string[];
+
+ //@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
+
+ @JoinColumn({ name: "team_id" })
+ @ManyToOne(() => Team, {
+ onDelete: "CASCADE",
+ nullable: true
+ })
+ team?: Team;
}
export interface ApplicationCommand {
@@ -93,7 +138,7 @@ export enum ApplicationCommandOptionType {
BOOLEAN = 5,
USER = 6,
CHANNEL = 7,
- ROLE = 8,
+ ROLE = 8
}
export interface ApplicationCommandInteractionData {
diff --git a/util/src/entities/Attachment.ts b/src/util/entities/Attachment.ts
index 7b4b17eb..8392f415 100644
--- a/util/src/entities/Attachment.ts
+++ b/src/util/entities/Attachment.ts
@@ -32,7 +32,7 @@ export class Attachment extends BaseClass {
@JoinColumn({ name: "message_id" })
@ManyToOne(() => require("./Message").Message, (message: import("./Message").Message) => message.attachments, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
message: import("./Message").Message;
diff --git a/util/src/entities/AuditLog.ts b/src/util/entities/AuditLog.ts
index b003e7ba..6f394f42 100644
--- a/util/src/entities/AuditLog.ts
+++ b/src/util/entities/AuditLog.ts
@@ -5,24 +5,24 @@ import { User } from "./User";
export enum AuditLogEvents {
// guild level
- GUILD_UPDATE = 1,
+ GUILD_UPDATE = 1,
GUILD_IMPORT = 2,
GUILD_EXPORTED = 3,
GUILD_ARCHIVE = 4,
GUILD_UNARCHIVE = 5,
// join-leave
- USER_JOIN = 6,
+ USER_JOIN = 6,
USER_LEAVE = 7,
// channels
- CHANNEL_CREATE = 10,
+ CHANNEL_CREATE = 10,
CHANNEL_UPDATE = 11,
CHANNEL_DELETE = 12,
// permission overrides
- CHANNEL_OVERWRITE_CREATE = 13,
+ CHANNEL_OVERWRITE_CREATE = 13,
CHANNEL_OVERWRITE_UPDATE = 14,
CHANNEL_OVERWRITE_DELETE = 15,
// kick and ban
- MEMBER_KICK = 20,
+ MEMBER_KICK = 20,
MEMBER_PRUNE = 21,
MEMBER_BAN_ADD = 22,
MEMBER_BAN_REMOVE = 23,
@@ -79,18 +79,18 @@ export enum AuditLogEvents {
// application commands
APPLICATION_COMMAND_PERMISSION_UPDATE = 121,
// automod
- POLICY_CREATE = 140,
+ POLICY_CREATE = 140,
POLICY_UPDATE = 141,
POLICY_DELETE = 142,
- MESSAGE_BLOCKED_BY_POLICIES = 143, // in fosscord, blocked messages are stealth-dropped
+ 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,
+ ROUTE_CREATE = 225,
+ ROUTE_UPDATE = 226
}
@Entity("audit_logs")
diff --git a/util/src/entities/Ban.ts b/src/util/entities/Ban.ts
index 9504bd8e..27c75278 100644
--- a/util/src/entities/Ban.ts
+++ b/src/util/entities/Ban.ts
@@ -11,7 +11,7 @@ export class Ban extends BaseClass {
@JoinColumn({ name: "user_id" })
@ManyToOne(() => User, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
user: User;
@@ -21,7 +21,7 @@ export class Ban extends BaseClass {
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
guild: Guild;
diff --git a/util/src/entities/Categories.ts b/src/util/entities/Categories.ts
index 81fbc303..08a79112 100644
--- a/util/src/entities/Categories.ts
+++ b/src/util/entities/Categories.ts
@@ -1,4 +1,4 @@
-import { PrimaryColumn, Column, Entity} from "typeorm";
+import { Column, Entity, PrimaryColumn } from "typeorm";
import { BaseClassWithoutId } from "./BaseClass";
// TODO: categories:
@@ -16,18 +16,18 @@ import { BaseClassWithoutId } from "./BaseClass";
// Also populate discord default categories
@Entity("categories")
-export class Categories extends BaseClassWithoutId { // Not using snowflake
-
- @PrimaryColumn()
- id: number;
+export class Categories extends BaseClassWithoutId {
+ // Not using snowflake
- @Column({ nullable: true })
- name: string;
+ @PrimaryColumn()
+ id: number;
- @Column({ type: "simple-json" })
- localizations: string;
+ @Column({ nullable: true })
+ name: string;
- @Column({ nullable: true })
- is_primary: boolean;
+ @Column({ type: "simple-json" })
+ localizations: string;
-}
\ No newline at end of file
+ @Column({ nullable: true })
+ is_primary: boolean;
+}
diff --git a/util/src/entities/Channel.ts b/src/util/entities/Channel.ts
index 69c08be7..b17fdba0 100644
--- a/util/src/entities/Channel.ts
+++ b/src/util/entities/Channel.ts
@@ -1,380 +1,389 @@
-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,
-}
+import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm";
+import { DmChannelDTO } from "../dtos";
+import { ChannelCreateEvent, ChannelRecipientRemoveEvent } from "../interfaces";
+import { containsAll, emitEvent, getPermission, InvisibleCharacters, Snowflake, trimSpecial } from "../util";
+import { HTTPError } from "../util/imports/HTTPError";
+import { OrmUtils } from "../util/imports/OrmUtils";
+import { BaseClass } from "./BaseClass";
+import { Guild } from "./Guild";
+import { Invite } from "./Invite";
+import { Message } from "./Message";
+import { ReadState } from "./ReadState";
+import { Recipient } from "./Recipient";
+import { PublicUserProjection, User } from "./User";
+import { VoiceState } from "./VoiceState";
+import { Webhook } from "./Webhook";
+
+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[];
+
+ @Column({ nullable: true })
+ flags?: number = 0;
+
+ @Column({ nullable: true })
+ default_thread_rate_limit_per_user?: number = 0;
+
+ // 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({ where: { id: channel.guild_id } });
+ if (!guild.features.includes("ALLOW_INVALID_CHANNEL_NAMES") && channel.name) {
+ for (let character of InvisibleCharacters)
+ if (channel.name.includes(character)) throw new HTTPError("Channel name cannot include invalid characters", 403);
+
+ // Categories and voice skip these checks on discord.com
+ const skipChecksTypes = [ChannelType.GUILD_CATEGORY, ChannelType.GUILD_VOICE];
+ if ((channel.type && !skipChecksTypes.includes(channel.type)) || guild.features.includes("IRC_LIKE_CHANNEL_NAMES")) {
+ if (channel.name.includes(" ")) 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);
+ } else channel.name = channel.name.trim(); //category names are trimmed client side on discord.com
+ }
+
+ 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({ where: { 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([
+ OrmUtils.mergeDeep(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.GROUP_DM : ChannelType.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;
+ ur = OrmUtils.mergeDeep(ur, { closed: false });
+ await ur.save();
+ }
+ }
+ }
+ }
+
+ if (channel == null) {
+ name = trimSpecial(name);
+
+ channel = await (
+ OrmUtils.mergeDeep(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) =>
+ OrmUtils.mergeDeep(new Recipient(), {
+ user_id: x,
+ closed: !(type === ChannelType.GROUP_DM || x === creator_user_id)
+ })
+ )
+ }) as Channel
+ ).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/src/util/entities/ClientRelease.ts
index c5afd307..2723ab67 100644
--- a/util/src/entities/ClientRelease.ts
+++ b/src/util/entities/ClientRelease.ts
@@ -1,4 +1,4 @@
-import { Column, Entity} from "typeorm";
+import { Column, Entity } from "typeorm";
import { BaseClass } from "./BaseClass";
@Entity("client_release")
diff --git a/util/src/entities/ConnectedAccount.ts b/src/util/entities/ConnectedAccount.ts
index 09ae30ab..018b3995 100644
--- a/util/src/entities/ConnectedAccount.ts
+++ b/src/util/entities/ConnectedAccount.ts
@@ -12,7 +12,7 @@ export class ConnectedAccount extends BaseClass {
@JoinColumn({ name: "user_id" })
@ManyToOne(() => User, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
user: User;
diff --git a/util/src/entities/Emoji.ts b/src/util/entities/Emoji.ts
index a3615b7d..a2552995 100644
--- a/util/src/entities/Emoji.ts
+++ b/src/util/entities/Emoji.ts
@@ -2,7 +2,6 @@ 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 {
@@ -17,7 +16,7 @@ export class Emoji extends BaseClass {
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
guild: Guild;
@@ -40,7 +39,7 @@ export class Emoji extends BaseClass {
@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/Group.ts b/src/util/entities/Group.ts
index b24d38cf..23aaabf2 100644
--- a/util/src/entities/Group.ts
+++ b/src/util/entities/Group.ts
@@ -1,11 +1,11 @@
-import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
+import { Column, Entity } from "typeorm";
import { BaseClass } from "./BaseClass";
@Entity("groups")
export class UserGroup extends BaseClass {
- @Column({ nullable: true })
- parent?: BigInt;
+ @Column({ nullable: true })
+ parent?: BigInt;
@Column()
color: number;
@@ -13,7 +13,7 @@ export class UserGroup extends BaseClass {
@Column()
hoist: boolean;
- @Column()
+ @Column()
mentionable: boolean;
@Column()
diff --git a/util/src/entities/Guild.ts b/src/util/entities/Guild.ts
index 70bb41c5..015c6d04 100644
--- a/util/src/entities/Guild.ts
+++ b/src/util/entities/Guild.ts
@@ -1,5 +1,6 @@
-import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToMany, OneToOne, RelationId } from "typeorm";
+import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm";
import { Config, handleFile, Snowflake } from "..";
+import { OrmUtils } from "../util/imports/OrmUtils";
import { Ban } from "./Ban";
import { BaseClass } from "./BaseClass";
import { Channel } from "./Channel";
@@ -31,15 +32,7 @@ import { Webhook } from "./Webhook";
// "Gacha"
// ],
-export const PublicGuildRelations = [
- "channels",
- "emojis",
- "members",
- "roles",
- "stickers",
- "voice_states",
- "members.user",
-];
+export const PublicGuildRelations = ["channels", "emojis", "members", "roles", "stickers", "voice_states", "members.user"];
@Entity("guilds")
export class Guild extends BaseClass {
@@ -52,7 +45,7 @@ export class Guild extends BaseClass {
afk_channel?: Channel;
@Column({ nullable: true })
- afk_timeout?: number;
+ afk_timeout?: number = Config.get().defaults.guild.afkTimeout;
// * commented out -> use owner instead
// application id of the guild creator if it is bot-created
@@ -62,7 +55,7 @@ export class Guild extends BaseClass {
@JoinColumn({ name: "ban_ids" })
@OneToMany(() => Ban, (ban: Ban) => ban.guild, {
cascade: true,
- orphanedRowAction: "delete",
+ orphanedRowAction: "delete"
})
bans: Ban[];
@@ -70,7 +63,7 @@ export class Guild extends BaseClass {
banner?: string;
@Column({ nullable: true })
- default_message_notifications?: number;
+ default_message_notifications?: number = Config.get().defaults.guild.defaultMessageNotifications;
@Column({ nullable: true })
description?: string;
@@ -79,7 +72,7 @@ export class Guild extends BaseClass {
discovery_splash?: string;
@Column({ nullable: true })
- explicit_content_filter?: number;
+ explicit_content_filter?: number = Config.get().defaults.guild.explicitContentFilter;
@Column({ type: "simple-array" })
features: string[]; //TODO use enum
@@ -95,24 +88,24 @@ export class Guild extends BaseClass {
large?: boolean;
@Column({ nullable: true })
- max_members?: number; // e.g. default 100.000
+ max_members?: number = Config.get().limits.guild.maxMembers; // e.g. default 100.000
@Column({ nullable: true })
- max_presences?: number;
+ max_presences?: number = Config.get().defaults.guild.maxPresences;
@Column({ nullable: true })
- max_video_channel_users?: number; // ? default: 25, is this max 25 streaming or watching
+ max_video_channel_users?: number = Config.get().defaults.guild.maxVideoChannelUsers; // ? default: 25, is this max 25 streaming or watching
@Column({ nullable: true })
- member_count?: number;
+ member_count?: number = 0;
@Column({ nullable: true })
- presence_count?: number; // users online
+ presence_count?: number = 0; // users online
@OneToMany(() => Member, (member: Member) => member.guild, {
cascade: true,
orphanedRowAction: "delete",
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
members: Member[];
@@ -120,14 +113,14 @@ export class Guild extends BaseClass {
@OneToMany(() => Role, (role: Role) => role.guild, {
cascade: true,
orphanedRowAction: "delete",
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
roles: Role[];
@JoinColumn({ name: "channel_ids" })
@OneToMany(() => Channel, (channel: Channel) => channel.guild, {
cascade: true,
- orphanedRowAction: "delete",
+ orphanedRowAction: "delete"
})
channels: Channel[];
@@ -143,7 +136,7 @@ export class Guild extends BaseClass {
@OneToMany(() => Emoji, (emoji: Emoji) => emoji.guild, {
cascade: true,
orphanedRowAction: "delete",
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
emojis: Emoji[];
@@ -151,7 +144,7 @@ export class Guild extends BaseClass {
@OneToMany(() => Sticker, (sticker: Sticker) => sticker.guild, {
cascade: true,
orphanedRowAction: "delete",
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
stickers: Sticker[];
@@ -159,7 +152,7 @@ export class Guild extends BaseClass {
@OneToMany(() => Invite, (invite: Invite) => invite.guild, {
cascade: true,
orphanedRowAction: "delete",
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
invites: Invite[];
@@ -167,7 +160,7 @@ export class Guild extends BaseClass {
@OneToMany(() => VoiceState, (voicestate: VoiceState) => voicestate.guild, {
cascade: true,
orphanedRowAction: "delete",
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
voice_states: VoiceState[];
@@ -175,7 +168,7 @@ export class Guild extends BaseClass {
@OneToMany(() => Webhook, (webhook: Webhook) => webhook.guild, {
cascade: true,
orphanedRowAction: "delete",
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
webhooks: Webhook[];
@@ -269,7 +262,7 @@ export class Guild extends BaseClass {
@Column({ nullable: true })
nsfw?: boolean;
-
+
// TODO: nested guilds
@Column({ nullable: true })
parent?: string;
@@ -277,15 +270,14 @@ export class Guild extends BaseClass {
// only for developer portal
permissions?: number;
- static async createGuild(body: {
- name?: string;
- icon?: string | null;
- owner_id?: string;
- channels?: Partial<Channel>[];
- }) {
+ //new guild settings, 11/08/2022:
+ @Column({ nullable: true })
+ premium_progress_bar_enabled: boolean = false;
+
+ 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({
+ const guild: Guild = OrmUtils.mergeDeep(new Guild(), {
name: body.name || "Fosscord",
icon: await handleFile(`/icons/${guild_id}`, body.icon as string),
region: Config.get().regions.default,
@@ -313,14 +305,15 @@ export class Guild extends BaseClass {
welcome_screen: {
enabled: false,
description: "Fill in your description",
- welcome_channels: [],
+ welcome_channels: []
},
- widget_enabled: true, // NB: don't set it as false to prevent artificial restrictions
- }).save();
+ widget_enabled: true // NB: don't set it as false to prevent artificial restrictions
+ });
+ await guild.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({
+ let role: Role = OrmUtils.mergeDeep(new Role(), {
id: guild_id,
guild_id: guild_id,
color: 0,
@@ -333,7 +326,8 @@ export class Guild extends BaseClass {
position: 0,
icon: null,
unicode_emoji: null
- }).save();
+ });
+ await role.save();
if (!body.channels || !body.channels.length) body.channels = [{ id: "01", type: 0, name: "general" }];
@@ -346,15 +340,15 @@ export class Guild extends BaseClass {
});
for (const channel of body.channels?.sort((a, b) => (a.parent_id ? 1 : -1))) {
- var id = ids.get(channel.id) || Snowflake.generate();
+ let id = ids.get(channel.id) || Snowflake.generate();
- var parent_id = ids.get(channel.parent_id);
+ let 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,
+ skipEventEmit: true
});
}
diff --git a/util/src/entities/Invite.ts b/src/util/entities/Invite.ts
index 6ac64ddc..f6ba85d7 100644
--- a/util/src/entities/Invite.ts
+++ b/src/util/entities/Invite.ts
@@ -1,8 +1,9 @@
-import { Column, Entity, JoinColumn, ManyToOne, RelationId, PrimaryColumn } from "typeorm";
-import { Member } from "./Member";
+import { random } from "@fosscord/api";
+import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn, RelationId } from "typeorm";
import { BaseClassWithoutId } from "./BaseClass";
import { Channel } from "./Channel";
import { Guild } from "./Guild";
+import { Member } from "./Member";
import { User } from "./User";
export const PublicInviteRelation = ["inviter", "guild", "channel"];
@@ -10,13 +11,13 @@ export const PublicInviteRelation = ["inviter", "guild", "channel"];
@Entity("invites")
export class Invite extends BaseClassWithoutId {
@PrimaryColumn()
- code: string;
+ code: string = random();
@Column()
- temporary: boolean;
+ temporary: boolean = true;
@Column()
- uses: number;
+ uses: number = 0;
@Column()
max_uses: number;
@@ -25,7 +26,7 @@ export class Invite extends BaseClassWithoutId {
max_age: number;
@Column()
- created_at: Date;
+ created_at: Date = new Date();
@Column()
expires_at: Date;
@@ -36,7 +37,7 @@ export class Invite extends BaseClassWithoutId {
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
guild: Guild;
@@ -46,7 +47,7 @@ export class Invite extends BaseClassWithoutId {
@JoinColumn({ name: "channel_id" })
@ManyToOne(() => Channel, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
channel: Channel;
@@ -55,7 +56,9 @@ export class Invite extends BaseClassWithoutId {
inviter_id: string;
@JoinColumn({ name: "inviter_id" })
- @ManyToOne(() => User)
+ @ManyToOne(() => User, {
+ onDelete: "CASCADE"
+ })
inviter: User;
@Column({ nullable: true })
@@ -64,7 +67,7 @@ export class Invite extends BaseClassWithoutId {
@JoinColumn({ name: "target_user_id" })
@ManyToOne(() => User, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
target_user?: string; // could be used for "User specific invites" https://github.com/fosscord/fosscord/issues/62
@@ -75,7 +78,7 @@ export class Invite extends BaseClassWithoutId {
vanity_url?: boolean;
static async joinGuild(user_id: string, code: string) {
- const invite = await Invite.findOneOrFail({ code });
+ const invite = await Invite.findOneOrFail({ where: { code } });
if (invite.uses++ >= invite.max_uses && invite.max_uses !== 0) await Invite.delete({ code });
else await invite.save();
diff --git a/util/src/entities/Member.ts b/src/util/entities/Member.ts
index a6798a7c..f67ec02c 100644
--- a/util/src/entities/Member.ts
+++ b/src/util/entities/Member.ts
@@ -1,32 +1,14 @@
-import { PublicUser, User } from "./User";
-import { Message } from "./Message";
-import { BaseClass } from "./BaseClass";
-import {
- Column,
- Entity,
- Index,
- JoinColumn,
- JoinTable,
- ManyToMany,
- ManyToOne,
- PrimaryGeneratedColumn,
- RelationId,
-} from "typeorm";
-import { Guild } from "./Guild";
+import { Column, Entity, Index, JoinColumn, JoinTable, ManyToMany, ManyToOne, PrimaryGeneratedColumn, RelationId } from "typeorm";
+import { Ban, PublicGuildRelations, Message } from ".";
+import { GuildCreateEvent, GuildDeleteEvent, GuildMemberAddEvent, GuildMemberRemoveEvent, GuildMemberUpdateEvent, MessageCreateEvent } from "../interfaces";
import { Config, emitEvent } from "../util";
-import {
- GuildCreateEvent,
- GuildDeleteEvent,
- GuildMemberAddEvent,
- GuildMemberRemoveEvent,
- GuildMemberUpdateEvent,
- MessageCreateEvent,
-} from "../interfaces";
-import { HTTPError } from "lambert-server";
-import { Role } from "./Role";
-import { BaseClassWithoutId } from "./BaseClass";
-import { Ban, PublicGuildRelations } from ".";
import { DiscordApiErrors } from "../util/Constants";
+import { HTTPError } from "../util/imports/HTTPError";
+import { OrmUtils } from "../util/imports/OrmUtils";
+import { BaseClassWithoutId } from "./BaseClass";
+import { Guild } from "./Guild";
+import { Role } from "./Role";
+import { PublicUser, User } from "./User";
export const MemberPrivateProjection: (keyof Member)[] = [
"id",
@@ -41,7 +23,7 @@ export const MemberPrivateProjection: (keyof Member)[] = [
"premium_since",
"roles",
"settings",
- "user",
+ "user"
];
@Entity("members")
@@ -56,7 +38,7 @@ export class Member extends BaseClassWithoutId {
@JoinColumn({ name: "id" })
@ManyToOne(() => User, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
user: User;
@@ -66,7 +48,7 @@ export class Member extends BaseClassWithoutId {
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
guild: Guild;
@@ -78,8 +60,8 @@ export class Member extends BaseClassWithoutId {
joinColumn: { name: "index", referencedColumnName: "index" },
inverseJoinColumn: {
name: "role_id",
- referencedColumnName: "id",
- },
+ referencedColumnName: "id"
+ }
})
@ManyToMany(() => Role, { cascade: true })
roles: Role[];
@@ -87,8 +69,8 @@ export class Member extends BaseClassWithoutId {
@Column()
joined_at: Date;
- @Column({ type: "bigint", nullable: true })
- premium_since?: number;
+ @Column({ nullable: true })
+ premium_since?: Date;
@Column()
deaf: boolean;
@@ -119,35 +101,37 @@ export class Member extends BaseClassWithoutId {
// 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;
+ if (await Member.count({ where: { 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 } });
+ const guild = await Guild.findOneOrFail({ select: ["owner_id", "member_count"], 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
+ //TODO: check for bugs
+ if (guild.member_count) guild.member_count--;
return Promise.all([
Member.delete({
id: user_id,
- guild_id,
+ guild_id
}),
- Guild.decrement({ id: guild_id }, "member_count", -1),
+ //Guild.decrement({ id: guild_id }, "member_count", -1),
emitEvent({
event: "GUILD_DELETE",
data: {
- id: guild_id,
+ id: guild_id
},
- user_id: user_id,
+ user_id: user_id
} as GuildDeleteEvent),
emitEvent({
event: "GUILD_MEMBER_REMOVE",
data: { guild_id, user: member.user },
- guild_id,
- } as GuildMemberRemoveEvent),
+ guild_id
+ } as GuildMemberRemoveEvent)
]);
}
@@ -157,11 +141,11 @@ export class Member extends BaseClassWithoutId {
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"],
+ select: ["index"]
}),
- Role.findOneOrFail({ where: { id: role_id, guild_id }, select: ["id"] }),
+ Role.findOneOrFail({ where: { id: role_id, guild_id }, select: ["id"] })
]);
- member.roles.push(new Role({ id: role_id }));
+ member.roles.push(OrmUtils.mergeDeep(new Role(), { id: role_id }));
await Promise.all([
member.save(),
@@ -170,10 +154,10 @@ export class Member extends BaseClassWithoutId {
data: {
guild_id,
user: member.user,
- roles: member.roles.map((x) => x.id),
+ roles: member.roles.map((x) => x.id)
},
- guild_id,
- } as GuildMemberUpdateEvent),
+ guild_id
+ } as GuildMemberUpdateEvent)
]);
}
@@ -183,9 +167,9 @@ export class Member extends BaseClassWithoutId {
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"],
+ select: ["index"]
}),
- await Role.findOneOrFail({ id: role_id, guild_id }),
+ await Role.findOneOrFail({ where: { id: role_id, guild_id } })
]);
member.roles = member.roles.filter((x) => x.id == role_id);
@@ -196,10 +180,10 @@ export class Member extends BaseClassWithoutId {
data: {
guild_id,
user: member.user,
- roles: member.roles.map((x) => x.id),
+ roles: member.roles.map((x) => x.id)
},
- guild_id,
- } as GuildMemberUpdateEvent),
+ guild_id
+ } as GuildMemberUpdateEvent)
]);
}
@@ -207,9 +191,9 @@ export class Member extends BaseClassWithoutId {
const member = await Member.findOneOrFail({
where: {
id: user_id,
- guild_id,
+ guild_id
},
- relations: ["user"],
+ relations: ["user"]
});
member.nick = nickname;
@@ -221,10 +205,10 @@ export class Member extends BaseClassWithoutId {
data: {
guild_id,
user: member.user,
- nick: nickname,
+ nick: nickname
},
- guild_id,
- } as GuildMemberUpdateEvent),
+ guild_id
+ } as GuildMemberUpdateEvent)
]);
}
@@ -235,19 +219,19 @@ export class Member extends BaseClassWithoutId {
throw DiscordApiErrors.USER_BANNED;
}
const { maxGuilds } = Config.get().limits.user;
- const guild_count = await Member.count({ id: user_id });
+ const guild_count = await Member.count({ where: { 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,
+ id: guild_id
},
- relations: [ ...PublicGuildRelations, "system_channel" ],
+ relations: PublicGuildRelations
});
- if (await Member.count({ id: user.id, guild: { id: guild_id } }))
+ if (await Member.count({ where: { id: user.id, guild: { id: guild_id } } }))
throw new HTTPError("You are already a member of this guild", 400);
const member = {
@@ -256,16 +240,17 @@ export class Member extends BaseClassWithoutId {
nick: undefined,
roles: [guild_id], // @everyone role
joined_at: new Date(),
- premium_since: (new Date()).getTime(),
+ premium_since: null,
deaf: false,
mute: false,
- pending: false,
+ pending: false
};
-
+ //TODO: check for bugs
+ if (guild.member_count) guild.member_count++;
await Promise.all([
- new Member({
+ OrmUtils.mergeDeep(new Member(), {
...member,
- roles: [new Role({ id: guild_id })],
+ roles: [OrmUtils.mergeDeep(new Role(), { id: guild_id })],
// read_state: {},
settings: {
channel_overrides: [],
@@ -274,19 +259,19 @@ export class Member extends BaseClassWithoutId {
muted: false,
suppress_everyone: false,
suppress_roles: false,
- version: 0,
- },
+ version: 0
+ }
// Member.save is needed because else the roles relations wouldn't be updated
}).save(),
- Guild.increment({ id: guild_id }, "member_count", 1),
+ //Guild.increment({ id: guild_id }, "member_count", 1),
emitEvent({
event: "GUILD_MEMBER_ADD",
data: {
...member,
user,
- guild_id,
+ guild_id
},
- guild_id,
+ guild_id
} as GuildMemberAddEvent),
emitEvent({
event: "GUILD_CREATE",
@@ -299,15 +284,15 @@ export class Member extends BaseClassWithoutId {
joined_at: member.joined_at,
presences: [],
stage_instances: [],
- threads: [],
+ threads: []
},
- user_id,
- } as GuildCreateEvent),
+ user_id
+ } as GuildCreateEvent)
]);
if (guild.system_channel_id) {
// send welcome message
- const message = new Message({
+ const message = OrmUtils.mergeDeep(new Message(), {
type: 7,
guild_id: guild.id,
channel_id: guild.system_channel_id,
@@ -349,16 +334,7 @@ export interface MuteConfig {
selected_time_window: number;
}
-export type PublicMemberKeys =
- | "id"
- | "guild_id"
- | "nick"
- | "roles"
- | "joined_at"
- | "pending"
- | "deaf"
- | "mute"
- | "premium_since";
+export type PublicMemberKeys = "id" | "guild_id" | "nick" | "roles" | "joined_at" | "pending" | "deaf" | "mute" | "premium_since";
export const PublicMemberProjection: PublicMemberKeys[] = [
"id",
@@ -369,7 +345,7 @@ export const PublicMemberProjection: PublicMemberKeys[] = [
"pending",
"deaf",
"mute",
- "premium_since",
+ "premium_since"
];
// @ts-ignore
diff --git a/util/src/entities/Message.ts b/src/util/entities/Message.ts
index e18cf691..8122b532 100644
--- a/util/src/entities/Message.ts
+++ b/src/util/entities/Message.ts
@@ -1,29 +1,15 @@
-import { User } from "./User";
-import { Member } from "./Member";
-import { Role } from "./Role";
-import { Channel } from "./Channel";
+import { Column, CreateDateColumn, Entity, Index, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToMany, RelationId } from "typeorm";
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 { Attachment } from "./Attachment";
import { BaseClass } from "./BaseClass";
+import { Channel } from "./Channel";
import { Guild } from "./Guild";
-import { Webhook } from "./Webhook";
+import { Member } from "./Member";
+import { Role } from "./Role";
import { Sticker } from "./Sticker";
-import { Attachment } from "./Attachment";
+import { User } from "./User";
+import { Webhook } from "./Webhook";
export enum MessageType {
DEFAULT = 0,
@@ -63,7 +49,7 @@ export class Message extends BaseClass {
@JoinColumn({ name: "channel_id" })
@ManyToOne(() => Channel, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
channel: Channel;
@@ -73,7 +59,7 @@ export class Message extends BaseClass {
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
guild?: Guild;
@@ -84,7 +70,7 @@ export class Message extends BaseClass {
@JoinColumn({ name: "author_id", referencedColumnName: "id" })
@ManyToOne(() => User, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
author?: User;
@@ -94,7 +80,7 @@ export class Message extends BaseClass {
@JoinColumn({ name: "member_id", referencedColumnName: "id" })
@ManyToOne(() => User, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
member?: Member;
@@ -148,7 +134,7 @@ export class Message extends BaseClass {
@OneToMany(() => Attachment, (attachment: Attachment) => attachment.message, {
cascade: true,
- orphanedRowAction: "delete",
+ orphanedRowAction: "delete"
})
attachments?: Attachment[];
@@ -213,7 +199,7 @@ export interface MessageComponent {
export enum MessageComponentType {
Script = 0, // self command script
ActionRow = 1,
- Button = 2,
+ Button = 2
}
export interface Embed {
@@ -254,7 +240,7 @@ export enum EmbedType {
video = "video",
gifv = "gifv",
article = "article",
- link = "link",
+ link = "link"
}
export interface EmbedImage {
diff --git a/util/src/entities/Migration.ts b/src/util/entities/Migration.ts
index 3f39ae72..626ec429 100644
--- a/util/src/entities/Migration.ts
+++ b/src/util/entities/Migration.ts
@@ -1,9 +1,7 @@
import { Column, Entity, ObjectIdColumn, PrimaryGeneratedColumn } from "typeorm";
import { BaseClassWithoutId } from ".";
-export const PrimaryIdAutoGenerated = process.env.DATABASE?.startsWith("mongodb")
- ? ObjectIdColumn
- : PrimaryGeneratedColumn;
+export const PrimaryIdAutoGenerated = process.env.DATABASE?.startsWith("mongodb") ? ObjectIdColumn : PrimaryGeneratedColumn;
@Entity("migrations")
export class Migration extends BaseClassWithoutId {
diff --git a/util/src/entities/RateLimit.ts b/src/util/entities/RateLimit.ts
index f5916f6b..f5916f6b 100644
--- a/util/src/entities/RateLimit.ts
+++ b/src/util/entities/RateLimit.ts
diff --git a/util/src/entities/ReadState.ts b/src/util/entities/ReadState.ts
index b915573b..77d2c08a 100644
--- a/util/src/entities/ReadState.ts
+++ b/src/util/entities/ReadState.ts
@@ -1,7 +1,6 @@
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
@@ -17,7 +16,7 @@ export class ReadState extends BaseClass {
@JoinColumn({ name: "channel_id" })
@ManyToOne(() => Channel, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
channel: Channel;
@@ -27,14 +26,14 @@ export class ReadState extends BaseClass {
@JoinColumn({ name: "user_id" })
@ManyToOne(() => User, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
user: User;
// fully read marker
@Column({ nullable: true })
- last_message_id: string;
-
+ last_message_id: string;
+
// public read receipt
@Column({ nullable: true })
public_ack: string;
diff --git a/util/src/entities/Recipient.ts b/src/util/entities/Recipient.ts
index a945f938..fc9e629b 100644
--- a/util/src/entities/Recipient.ts
+++ b/src/util/entities/Recipient.ts
@@ -9,7 +9,7 @@ export class Recipient extends BaseClass {
@JoinColumn({ name: "channel_id" })
@ManyToOne(() => require("./Channel").Channel, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
channel: import("./Channel").Channel;
@@ -19,7 +19,7 @@ export class Recipient extends BaseClass {
@JoinColumn({ name: "user_id" })
@ManyToOne(() => require("./User").User, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
user: import("./User").User;
diff --git a/util/src/entities/Relationship.ts b/src/util/entities/Relationship.ts
index c3592c76..b55d9e64 100644
--- a/util/src/entities/Relationship.ts
+++ b/src/util/entities/Relationship.ts
@@ -6,7 +6,7 @@ export enum RelationshipType {
outgoing = 4,
incoming = 3,
blocked = 2,
- friends = 1,
+ friends = 1
}
@Entity("relationships")
@@ -18,7 +18,7 @@ export class Relationship extends BaseClass {
@JoinColumn({ name: "from_id" })
@ManyToOne(() => User, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
from: User;
@@ -28,7 +28,7 @@ export class Relationship extends BaseClass {
@JoinColumn({ name: "to_id" })
@ManyToOne(() => User, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
to: User;
@@ -43,7 +43,7 @@ export class Relationship extends BaseClass {
id: this.to?.id || this.to_id,
type: this.type,
nickname: this.nickname,
- user: this.to?.toPublicUser(),
+ user: this.to?.toPublicUser()
};
}
}
diff --git a/util/src/entities/Role.ts b/src/util/entities/Role.ts
index 4b721b5b..b1fd9bb1 100644
--- a/util/src/entities/Role.ts
+++ b/src/util/entities/Role.ts
@@ -11,7 +11,7 @@ export class Role extends BaseClass {
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
guild: Guild;
diff --git a/util/src/entities/Session.ts b/src/util/entities/Session.ts
index 969efa89..0cb4c309 100644
--- a/util/src/entities/Session.ts
+++ b/src/util/entities/Session.ts
@@ -1,8 +1,8 @@
-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";
+import { Status } from "../interfaces/Status";
+import { BaseClass } from "./BaseClass";
+import { User } from "./User";
//TODO we need to remove all sessions on server start because if the server crashes without closing websockets it won't delete them
@@ -14,7 +14,7 @@ export class Session extends BaseClass {
@JoinColumn({ name: "user_id" })
@ManyToOne(() => User, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
user: User;
@@ -37,10 +37,4 @@ export class Session extends BaseClass {
status: Status; //TODO enum
}
-export const PrivateSessionProjection: (keyof Session)[] = [
- "user_id",
- "session_id",
- "activities",
- "client_info",
- "status",
-];
+export const PrivateSessionProjection: (keyof Session)[] = ["user_id", "session_id", "activities", "client_info", "status"];
diff --git a/util/src/entities/Sticker.ts b/src/util/entities/Sticker.ts
index 37bc6fbe..69836e62 100644
--- a/util/src/entities/Sticker.ts
+++ b/src/util/entities/Sticker.ts
@@ -1,18 +1,18 @@
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
-import { User } from "./User";
import { BaseClass } from "./BaseClass";
import { Guild } from "./Guild";
+import { User } from "./User";
export enum StickerType {
STANDARD = 1,
- GUILD = 2,
+ GUILD = 2
}
export enum StickerFormatType {
GIF = 0, // gif is a custom format type and not in discord spec
PNG = 1,
APNG = 2,
- LOTTIE = 3,
+ LOTTIE = 3
}
@Entity("stickers")
@@ -36,7 +36,7 @@ export class Sticker extends BaseClass {
@JoinColumn({ name: "pack_id" })
@ManyToOne(() => require("./StickerPack").StickerPack, {
onDelete: "CASCADE",
- nullable: true,
+ nullable: true
})
pack: import("./StickerPack").StickerPack;
@@ -45,7 +45,7 @@ export class Sticker extends BaseClass {
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
guild?: Guild;
@@ -54,7 +54,7 @@ export class Sticker extends BaseClass {
@JoinColumn({ name: "user_id" })
@ManyToOne(() => User, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
user?: User;
diff --git a/util/src/entities/StickerPack.ts b/src/util/entities/StickerPack.ts
index ec8c69a2..4619af34 100644
--- a/util/src/entities/StickerPack.ts
+++ b/src/util/entities/StickerPack.ts
@@ -1,4 +1,4 @@
-import { Column, Entity, JoinColumn, ManyToOne, OneToMany, OneToOne, RelationId } from "typeorm";
+import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm";
import { Sticker } from ".";
import { BaseClass } from "./BaseClass";
@@ -15,7 +15,7 @@ export class StickerPack extends BaseClass {
@OneToMany(() => Sticker, (sticker: Sticker) => sticker.pack, {
cascade: true,
- orphanedRowAction: "delete",
+ orphanedRowAction: "delete"
})
stickers: Sticker[];
diff --git a/util/src/entities/Team.ts b/src/util/entities/Team.ts
index 22140b7f..1d2d7002 100644
--- a/util/src/entities/Team.ts
+++ b/src/util/entities/Team.ts
@@ -1,4 +1,4 @@
-import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToMany, RelationId } from "typeorm";
+import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm";
import { BaseClass } from "./BaseClass";
import { TeamMember } from "./TeamMember";
import { User } from "./User";
@@ -10,7 +10,7 @@ export class Team extends BaseClass {
@JoinColumn({ name: "member_ids" })
@OneToMany(() => TeamMember, (member: TeamMember) => member.team, {
- orphanedRowAction: "delete",
+ orphanedRowAction: "delete"
})
members: TeamMember[];
diff --git a/util/src/entities/TeamMember.ts b/src/util/entities/TeamMember.ts
index b726e1e8..d11ebf95 100644
--- a/util/src/entities/TeamMember.ts
+++ b/src/util/entities/TeamMember.ts
@@ -4,7 +4,7 @@ import { User } from "./User";
export enum TeamMemberState {
INVITED = 1,
- ACCEPTED = 2,
+ ACCEPTED = 2
}
@Entity("team_members")
@@ -21,7 +21,7 @@ export class TeamMember extends BaseClass {
@JoinColumn({ name: "team_id" })
@ManyToOne(() => require("./Team").Team, (team: import("./Team").Team) => team.members, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
team: import("./Team").Team;
@@ -31,7 +31,7 @@ export class TeamMember extends BaseClass {
@JoinColumn({ name: "user_id" })
@ManyToOne(() => User, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
user: User;
}
diff --git a/util/src/entities/Template.ts b/src/util/entities/Template.ts
index 1d952283..1d952283 100644
--- a/util/src/entities/Template.ts
+++ b/src/util/entities/Template.ts
diff --git a/util/src/entities/User.ts b/src/util/entities/User.ts
index 9b1c494e..1237b676 100644
--- a/util/src/entities/User.ts
+++ b/src/util/entities/User.ts
@@ -1,10 +1,11 @@
-import { Column, Entity, FindOneOptions, JoinColumn, ManyToMany, OneToMany, RelationId } from "typeorm";
-import { BaseClass } from "./BaseClass";
+import { Column, Entity, FindOneOptions, FindOptionsSelectByString, JoinColumn, OneToMany, OneToOne } from "typeorm";
+import { Member, Session, UserSettings } from ".";
+import { Config, FieldErrors, Snowflake, trimSpecial } from "..";
import { BitField } from "../util/BitField";
-import { Relationship } from "./Relationship";
+import { OrmUtils } from "../util/imports/OrmUtils";
+import { BaseClass } from "./BaseClass";
import { ConnectedAccount } from "./ConnectedAccount";
-import { Config, FieldErrors, Snowflake, trimSpecial } from "..";
-import { Member, Session } from ".";
+import { Relationship } from "./Relationship";
export enum PublicUserEnum {
username,
@@ -16,7 +17,7 @@ export enum PublicUserEnum {
banner,
bio,
bot,
- premium_since,
+ premium_since
}
export type PublicUserKeys = keyof typeof PublicUserEnum;
@@ -30,17 +31,15 @@ export enum PrivateUserEnum {
premium,
premium_type,
disabled,
- settings,
+ settings
// locale
}
export type PrivateUserKeys = keyof typeof PrivateUserEnum | PublicUserKeys;
-export const PublicUserProjection = Object.values(PublicUserEnum).filter(
- (x) => typeof x === "string"
-) as 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"),
+ ...Object.values(PrivateUserEnum).filter((x) => typeof x === "string")
] as PrivateUserKeys[];
// Private user data that should never get sent to the client
@@ -82,58 +81,64 @@ export class User extends BaseClass {
phone?: string; // phone number of the user
@Column({ select: false })
- desktop: boolean; // if the user has desktop app installed
+ desktop: boolean = false; // if the user has desktop app installed
@Column({ select: false })
- mobile: boolean; // if the user has mobile app installed
+ mobile: boolean = false; // if the user has mobile app installed
@Column()
- premium: boolean; // if user bought individual premium
-
- @Column()
- premium_type: number; // individual premium level
+ premium: boolean = Config.get().defaults.user.premium; // if user bought individual premium
@Column()
- bot: boolean; // if user is bot
+ premium_type: number = Config.get().defaults.user.premium_type; // individual premium level
@Column()
+ bot: boolean = false; // if user is bot
+
+ @Column({ nullable: true })
bio: string; // short description of the user (max 190 chars -> should be configurable)
@Column()
- system: boolean; // shouldn't be used, the api sends this field type true, if the generated message comes from a system generated author
+ system: boolean = false; // shouldn't be used, the api sends this field type true, if the generated message comes from a system generated author
@Column({ select: false })
- nsfw_allowed: boolean; // if the user can do age-restricted actions (NSFW channels/guilds/commands)
-
- @Column({ select: false })
+ nsfw_allowed: boolean = true; // if the user can do age-restricted actions (NSFW channels/guilds/commands) // TODO: depending on age
+
+ @Column({ select: false, nullable: true })
mfa_enabled: boolean; // if multi factor authentication is enabled
+ @Column({ select: false, nullable: true })
+ totp_secret?: string;
+
+ @Column({ nullable: true, select: false })
+ totp_last_ticket?: string;
+
@Column()
- created_at: Date; // registration date
+ created_at: Date = new Date(); // registration date
@Column({ nullable: true })
- premium_since: Date; // premium date
+ premium_since: Date = new Date(); // premium date
@Column({ select: false })
- verified: boolean; // if the user is offically verified
+ verified: boolean = Config.get().defaults.user.verified; // if the user is offically verified
@Column()
- disabled: boolean; // if the account is disabled
+ disabled: boolean = false; // if the account is disabled
@Column()
- deleted: boolean; // if the user was deleted
+ deleted: boolean = false; // if the user was deleted
@Column({ nullable: true, select: false })
email?: string; // email of the user
@Column()
- flags: string; // UserFlags
+ flags: string = "0"; // UserFlags // TODO: generate
@Column()
- public_flags: number;
+ public_flags: number = 0;
@Column({ type: "bigint" })
- rights: string; // Rights
+ rights: string = Config.get().register.defaultRights; // Rights
@OneToMany(() => Session, (session: Session) => session.user)
sessions: Session[];
@@ -141,14 +146,14 @@ export class User extends BaseClass {
@JoinColumn({ name: "relationship_ids" })
@OneToMany(() => Relationship, (relationship: Relationship) => relationship.from, {
cascade: true,
- orphanedRowAction: "delete",
+ orphanedRowAction: "delete"
})
relationships: Relationship[];
@JoinColumn({ name: "connected_account_ids" })
@OneToMany(() => ConnectedAccount, (account: ConnectedAccount) => account.user, {
cascade: true,
- orphanedRowAction: "delete",
+ orphanedRowAction: "delete"
})
connected_accounts: ConnectedAccount[];
@@ -159,17 +164,29 @@ export class User extends BaseClass {
};
@Column({ type: "simple-array", select: false })
- fingerprints: string[]; // array of fingerprints -> used to prevent multiple accounts
+ fingerprints: string[] = []; // array of fingerprints -> used to prevent multiple accounts
- @Column({ type: "simple-json", select: false })
+ @OneToOne(() => UserSettings, {
+ cascade: true,
+ orphanedRowAction: "delete",
+ eager: false
+ })
+ @JoinColumn()
settings: UserSettings;
-
+
// workaround to prevent fossord-unaware clients from deleting settings not used by them
@Column({ type: "simple-json", select: false })
- extended_settings: string;
+ extended_settings: string = "{}";
@Column({ type: "simple-json" })
- notes: { [key: string]: string }; //key is ID of user
+ notes: { [key: string]: string } = {}; //key is ID of user
+
+ async save(): Promise<any> {
+ if (!this.settings) this.settings = new UserSettings();
+ this.settings.id = this.id;
+ //await this.settings.save();
+ return super.save();
+ }
toPublicUser() {
const user: any = {};
@@ -180,19 +197,17 @@ export class User extends BaseClass {
}
static async getPublicUser(user_id: string, opts?: FindOneOptions<User>) {
- return await User.findOneOrFail(
- { id: user_id },
- {
- ...opts,
- select: [...PublicUserProjection, ...(opts?.select || [])],
- }
- );
+ return await User.findOneOrFail({
+ where: { id: user_id },
+ select: [...PublicUserProjection, ...((opts?.select as FindOptionsSelectByString<User>) || [])],
+ ...opts
+ });
}
- private static async generateDiscriminator(username: string): Promise<string | undefined> {
+ public 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)));
@@ -223,7 +238,7 @@ export class User extends BaseClass {
username,
password,
date_of_birth,
- req,
+ req
}: {
username: string;
password?: string;
@@ -240,48 +255,30 @@ export class User extends BaseClass {
throw FieldErrors({
username: {
code: "USERNAME_TOO_MANY_USERS",
- message: req.t("auth:register.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 language = req?.language === "en" ? "en-US" : req?.language || "en-US";
- const user = new User({
- created_at: new Date(),
+ const user = OrmUtils.mergeDeep(new User(), {
+ //required:
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(),
+ valid_tokens_since: new Date()
},
- settings: { ...defaultSettings, locale: language },
- extended_settings: {},
- fingerprints: [],
- notes: {},
+ settings: { ...new UserSettings(), locale: language }
});
+ //await (user.settings as UserSettings).save();
await user.save();
setImmediate(async () => {
@@ -296,85 +293,6 @@ export class User extends BaseClass {
}
}
-export const defaultSettings: UserSettings = {
- afk_timeout: 3600,
- allow_accessibility_detection: true,
- animate_emoji: true,
- animate_stickers: 0,
- contact_sync_enabled: false,
- convert_emoticons: false,
- custom_status: null,
- default_guilds_restricted: false,
- detect_platform_accounts: false,
- developer_mode: true,
- disable_games_tab: true,
- enable_tts_command: false,
- explicit_content_filter: 0,
- friend_source_flags: { all: true },
- gateway_connected: false,
- gif_auto_play: true,
- guild_folders: [],
- guild_positions: [],
- inline_attachment_media: true,
- inline_embed_media: true,
- locale: "en-US",
- message_display_compact: 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 {
@@ -398,6 +316,6 @@ export class UserFlags extends BitField {
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),
+ BOT_HTTP_INTERACTIONS: BigInt(1) << BigInt(19)
};
}
diff --git a/util/src/entities/UserGroup.ts b/src/util/entities/UserGroup.ts
index 709b9d0b..08d68a4e 100644
--- a/util/src/entities/UserGroup.ts
+++ b/src/util/entities/UserGroup.ts
@@ -1,7 +1,6 @@
-import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
+import { Column, Entity, JoinColumn, ManyToOne } from "typeorm";
import { BaseClass } from "./BaseClass";
-import { Guild } from "./Guild";
import { User } from "./User";
@Entity("groups")
@@ -11,11 +10,11 @@ export class UserGroup extends BaseClass {
@Column()
hoist: boolean;
-
+
@JoinColumn({ name: "controller", referencedColumnName: "id" })
@ManyToOne(() => User)
controller?: User;
-
+
@Column()
mentionable_by?: string;
@@ -27,11 +26,10 @@ export class UserGroup extends BaseClass {
@Column({ nullable: true })
icon: string;
-
+
@Column({ nullable: true })
parent?: string;
-
- @Column({ type: "simple-array", nullable: true})
- associciations: string[];
+ @Column({ type: "simple-array", nullable: true })
+ associciations: string[];
}
diff --git a/util/src/entities/VoiceState.ts b/src/util/entities/VoiceState.ts
index 75748a01..baf2c687 100644
--- a/util/src/entities/VoiceState.ts
+++ b/src/util/entities/VoiceState.ts
@@ -2,8 +2,8 @@ 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";
+import { User } from "./User";
//https://gist.github.com/vassjozsef/e482c65df6ee1facaace8b3c9ff66145#file-voice_state-ex
@Entity("voice_states")
@@ -14,7 +14,7 @@ export class VoiceState extends BaseClass {
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
guild?: Guild;
@@ -24,7 +24,7 @@ export class VoiceState extends BaseClass {
@JoinColumn({ name: "channel_id" })
@ManyToOne(() => Channel, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
channel: Channel;
@@ -34,7 +34,7 @@ export class VoiceState extends BaseClass {
@JoinColumn({ name: "user_id" })
@ManyToOne(() => User, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
user: User;
diff --git a/util/src/entities/Webhook.ts b/src/util/entities/Webhook.ts
index 89538417..3d94ddb6 100644
--- a/util/src/entities/Webhook.ts
+++ b/src/util/entities/Webhook.ts
@@ -7,7 +7,7 @@ import { User } from "./User";
export enum WebhookType {
Incoming = 1,
- ChannelFollower = 2,
+ ChannelFollower = 2
}
@Entity("webhooks")
@@ -30,7 +30,7 @@ export class Webhook extends BaseClass {
@JoinColumn({ name: "guild_id" })
@ManyToOne(() => Guild, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
guild: Guild;
@@ -40,7 +40,7 @@ export class Webhook extends BaseClass {
@JoinColumn({ name: "channel_id" })
@ManyToOne(() => Channel, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
channel: Channel;
@@ -50,7 +50,7 @@ export class Webhook extends BaseClass {
@JoinColumn({ name: "application_id" })
@ManyToOne(() => Application, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
application: Application;
@@ -60,7 +60,7 @@ export class Webhook extends BaseClass {
@JoinColumn({ name: "user_id" })
@ManyToOne(() => User, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
user: User;
@@ -70,7 +70,7 @@ export class Webhook extends BaseClass {
@JoinColumn({ name: "source_guild_id" })
@ManyToOne(() => Guild, {
- onDelete: "CASCADE",
+ onDelete: "CASCADE"
})
source_guild: Guild;
}
diff --git a/util/src/entities/index.ts b/src/util/entities/index.ts
index f023d5a6..2b91c2ba 100644
--- a/util/src/entities/index.ts
+++ b/src/util/entities/index.ts
@@ -1,10 +1,12 @@
export * from "./Application";
export * from "./Attachment";
export * from "./AuditLog";
+export * from "./BackupCodes";
export * from "./Ban";
export * from "./BaseClass";
export * from "./Categories";
export * from "./Channel";
+export * from "./ClientRelease";
export * from "./Config";
export * from "./ConnectedAccount";
export * from "./Emoji";
@@ -13,6 +15,7 @@ export * from "./Invite";
export * from "./Member";
export * from "./Message";
export * from "./Migration";
+export * from "./Note";
export * from "./RateLimit";
export * from "./ReadState";
export * from "./Recipient";
@@ -25,6 +28,6 @@ export * from "./Team";
export * from "./TeamMember";
export * from "./Template";
export * from "./User";
+export * from "./UserSettings";
export * from "./VoiceState";
export * from "./Webhook";
-export * from "./ClientRelease";
\ No newline at end of file
diff --git a/util/src/index.ts b/src/util/index.ts
index ae0f7e54..b26ed278 100644
--- a/util/src/index.ts
+++ b/src/util/index.ts
@@ -1,6 +1,9 @@
import "reflect-metadata";
-export * from "./util/index";
-export * from "./interfaces/index";
-export * from "./entities/index";
+export * from "./config/index";
export * from "./dtos/index";
+export * from "./entities/index";
+export * from "./interfaces/index";
+export * from "./schemas";
+export * from "./util/index";
+export * from "./util/MFA";
diff --git a/util/src/interfaces/Activity.ts b/src/util/interfaces/Activity.ts
index 43984afd..3b36b4a6 100644
--- a/util/src/interfaces/Activity.ts
+++ b/src/util/interfaces/Activity.ts
@@ -40,5 +40,5 @@ export enum ActivityType {
STREAMING = 1,
LISTENING = 2,
CUSTOM = 4,
- COMPETING = 5,
+ COMPETING = 5
}
diff --git a/util/src/interfaces/Event.ts b/src/util/interfaces/Event.ts
index 416082ed..f97f4615 100644
--- a/util/src/interfaces/Event.ts
+++ b/src/util/interfaces/Event.ts
@@ -1,19 +1,19 @@
-import { PublicUser, User, UserSettings } from "../entities/User";
+import { Activity, Status } from ".";
+import { Sticker, UserSettings } from "..";
+import { ApplicationCommand } from "../entities/Application";
import { Channel } from "../entities/Channel";
-import { Guild } from "../entities/Guild";
-import { Member, PublicMember, UserGuildSettings } from "../entities/Member";
+import { ConnectedAccount } from "../entities/ConnectedAccount";
import { Emoji } from "../entities/Emoji";
-import { Role } from "../entities/Role";
+import { Guild } from "../entities/Guild";
import { Invite } from "../entities/Invite";
+import { PublicMember, UserGuildSettings } from "../entities/Member";
import { Message, PartialEmoji } from "../entities/Message";
+import { RelationshipType } from "../entities/Relationship";
+import { Role } from "../entities/Role";
+import { PublicUser, User } from "../entities/User";
import { VoiceState } from "../entities/VoiceState";
-import { ApplicationCommand } from "../entities/Application";
import { Interaction } from "./Interaction";
-import { ConnectedAccount } from "../entities/ConnectedAccount";
-import { Relationship, RelationshipType } from "../entities/Relationship";
import { Presence } from "./Presence";
-import { Sticker } from "..";
-import { Activity, Status } from ".";
export interface Event {
guild_id?: string;
@@ -93,7 +93,7 @@ export interface ReadyEventData {
};
application?: {
id: string;
- flags: string;
+ flags: number;
};
merged_members?: PublicMember[][];
// probably all users who the user is in contact with
@@ -580,7 +580,7 @@ export enum EVENTEnum {
ApplicationCommandCreate = "APPLICATION_COMMAND_CREATE",
ApplicationCommandUpdate = "APPLICATION_COMMAND_UPDATE",
ApplicationCommandDelete = "APPLICATION_COMMAND_DELETE",
- SessionsReplace = "SESSIONS_REPLACE",
+ SessionsReplace = "SESSIONS_REPLACE"
}
export type EVENT =
diff --git a/util/src/interfaces/Interaction.ts b/src/util/interfaces/Interaction.ts
index 5d3aae24..c53a1ed4 100644
--- a/util/src/interfaces/Interaction.ts
+++ b/src/util/interfaces/Interaction.ts
@@ -14,7 +14,7 @@ export interface Interaction {
export enum InteractionType {
SelfCommand = 0,
Ping = 1,
- ApplicationCommand = 2,
+ ApplicationCommand = 2
}
export enum InteractionResponseType {
@@ -23,7 +23,7 @@ export enum InteractionResponseType {
Acknowledge = 2,
ChannelMessage = 3,
ChannelMessageWithSource = 4,
- AcknowledgeWithSource = 5,
+ AcknowledgeWithSource = 5
}
export interface InteractionApplicationCommandCallbackData {
diff --git a/util/src/interfaces/Presence.ts b/src/util/interfaces/Presence.ts
index 7663891a..5b66139e 100644
--- a/util/src/interfaces/Presence.ts
+++ b/src/util/interfaces/Presence.ts
@@ -1,6 +1,6 @@
-import { ClientStatus, Status } from "./Status";
-import { Activity } from "./Activity";
import { PublicUser } from "../entities/User";
+import { Activity } from "./Activity";
+import { ClientStatus, Status } from "./Status";
export interface Presence {
user: PublicUser;
diff --git a/util/src/interfaces/Status.ts b/src/util/interfaces/Status.ts
index 5d2e1bba..5d2e1bba 100644
--- a/util/src/interfaces/Status.ts
+++ b/src/util/interfaces/Status.ts
diff --git a/util/src/interfaces/index.ts b/src/util/interfaces/index.ts
index ab7fa429..a074030e 100644
--- a/util/src/interfaces/index.ts
+++ b/src/util/interfaces/index.ts
@@ -1,5 +1,5 @@
export * from "./Activity";
-export * from "./Presence";
-export * from "./Interaction";
export * from "./Event";
+export * from "./Interaction";
+export * from "./Presence";
export * from "./Status";
diff --git a/util/src/util/ApiError.ts b/src/util/util/ApiError.ts
index f1a9b4f6..c133e6e7 100644
--- a/util/src/util/ApiError.ts
+++ b/src/util/util/ApiError.ts
@@ -9,8 +9,7 @@ export class ApiError extends Error {
}
withDefaultParams(): ApiError {
- if (this.defaultParams)
- return new ApiError(applyParamsToString(this.message, this.defaultParams), this.code, this.httpStatus);
+ if (this.defaultParams) return new ApiError(applyParamsToString(this.message, this.defaultParams), this.code, this.httpStatus);
return this;
}
diff --git a/util/src/util/Array.ts b/src/util/util/Array.ts
index 5a45d1b5..5a45d1b5 100644
--- a/util/src/util/Array.ts
+++ b/src/util/util/Array.ts
diff --git a/util/src/util/AutoUpdate.ts b/src/util/util/AutoUpdate.ts
index 531bd8b7..08418040 100644
--- a/util/src/util/AutoUpdate.ts
+++ b/src/util/util/AutoUpdate.ts
@@ -1,13 +1,12 @@
-import "missing-native-js-functions";
-import fetch from "node-fetch";
-import ProxyAgent from 'proxy-agent';
-import readline from "readline";
import fs from "fs/promises";
+import fetch from "node-fetch";
import path from "path";
+import ProxyAgent from "proxy-agent";
+import readline from "readline";
const rl = readline.createInterface({
input: process.stdin,
- output: process.stdout,
+ output: process.stdout
});
export function enableAutoUpdate(opts: {
@@ -18,7 +17,7 @@ export function enableAutoUpdate(opts: {
downloadType?: "zip";
}) {
if (!opts.checkInterval) return;
- var interval = 1000 * 60 * 60 * 24;
+ let interval = 1000 * 60 * 60 * 24;
if (typeof opts.checkInterval === "number") opts.checkInterval = 1000 * interval;
const i = setInterval(async () => {
@@ -76,7 +75,7 @@ async function getLatestVersion(url: string) {
try {
const agent = new ProxyAgent();
const response = await fetch(url, { agent });
- const content = await response.json();
+ const content: any = await response.json();
return content.version;
} catch (error) {
throw new Error("[Auto update] check failed for " + url);
diff --git a/util/src/util/BitField.ts b/src/util/util/BitField.ts
index fb887e05..306bfb32 100644
--- a/util/src/util/BitField.ts
+++ b/src/util/util/BitField.ts
@@ -138,6 +138,9 @@ export class BitField {
return bit.map((p) => resolve.call(this, p)).reduce((prev, p) => BigInt(prev) | BigInt(p), BigInt(0));
}
if (typeof bit === "string" && typeof FLAGS[bit] !== "undefined") return FLAGS[bit];
+ if (bit === "0") return BigInt(0); //special case: 0
+ if (typeof bit === "string") return BigInt(bit); //last ditch effort...
+ if (/--debug|--inspect/.test(process.execArgv.join(" "))) debugger; //if you're here, we have an invalid bitfield... if bit is 0, thats fine, I guess...
throw new RangeError("BITFIELD_INVALID: " + bit);
}
}
diff --git a/util/src/util/Categories.ts b/src/util/util/Categories.ts
index a3c69da7..cd706a8a 100644
--- a/util/src/util/Categories.ts
+++ b/src/util/util/Categories.ts
@@ -1 +1 @@
-//TODO: populate default discord categories + init, get and set methods
\ No newline at end of file
+//TODO: populate default discord categories + init, get and set methods
diff --git a/util/src/util/Constants.ts b/src/util/util/Constants.ts
index a5d3fcd2..f7aff26a 100644
--- a/util/src/util/Constants.ts
+++ b/src/util/util/Constants.ts
@@ -6,7 +6,7 @@ export const WSCodes = {
4010: "SHARDING_INVALID",
4011: "SHARDING_REQUIRED",
4013: "INVALID_INTENTS",
- 4014: "DISALLOWED_INTENTS",
+ 4014: "DISALLOWED_INTENTS"
};
/**
@@ -31,7 +31,7 @@ export const WsStatus = {
DISCONNECTED: 5,
WAITING_FOR_GUILDS: 6,
IDENTIFYING: 7,
- RESUMING: 8,
+ RESUMING: 8
};
/**
@@ -48,7 +48,7 @@ export const VoiceStatus = {
CONNECTING: 1,
AUTHENTICATING: 2,
RECONNECTING: 3,
- DISCONNECTED: 4,
+ DISCONNECTED: 4
};
export const OPCodes = {
@@ -63,7 +63,7 @@ export const OPCodes = {
REQUEST_GUILD_MEMBERS: 8,
INVALID_SESSION: 9,
HELLO: 10,
- HEARTBEAT_ACK: 11,
+ HEARTBEAT_ACK: 11
};
export const VoiceOPCodes = {
@@ -75,7 +75,7 @@ export const VoiceOPCodes = {
SPEAKING: 5,
HELLO: 8,
CLIENT_CONNECT: 12,
- CLIENT_DISCONNECT: 13,
+ CLIENT_DISCONNECT: 13
};
export const Events = {
@@ -133,7 +133,7 @@ export const Events = {
SHARD_READY: "shardReady",
SHARD_RESUME: "shardResume",
INVALIDATED: "invalidated",
- RAW: "raw",
+ RAW: "raw"
};
export const ShardEvents = {
@@ -142,7 +142,7 @@ export const ShardEvents = {
INVALID_SESSION: "invalidSession",
READY: "ready",
RESUMED: "resumed",
- ALL_READY: "allReady",
+ ALL_READY: "allReady"
};
/**
@@ -234,7 +234,7 @@ export const WSEvents = keyMirror([
"TYPING_START",
"VOICE_STATE_UPDATE",
"VOICE_SERVER_UPDATE",
- "WEBHOOKS_UPDATE",
+ "WEBHOOKS_UPDATE"
]);
/**
@@ -277,7 +277,7 @@ export const MessageTypes = [
null,
null,
null,
- "REPLY",
+ "REPLY"
];
/**
@@ -286,9 +286,7 @@ export const MessageTypes = [
* * REPLY
* @typedef {string} SystemMessageType
*/
-export const SystemMessageTypes = MessageTypes.filter(
- (type: string | null) => type && type !== "DEFAULT" && type !== "REPLY"
-);
+export const SystemMessageTypes = MessageTypes.filter((type: string | null) => type && type !== "DEFAULT" && type !== "REPLY");
/**
* <info>Bots cannot set a `CUSTOM_STATUS`, it is only for custom statuses received from users</info>
@@ -310,12 +308,12 @@ export const ChannelTypes = {
GROUP: 3,
CATEGORY: 4,
NEWS: 5,
- STORE: 6,
+ STORE: 6
};
export const ClientApplicationAssetTypes = {
SMALL: 1,
- BIG: 2,
+ BIG: 2
};
export const Colors = {
@@ -347,7 +345,7 @@ export const Colors = {
BLURPLE: 0x7289da,
GREYPLE: 0x99aab5,
DARK_BUT_NOT_BLACK: 0x2c2f33,
- NOT_QUITE_BLACK: 0x23272a,
+ NOT_QUITE_BLACK: 0x23272a
};
/**
@@ -556,14 +554,8 @@ export const DiscordApiErrors = {
UNKNOWN_GUILD_SCHEDULED_EVENT_USER: new ApiError("Unknown Guild Scheduled Event User", 10071),
BOT_PROHIBITED_ENDPOINT: new ApiError("Bots cannot use this endpoint", 20001),
BOT_ONLY_ENDPOINT: new ApiError("Only bots can use this endpoint", 20002),
- EXPLICIT_CONTENT_CANNOT_BE_SENT_TO_RECIPIENT: new ApiError(
- "Explicit content cannot be sent to the desired recipient(s)",
- 20009
- ),
- ACTION_NOT_AUTHORIZED_ON_APPLICATION: new ApiError(
- "You are not authorized to perform this action on this application",
- 20012
- ),
+ EXPLICIT_CONTENT_CANNOT_BE_SENT_TO_RECIPIENT: new ApiError("Explicit content cannot be sent to the desired recipient(s)", 20009),
+ ACTION_NOT_AUTHORIZED_ON_APPLICATION: new ApiError("You are not authorized to perform this action on this application", 20012),
SLOWMODE_RATE_LIMIT: new ApiError("This action cannot be performed due to slowmode rate limit", 20016),
ONLY_OWNER: new ApiError("Only the owner of this account can perform this action", 20018),
ANNOUNCEMENT_RATE_LIMITS: new ApiError("This message cannot be edited due to announcement rate limits", 20022),
@@ -576,40 +568,25 @@ export const DiscordApiErrors = {
MAXIMUM_GUILDS: new ApiError("Maximum number of guilds reached ({})", 30001, undefined, ["100"]),
MAXIMUM_FRIENDS: new ApiError("Maximum number of friends reached ({})", 30002, undefined, ["1000"]),
MAXIMUM_PINS: new ApiError("Maximum number of pins reached for the channel ({})", 30003, undefined, ["50"]),
- MAXIMUM_NUMBER_OF_RECIPIENTS_REACHED: new ApiError("Maximum number of recipients reached ({})", 30004, undefined, [
- "10",
- ]),
+ MAXIMUM_NUMBER_OF_RECIPIENTS_REACHED: new ApiError("Maximum number of recipients reached ({})", 30004, undefined, ["10"]),
MAXIMUM_ROLES: new ApiError("Maximum number of guild roles reached ({})", 30005, undefined, ["250"]),
MAXIMUM_WEBHOOKS: new ApiError("Maximum number of webhooks reached ({})", 30007, undefined, ["10"]),
MAXIMUM_NUMBER_OF_EMOJIS_REACHED: new ApiError("Maximum number of emojis reached", 30008),
MAXIMUM_REACTIONS: new ApiError("Maximum number of reactions reached ({})", 30010, undefined, ["20"]),
MAXIMUM_CHANNELS: new ApiError("Maximum number of guild channels reached ({})", 30013, undefined, ["500"]),
- MAXIMUM_ATTACHMENTS: new ApiError("Maximum number of attachments in a message reached ({})", 30015, undefined, [
- "10",
- ]),
+ MAXIMUM_ATTACHMENTS: new ApiError("Maximum number of attachments in a message reached ({})", 30015, undefined, ["10"]),
MAXIMUM_INVITES: new ApiError("Maximum number of invites reached ({})", 30016, undefined, ["1000"]),
MAXIMUM_ANIMATED_EMOJIS: new ApiError("Maximum number of animated emojis reached", 30018),
MAXIMUM_SERVER_MEMBERS: new ApiError("Maximum number of server members reached", 30019),
- MAXIMUM_SERVER_CATEGORIES: new ApiError(
- "Maximum number of server categories has been reached ({})",
- 30030,
- undefined,
- ["5"]
- ),
+ MAXIMUM_SERVER_CATEGORIES: new ApiError("Maximum number of server categories has been reached ({})", 30030, undefined, ["5"]),
GUILD_ALREADY_HAS_TEMPLATE: new ApiError("Guild already has a template", 30031),
MAXIMUM_THREAD_PARTICIPANTS: new ApiError("Max number of thread participants has been reached", 30033),
- MAXIMUM_BANS_FOR_NON_GUILD_MEMBERS: new ApiError(
- "Maximum number of bans for non-guild members have been exceeded",
- 30035
- ),
+ MAXIMUM_BANS_FOR_NON_GUILD_MEMBERS: new ApiError("Maximum number of bans for non-guild members have been exceeded", 30035),
MAXIMUM_BANS_FETCHES: new ApiError("Maximum number of bans fetches has been reached", 30037),
MAXIMUM_STICKERS: new ApiError("Maximum number of stickers reached", 30039),
MAXIMUM_PRUNE_REQUESTS: new ApiError("Maximum number of prune requests has been reached. Try again later", 30040),
UNAUTHORIZED: new ApiError("Unauthorized. Provide a valid token and try again", 40001),
- ACCOUNT_VERIFICATION_REQUIRED: new ApiError(
- "You need to verify your account in order to perform this action",
- 40002
- ),
+ ACCOUNT_VERIFICATION_REQUIRED: new ApiError("You need to verify your account in order to perform this action", 40002),
OPENING_DIRECT_MESSAGES_TOO_FAST: new ApiError("You are opening direct messages too fast", 40003),
REQUEST_ENTITY_TOO_LARGE: new ApiError("Request entity too large. Try sending something smaller in size", 40005),
FEATURE_TEMPORARILY_DISABLED: new ApiError("This feature has been temporarily disabled server-side", 40006),
@@ -625,10 +602,7 @@ export const DiscordApiErrors = {
CANNOT_SEND_EMPTY_MESSAGE: new ApiError("Cannot send an empty message", 50006),
CANNOT_MESSAGE_USER: new ApiError("Cannot send messages to this user", 50007),
CANNOT_SEND_MESSAGES_IN_VOICE_CHANNEL: new ApiError("Cannot send messages in a voice channel", 50008),
- CHANNEL_VERIFICATION_LEVEL_TOO_HIGH: new ApiError(
- "Channel verification level is too high for you to gain access",
- 50009
- ),
+ CHANNEL_VERIFICATION_LEVEL_TOO_HIGH: new ApiError("Channel verification level is too high for you to gain access", 50009),
OAUTH2_APPLICATION_BOT_ABSENT: new ApiError("OAuth2 application does not have a bot", 50010),
MAXIMUM_OAUTH2_APPLICATIONS: new ApiError("OAuth2 application limit reached", 50011),
INVALID_OAUTH_STATE: new ApiError("Invalid OAuth2 state", 50012),
@@ -641,10 +615,7 @@ export const DiscordApiErrors = {
undefined,
["2", "100"]
),
- CANNOT_PIN_MESSAGE_IN_OTHER_CHANNEL: new ApiError(
- "A message can only be pinned to the channel it was sent in",
- 50019
- ),
+ CANNOT_PIN_MESSAGE_IN_OTHER_CHANNEL: new ApiError("A message can only be pinned to the channel it was sent in", 50019),
INVALID_OR_TAKEN_INVITE_CODE: new ApiError("Invite code was either invalid or taken", 50020),
CANNOT_EXECUTE_ON_SYSTEM_MESSAGE: new ApiError("Cannot execute action on a system message", 50021),
CANNOT_EXECUTE_ON_THIS_CHANNEL_TYPE: new ApiError("Cannot execute action on this channel type", 50024),
@@ -658,34 +629,22 @@ export const DiscordApiErrors = {
"Invalid form body (returned for both application/json and multipart/form-data bodies), or invalid Content-Type provided",
50035
),
- INVITE_ACCEPTED_TO_GUILD_NOT_CONTAINING_BOT: new ApiError(
- "An invite was accepted to a guild the application's bot is not in",
- 50036
- ),
+ INVITE_ACCEPTED_TO_GUILD_NOT_CONTAINING_BOT: new ApiError("An invite was accepted to a guild the application's bot is not in", 50036),
INVALID_API_VERSION: new ApiError("Invalid API version provided", 50041),
FILE_EXCEEDS_MAXIMUM_SIZE: new ApiError("File uploaded exceeds the maximum size", 50045),
INVALID_FILE_UPLOADED: new ApiError("Invalid file uploaded", 50046),
CANNOT_SELF_REDEEM_GIFT: new ApiError("Cannot self-redeem this gift", 50054),
PAYMENT_SOURCE_REQUIRED: new ApiError("Payment source required to redeem gift", 50070),
- CANNOT_DELETE_COMMUNITY_REQUIRED_CHANNEL: new ApiError(
- "Cannot delete a channel required for Community guilds",
- 50074
- ),
+ CANNOT_DELETE_COMMUNITY_REQUIRED_CHANNEL: new ApiError("Cannot delete a channel required for Community guilds", 50074),
INVALID_STICKER_SENT: new ApiError("Invalid sticker sent", 50081),
CANNOT_EDIT_ARCHIVED_THREAD: new ApiError(
"Tried to perform an operation on an archived thread, such as editing a message or adding a user to the thread",
50083
),
INVALID_THREAD_NOTIFICATION_SETTINGS: new ApiError("Invalid thread notification settings", 50084),
- BEFORE_EARLIER_THAN_THREAD_CREATION_DATE: new ApiError(
- "before value is earlier than the thread creation date",
- 50085
- ),
+ BEFORE_EARLIER_THAN_THREAD_CREATION_DATE: new ApiError("before value is earlier than the thread creation date", 50085),
SERVER_NOT_AVAILABLE_IN_YOUR_LOCATION: new ApiError("This server is not available in your location", 50095),
- SERVER_NEEDS_MONETIZATION_ENABLED: new ApiError(
- "This server needs monetization enabled in order to perform this action",
- 50097
- ),
+ SERVER_NEEDS_MONETIZATION_ENABLED: new ApiError("This server needs monetization enabled in order to perform this action", 50097),
TWO_FACTOR_REQUIRED: new ApiError("Two factor is required for this operation", 60003),
NO_USERS_WITH_DISCORDTAG_EXIST: new ApiError("No users with DiscordTag exist", 80004),
REACTION_BLOCKED: new ApiError("Reaction was blocked", 90001),
@@ -694,33 +653,17 @@ export const DiscordApiErrors = {
THREAD_ALREADY_CREATED_FOR_THIS_MESSAGE: new ApiError("A thread has already been created for this message", 160004),
THREAD_IS_LOCKED: new ApiError("Thread is locked", 160005),
MAXIMUM_NUMBER_OF_ACTIVE_THREADS: new ApiError("Maximum number of active threads reached", 160006),
- MAXIMUM_NUMBER_OF_ACTIVE_ANNOUNCEMENT_THREADS: new ApiError(
- "Maximum number of active announcement threads reached",
- 160007
- ),
+ MAXIMUM_NUMBER_OF_ACTIVE_ANNOUNCEMENT_THREADS: new ApiError("Maximum number of active announcement threads reached", 160007),
INVALID_JSON_FOR_UPLOADED_LOTTIE_FILE: new ApiError("Invalid JSON for uploaded Lottie file", 170001),
- LOTTIES_CANNOT_CONTAIN_RASTERIZED_IMAGES: new ApiError(
- "Uploaded Lotties cannot contain rasterized images such as PNG or JPEG",
- 170002
- ),
+ LOTTIES_CANNOT_CONTAIN_RASTERIZED_IMAGES: new ApiError("Uploaded Lotties cannot contain rasterized images such as PNG or JPEG", 170002),
STICKER_MAXIMUM_FRAMERATE: new ApiError("Sticker maximum framerate exceeded", 170003),
- STICKER_MAXIMUM_FRAME_COUNT: new ApiError("Sticker frame count exceeds maximum of {} frames", 170004, undefined, [
- "1000",
- ]),
+ STICKER_MAXIMUM_FRAME_COUNT: new ApiError("Sticker frame count exceeds maximum of {} frames", 170004, undefined, ["1000"]),
LOTTIE_ANIMATION_MAXIMUM_DIMENSIONS: new ApiError("Lottie animation maximum dimensions exceeded", 170005),
- STICKER_FRAME_RATE_TOO_SMALL_OR_TOO_LARGE: new ApiError(
- "Sticker frame rate is either too small or too large",
- 170006
- ),
- STICKER_ANIMATION_DURATION_MAXIMUM: new ApiError(
- "Sticker animation duration exceeds maximum of {} seconds",
- 170007,
- undefined,
- ["5"]
- ),
+ STICKER_FRAME_RATE_TOO_SMALL_OR_TOO_LARGE: new ApiError("Sticker frame rate is either too small or too large", 170006),
+ STICKER_ANIMATION_DURATION_MAXIMUM: new ApiError("Sticker animation duration exceeds maximum of {} seconds", 170007, undefined, ["5"]),
//Other errors
- UNKNOWN_VOICE_STATE: new ApiError("Unknown Voice State", 10065, 404),
+ UNKNOWN_VOICE_STATE: new ApiError("Unknown Voice State", 10065, 404)
};
/**
@@ -746,7 +689,7 @@ export const FosscordApiErrors = {
CANNOT_BACKFILL_TO_THE_FUTURE: new ApiError("You cannot backfill messages in the future", 55003),
CANNOT_GRANT_PERMISSIONS_EXCEEDING_RIGHTS: new ApiError("You cannot grant permissions exceeding your own rights", 50050),
ROUTES_LOOPING: new ApiError("Loops in the route definition ({})", 50060, undefined, [""]),
- CANNOT_REMOVE_ROUTE: new ApiError("Cannot remove message route while it is in effect and being used", 50061),
+ CANNOT_REMOVE_ROUTE: new ApiError("Cannot remove message route while it is in effect and being used", 50061)
};
/**
@@ -765,11 +708,7 @@ export const DefaultMessageNotifications = ["ALL", "MENTIONS", "MUTED"];
* * INSERTED (Fosscord extension)
* @typedef {string} MembershipStates
*/
-export const MembershipStates = [
- "INSERTED",
- "INVITED",
- "ACCEPTED",
-];
+export const MembershipStates = ["INSERTED", "INVITED", "ACCEPTED"];
/**
* The value set for a webhook's type:
@@ -778,15 +717,10 @@ export const MembershipStates = [
* * Custom (Fosscord extension)
* @typedef {string} WebhookTypes
*/
-export const WebhookTypes = [
- "Custom",
- "Incoming",
- "Channel Follower",
-];
+export const WebhookTypes = ["Custom", "Incoming", "Channel Follower"];
function keyMirror(arr: string[]) {
let tmp = Object.create(null);
for (const value of arr) tmp[value] = value;
return tmp;
}
-
diff --git a/util/src/util/Email.ts b/src/util/util/Email.ts
index 6885da33..c98ccff0 100644
--- a/util/src/util/Email.ts
+++ b/src/util/util/Email.ts
@@ -15,7 +15,7 @@ export function adjustEmail(email?: string): string | undefined {
// replace .dots and +alternatives -> Gmail Dot Trick https://support.google.com/mail/answer/7436150 and https://generator.email/blog/gmail-generator
let v = user.replace(/[.]|(\+.*)/g, "") + "@gmail.com";
}
-
+
if (domain === "google.com") {
// replace .dots and +alternatives -> Google Staff GMail Dot Trick
let v = user.replace(/[.]|(\+.*)/g, "") + "@google.com";
diff --git a/util/src/util/Event.ts b/src/util/util/Event.ts
index bb624051..383e4e50 100644
--- a/util/src/util/Event.ts
+++ b/src/util/util/Event.ts
@@ -1,7 +1,7 @@
import { Channel } from "amqplib";
-import { RabbitMQ } from "./RabbitMQ";
import EventEmitter from "events";
import { EVENT, Event } from "../interfaces";
+import { RabbitMQ } from "./RabbitMQ";
export const events = new EventEmitter();
export async function emitEvent(payload: Omit<Event, "created_at">) {
@@ -58,8 +58,8 @@ export async function listenEvent(event: string, callback: (event: EventOpts) =>
process.setMaxListeners(process.getMaxListeners() - 1);
};
- const listener = (msg: ProcessEvent) => {
- msg.type === "event" && msg.id === event && callback({ ...msg.event, cancel });
+ const listener = (message: any) => {
+ message.type === "event" && message.id === event && callback({ ...message.event, cancel });
};
process.addListener("message", listener);
@@ -79,12 +79,7 @@ export async function listenEvent(event: string, callback: (event: EventOpts) =>
}
}
-async function rabbitListen(
- channel: Channel,
- id: string,
- callback: (event: EventOpts) => any,
- opts?: { acknowledge?: boolean }
-) {
+async function rabbitListen(channel: Channel, id: string, callback: (event: EventOpts) => any, opts?: { acknowledge?: boolean }) {
await channel.assertExchange(id, "fanout", { durable: false });
const q = await channel.assertQueue("", { exclusive: true, autoDelete: true });
@@ -109,12 +104,12 @@ async function rabbitListen(
channel.ack(opts);
},
channel,
- cancel,
+ cancel
});
// rabbitCh.ack(opts);
},
{
- noAck: !opts?.acknowledge,
+ noAck: !opts?.acknowledge
}
);
diff --git a/util/src/util/FieldError.ts b/src/util/util/FieldError.ts
index 406b33e8..bdffd618 100644
--- a/util/src/util/FieldError.ts
+++ b/src/util/util/FieldError.ts
@@ -1,5 +1,3 @@
-import "missing-native-js-functions";
-
export function FieldErrors(fields: Record<string, { code?: string; message: string }>) {
return new FieldError(
50035,
@@ -8,9 +6,9 @@ export function FieldErrors(fields: Record<string, { code?: string; message: str
_errors: [
{
message,
- code: code || "BASE_TYPE_INVALID",
- },
- ],
+ code: code || "BASE_TYPE_INVALID"
+ }
+ ]
}))
);
}
diff --git a/util/src/util/Intents.ts b/src/util/util/Intents.ts
index 1e840b76..db4c9e28 100644
--- a/util/src/util/Intents.ts
+++ b/src/util/util/Intents.ts
@@ -22,13 +22,12 @@ export class Intents extends BitField {
GUILD_POLICY_EXECUTION: BigInt(1) << BigInt(21), // guild policy execution
LIVE_MESSAGE_COMPOSITION: BigInt(1) << BigInt(32), // allow composing messages using the gateway
GUILD_ROUTES: BigInt(1) << BigInt(41), // message routes affecting the guild
- DIRECT_MESSAGES_THREADS: BigInt(1) << BigInt(42), // direct message threads
+ DIRECT_MESSAGES_THREADS: BigInt(1) << BigInt(42), // direct message threads
JUMBO_EVENTS: BigInt(1) << BigInt(43), // jumbo events (size limits to be defined later)
LOBBIES: BigInt(1) << BigInt(44), // lobbies
- INSTANCE_ROUTES: BigInt(1) << BigInt(60), // all message route changes
+ INSTANCE_ROUTES: BigInt(1) << BigInt(60), // all message route changes
INSTANCE_GUILD_CHANGES: BigInt(1) << BigInt(61), // all guild create, guild object patch, split, merge and delete events
INSTANCE_POLICY_UPDATES: BigInt(1) << BigInt(62), // all instance policy updates
INSTANCE_USER_UPDATES: BigInt(1) << BigInt(63) // all instance user updates
};
}
-
diff --git a/util/src/util/MessageFlags.ts b/src/util/util/MessageFlags.ts
index b59295c4..c5d5d02a 100644
--- a/util/src/util/MessageFlags.ts
+++ b/src/util/util/MessageFlags.ts
@@ -15,6 +15,6 @@ export class MessageFlags extends BitField {
INTERACTION_WAIT: BigInt(1) << BigInt(7), // discord.com calls this LOADING
// FAILED_TO_MENTION_SOME_ROLES_IN_THREAD: BigInt(1) << BigInt(8)
SCRIPT_WAIT: BigInt(1) << BigInt(24), // waiting for the self command to complete
- IMPORT_WAIT: BigInt(1) << BigInt(25), // latest message of a bulk import, waiting for the rest of the channel to be backfilled
+ IMPORT_WAIT: BigInt(1) << BigInt(25) // latest message of a bulk import, waiting for the rest of the channel to be backfilled
};
}
diff --git a/util/src/util/Permissions.ts b/src/util/util/Permissions.ts
index e5459ab5..b8d0d8ae 100644
--- a/util/src/util/Permissions.ts
+++ b/src/util/util/Permissions.ts
@@ -1,17 +1,8 @@
// https://github.com/discordjs/discord.js/blob/master/src/util/Permissions.js
// Apache License Version 2.0 Copyright 2015 - 2021 Amish Shah
+import { HTTPError } from "..";
import { Channel, ChannelPermissionOverwrite, Guild, Member, Role } from "../entities";
-import { BitField } from "./BitField";
-import "missing-native-js-functions";
-import { BitFieldResolvable, BitFlag } from "./BitField";
-
-var HTTPError: any;
-
-try {
- HTTPError = require("lambert-server").HTTPError;
-} catch (e) {
- HTTPError = Error;
-}
+import { BitField, BitFieldResolvable, BitFlag } from "./BitField";
export type PermissionResolvable = bigint | number | Permissions | PermissionResolvable[] | PermissionString;
@@ -68,7 +59,7 @@ export class Permissions extends BitField {
MANAGE_THREADS: BitFlag(34),
USE_PUBLIC_THREADS: BitFlag(35),
USE_PRIVATE_THREADS: BitFlag(36),
- USE_EXTERNAL_STICKERS: BitFlag(37),
+ USE_EXTERNAL_STICKERS: BitFlag(37)
/**
* CUSTOM PERMISSIONS ideas:
@@ -132,7 +123,7 @@ export class Permissions extends BitField {
static finalPermission({
user,
guild,
- channel,
+ channel
}: {
user: { id: string; roles: string[] };
guild: { roles: Role[] };
@@ -172,7 +163,7 @@ export class Permissions extends BitField {
"USE_EXTERNAL_EMOJIS",
"CONNECT",
"SPEAK",
- "MANAGE_CHANNELS",
+ "MANAGE_CHANNELS"
]);
}
@@ -207,9 +198,9 @@ export async function getPermission(
} = {}
) {
if (!user_id) throw new HTTPError("User not found");
- var channel: Channel | undefined;
- var member: Member | undefined;
- var guild: Guild | undefined;
+ let channel: Channel | undefined;
+ let member: Member | undefined;
+ let guild: Guild | undefined;
if (channel_id) {
channel = await Channel.findOneOrFail({
@@ -222,8 +213,8 @@ export async function getPermission(
"owner_id",
"guild_id",
// @ts-ignore
- ...(opts.channel_select || []),
- ],
+ ...(opts.channel_select || [])
+ ]
});
if (channel.guild_id) guild_id = channel.guild_id; // derive guild_id from the channel
}
@@ -235,9 +226,9 @@ export async function getPermission(
"id",
"owner_id",
// @ts-ignore
- ...(opts.guild_select || []),
+ ...(opts.guild_select || [])
],
- relations: opts.guild_relations,
+ relations: opts.guild_relations
});
if (guild.owner_id === user_id) return new Permissions(Permissions.FLAGS.ADMINISTRATOR);
@@ -247,9 +238,10 @@ export async function getPermission(
select: [
"id",
"roles",
+ "index",
// @ts-ignore
- ...(opts.member_select || []),
- ],
+ ...(opts.member_select || [])
+ ]
});
}
@@ -257,19 +249,19 @@ export async function getPermission(
if (!recipient_ids?.length) recipient_ids = null;
// TODO: remove guild.roles and convert recipient_ids to recipients
- var permission = Permissions.finalPermission({
+ let permission = Permissions.finalPermission({
user: {
id: user_id,
- roles: member?.roles.map((x) => x.id) || [],
+ roles: member?.roles.map((x) => x.id) || []
},
guild: {
- roles: member?.roles || [],
+ roles: member?.roles || []
},
channel: {
overwrites: channel?.permission_overwrites,
owner_id: channel?.owner_id,
- recipient_ids,
- },
+ recipient_ids
+ }
});
const obj = new Permissions(permission);
diff --git a/util/src/util/RabbitMQ.ts b/src/util/util/RabbitMQ.ts
index 0f5eb6aa..638b805b 100644
--- a/util/src/util/RabbitMQ.ts
+++ b/src/util/util/RabbitMQ.ts
@@ -1,4 +1,4 @@
-import amqp, { Connection, Channel } from "amqplib";
+import { Channel, Connection } from "amqplib";
// import Config from "./Config";
export const RabbitMQ: { connection: Connection | null; channel: Channel | null; init: () => Promise<void> } = {
@@ -15,5 +15,5 @@ export const RabbitMQ: { connection: Connection | null; channel: Channel | null;
// console.log(`[RabbitMQ] connected`);
// this.channel = await this.connection.createChannel();
// console.log(`[RabbitMQ] channel created`);
- },
+ }
};
diff --git a/util/src/util/Regex.ts b/src/util/util/Regex.ts
index 83fc9fe8..83fc9fe8 100644
--- a/util/src/util/Regex.ts
+++ b/src/util/util/Regex.ts
diff --git a/util/src/util/Rights.ts b/src/util/util/Rights.ts
index b28c75b7..51bb098c 100644
--- a/util/src/util/Rights.ts
+++ b/src/util/util/Rights.ts
@@ -1,15 +1,6 @@
-import { BitField } from "./BitField";
-import "missing-native-js-functions";
-import { BitFieldResolvable, BitFlag } from "./BitField";
+import { HTTPError } from "..";
import { User } from "../entities";
-
-var HTTPError: any;
-
-try {
- HTTPError = require("lambert-server").HTTPError;
-} catch (e) {
- HTTPError = Error;
-}
+import { BitField, BitFieldResolvable, BitFlag } from "./BitField";
export type RightResolvable = bigint | number | Rights | RightResolvable[] | RightString;
@@ -60,7 +51,7 @@ export class Rights extends BitField {
CREDITABLE: BitFlag(32), // can receive money from monetisation related features
KICK_BAN_MEMBERS: BitFlag(33),
// can kick or ban guild or group DM members in the guilds/groups that they have KICK_MEMBERS, or BAN_MEMBERS
- SELF_LEAVE_GROUPS: BitFlag(34),
+ SELF_LEAVE_GROUPS: BitFlag(34),
// can leave the guilds or group DMs that they joined on their own (one can always leave a guild or group DMs they have been force-added)
PRESENCE: BitFlag(35),
// inverts the presence confidentiality default (OPERATOR's presence is not routed by default, others' are) for a given user
@@ -88,15 +79,16 @@ export class Rights extends BitField {
// @ts-ignore
throw new HTTPError(`You are missing the following rights ${permission}`, 403);
}
-
}
const ALL_RIGHTS = Object.values(Rights.FLAGS).reduce((total, val) => total | val, BigInt(0));
-export async function getRights( user_id: string
+export async function getRights(
+ user_id: string
/**, opts: {
in_behalf?: (keyof User)[];
- } = {} **/) {
+ } = {} **/
+) {
let user = await User.findOneOrFail({ where: { id: user_id } });
return new Rights(user.rights);
-}
+}
diff --git a/util/src/util/Snowflake.ts b/src/util/util/Snowflake.ts
index 134d526e..89f4f0c3 100644
--- a/util/src/util/Snowflake.ts
+++ b/src/util/util/Snowflake.ts
@@ -83,14 +83,15 @@ export class Snowflake {
return dec;
}
- static generateWorkerProcess() { // worker process - returns a number
- var time = BigInt(Date.now() - Snowflake.EPOCH) << BigInt(22);
- var worker = Snowflake.workerId << 17n;
- var process = Snowflake.processId << 12n;
- var increment = Snowflake.INCREMENT++;
+ static generateWorkerProcess() {
+ // worker process - returns a number
+ let time = BigInt(Date.now() - Snowflake.EPOCH) << BigInt(22);
+ let worker = Snowflake.workerId << 17n;
+ let process = Snowflake.processId << 12n;
+ let increment = Snowflake.INCREMENT++;
return BigInt(time | worker | process | increment);
}
-
+
static generate() {
return Snowflake.generateWorkerProcess().toString();
}
@@ -117,13 +118,13 @@ export class Snowflake {
workerID: parseInt(BINARY.substring(42, 47), 2),
processID: parseInt(BINARY.substring(47, 52), 2),
increment: parseInt(BINARY.substring(52, 64), 2),
- binary: BINARY,
+ binary: BINARY
};
Object.defineProperty(res, "date", {
get: function get() {
return new Date(this.timestamp);
},
- enumerable: true,
+ enumerable: true
});
return res;
}
diff --git a/util/src/util/String.ts b/src/util/util/String.ts
index 55f11e8d..55f11e8d 100644
--- a/util/src/util/String.ts
+++ b/src/util/util/String.ts
diff --git a/util/src/util/Token.ts b/src/util/util/Token.ts
index 500ace45..d192a13a 100644
--- a/util/src/util/Token.ts
+++ b/src/util/util/Token.ts
@@ -1,6 +1,6 @@
import jwt, { VerifyOptions } from "jsonwebtoken";
-import { Config } from "./Config";
import { User } from "../entities";
+import { Config } from "./Config";
export const JWTOptions: VerifyOptions = { algorithms: ["HS256"] };
@@ -11,18 +11,17 @@ export function checkToken(token: string, jwtSecret: string): Promise<any> {
in fosscord, even with instances that have bot distinction; we won't enforce "Bot" prefix,
as we don't really have separate pathways for bots
**/
-
+
jwt.verify(token, jwtSecret, JWTOptions, async (err, decoded: any) => {
if (err || !decoded) return rej("Invalid Token");
- const user = await User.findOne(
- { id: decoded.id },
- { select: ["data", "bot", "disabled", "deleted", "rights"] }
- );
+ const user = await User.findOne({
+ where: { id: decoded.id },
+ select: ["data", "bot", "disabled", "deleted", "rights"]
+ });
if (!user) return rej("Invalid Token");
// we need to round it to seconds as it saved as seconds in jwt iat and valid_tokens_since is stored in milliseconds
- if (decoded.iat * 1000 < new Date(user.data.valid_tokens_since).setSeconds(0, 0))
- return rej("Invalid Token");
+ if (decoded.iat * 1000 < new Date(user.data.valid_tokens_since).setSeconds(0, 0)) return rej("Invalid Token");
if (user.disabled) return rej("User disabled");
if (user.deleted) return rej("User not found");
@@ -40,7 +39,7 @@ export async function generateToken(id: string) {
{ id: id, iat },
Config.get().security.jwtSecret,
{
- algorithm,
+ algorithm
},
(err, token) => {
if (err) return rej(err);
diff --git a/util/src/util/TraverseDirectory.ts b/src/util/util/TraverseDirectory.ts
index 3d0d6279..6f60e73b 100644
--- a/util/src/util/TraverseDirectory.ts
+++ b/src/util/util/TraverseDirectory.ts
@@ -1,13 +1,10 @@
import { Server, traverseDirectory } from "lambert-server";
//if we're using ts-node, use ts files instead of js
-const extension = Symbol.for("ts-node.register.instance") in process ? "ts" : "js"
+const extension = Symbol.for("ts-node.register.instance") in process ? "ts" : "js";
-const DEFAULT_FILTER = new RegExp("^([^\.].*)(?<!\.d)\.(" + extension + ")$");
+const DEFAULT_FILTER = new RegExp("^([^.].*)(?<!.d).(" + extension + ")$");
export function registerRoutes(server: Server, root: string) {
- return traverseDirectory(
- { dirname: root, recursive: true, filter: DEFAULT_FILTER },
- server.registerRoute.bind(server, root)
- );
+ return traverseDirectory({ dirname: root, recursive: true, filter: DEFAULT_FILTER }, server.registerRoute.bind(server, root));
}
diff --git a/util/src/util/cdn.ts b/src/util/util/cdn.ts
index ea950cd1..5573b848 100644
--- a/util/src/util/cdn.ts
+++ b/src/util/util/cdn.ts
@@ -1,8 +1,7 @@
import FormData from "form-data";
-import { HTTPError } from "lambert-server";
import fetch from "node-fetch";
+import { HTTPError } from "..";
import { Config } from "./Config";
-import multer from "multer";
export async function uploadFile(path: string, file?: Express.Multer.File) {
if (!file?.buffer) throw new HTTPError("Missing file in body");
@@ -10,16 +9,16 @@ export async function uploadFile(path: string, file?: Express.Multer.File) {
const form = new FormData();
form.append("file", file.buffer, {
contentType: file.mimetype,
- filename: file.originalname,
+ filename: file.originalname
});
const response = await fetch(`${Config.get().cdn.endpointPrivate || "http://localhost:3003"}${path}`, {
headers: {
signature: Config.get().security.requestSignature,
- ...form.getHeaders(),
+ ...form.getHeaders()
},
method: "POST",
- body: form,
+ body: form
});
const result = await response.json();
@@ -45,9 +44,9 @@ export async function handleFile(path: string, body?: string): Promise<string |
export async function deleteFile(path: string) {
const response = await fetch(`${Config.get().cdn.endpointPrivate || "http://localhost:3003"}${path}`, {
headers: {
- signature: Config.get().security.requestSignature,
+ signature: Config.get().security.requestSignature
},
- method: "DELETE",
+ method: "DELETE"
});
const result = await response.json();
diff --git a/util/src/util/index.ts b/src/util/util/index.ts
index f7a273cb..11f0b72a 100644
--- a/util/src/util/index.ts
+++ b/src/util/util/index.ts
@@ -1,6 +1,6 @@
export * from "./ApiError";
+export * from "./Array";
export * from "./BitField";
-export * from "./Token";
//export * from "./Categories";
export * from "./cdn";
export * from "./Config";
@@ -9,7 +9,11 @@ export * from "./Database";
export * from "./Email";
export * from "./Event";
export * from "./FieldError";
+export * from "./imports/HTTPError";
+export * from "./imports/index";
+export * from "./imports/OrmUtils";
export * from "./Intents";
+export * from "./InvisibleCharacters";
export * from "./MessageFlags";
export * from "./Permissions";
export * from "./RabbitMQ";
@@ -17,6 +21,5 @@ export * from "./Regex";
export * from "./Rights";
export * from "./Snowflake";
export * from "./String";
-export * from "./Array";
+export * from "./Token";
export * from "./TraverseDirectory";
-export * from "./InvisibleCharacters";
\ No newline at end of file
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/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/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/migrations/1633864260873-EmojiRoles.ts b/util/src/migrations/1633864260873-EmojiRoles.ts
deleted file mode 100644
index f0d709f2..00000000
--- a/util/src/migrations/1633864260873-EmojiRoles.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { MigrationInterface, QueryRunner } from "typeorm";
-
-export class EmojiRoles1633864260873 implements MigrationInterface {
- name = "EmojiRoles1633864260873";
-
- public async up(queryRunner: QueryRunner): Promise<void> {
- await queryRunner.query(`ALTER TABLE "emojis" ADD "roles" text NOT NULL DEFAULT ''`);
- }
-
- public async down(queryRunner: QueryRunner): Promise<void> {
- await queryRunner.query(`ALTER TABLE "emojis" DROP COLUMN column_name "roles"`);
- }
-}
diff --git a/util/src/migrations/1633864669243-EmojiUser.ts b/util/src/migrations/1633864669243-EmojiUser.ts
deleted file mode 100644
index 982405d7..00000000
--- a/util/src/migrations/1633864669243-EmojiUser.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { MigrationInterface, QueryRunner } from "typeorm";
-
-export class EmojiUser1633864669243 implements MigrationInterface {
- name = "EmojiUser1633864669243";
-
- public async up(queryRunner: QueryRunner): Promise<void> {
- await queryRunner.query(`ALTER TABLE "emojis" ADD "user_id" varchar`);
- try {
- await queryRunner.query(
- `ALTER TABLE "emojis" ADD CONSTRAINT FK_fa7ddd5f9a214e28ce596548421 FOREIGN KEY (user_id) REFERENCES users(id)`
- );
- } catch (error) {
- console.error(
- "sqlite doesn't support altering foreign keys: https://stackoverflow.com/questions/1884818/how-do-i-add-a-foreign-key-to-an-existing-sqlite-table"
- );
- }
- }
-
- public async down(queryRunner: QueryRunner): Promise<void> {
- await queryRunner.query(`ALTER TABLE "emojis" DROP COLUMN column_name "user_id"`);
- await queryRunner.query(`ALTER TABLE "emojis" DROP CONSTRAINT FK_fa7ddd5f9a214e28ce596548421`);
- }
-}
diff --git a/util/src/migrations/1633881705509-VanityInvite.ts b/util/src/migrations/1633881705509-VanityInvite.ts
deleted file mode 100644
index 45485310..00000000
--- a/util/src/migrations/1633881705509-VanityInvite.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { MigrationInterface, QueryRunner } from "typeorm";
-
-export class VanityInvite1633881705509 implements MigrationInterface {
- name = "VanityInvite1633881705509";
-
- public async up(queryRunner: QueryRunner): Promise<void> {
- try {
- await queryRunner.query(`ALTER TABLE "emojis" DROP COLUMN vanity_url_code`);
- await queryRunner.query(`ALTER TABLE "emojis" DROP CONSTRAINT FK_c2c1809d79eb120ea0cb8d342ad`);
- } catch (error) {}
- }
-
- public async down(queryRunner: QueryRunner): Promise<void> {
- await queryRunner.query(`ALTER TABLE "emojis" ADD vanity_url_code varchar`);
- await queryRunner.query(
- `ALTER TABLE "emojis" ADD CONSTRAINT FK_c2c1809d79eb120ea0cb8d342ad FOREIGN KEY ("vanity_url_code") REFERENCES "invites"("code") ON DELETE NO ACTION ON UPDATE NO ACTION`
- );
- }
-}
diff --git a/util/src/migrations/1634308884591-Stickers.ts b/util/src/migrations/1634308884591-Stickers.ts
deleted file mode 100644
index fbc4649f..00000000
--- a/util/src/migrations/1634308884591-Stickers.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import { MigrationInterface, QueryRunner, Table, TableColumn, TableForeignKey } from "typeorm";
-
-export class Stickers1634308884591 implements MigrationInterface {
- name = "Stickers1634308884591";
-
- public async up(queryRunner: QueryRunner): Promise<void> {
- await queryRunner.dropForeignKey("read_states", "FK_6f255d873cfbfd7a93849b7ff74");
- await queryRunner.changeColumn(
- "stickers",
- "tags",
- new TableColumn({ name: "tags", type: "varchar", isNullable: true })
- );
- await queryRunner.changeColumn(
- "stickers",
- "pack_id",
- new TableColumn({ name: "pack_id", type: "varchar", isNullable: true })
- );
- await queryRunner.changeColumn("stickers", "type", new TableColumn({ name: "type", type: "integer" }));
- await queryRunner.changeColumn(
- "stickers",
- "format_type",
- new TableColumn({ name: "format_type", type: "integer" })
- );
- await queryRunner.changeColumn(
- "stickers",
- "available",
- new TableColumn({ name: "available", type: "boolean", isNullable: true })
- );
- await queryRunner.changeColumn(
- "stickers",
- "user_id",
- new TableColumn({ name: "user_id", type: "boolean", isNullable: true })
- );
- await queryRunner.createForeignKey(
- "stickers",
- new TableForeignKey({
- name: "FK_8f4ee73f2bb2325ff980502e158",
- columnNames: ["user_id"],
- referencedColumnNames: ["id"],
- referencedTableName: "users",
- onDelete: "CASCADE",
- })
- );
- await queryRunner.createTable(
- new Table({
- name: "sticker_packs",
- columns: [
- new TableColumn({ name: "id", type: "varchar", isPrimary: true }),
- new TableColumn({ name: "name", type: "varchar" }),
- new TableColumn({ name: "description", type: "varchar", isNullable: true }),
- new TableColumn({ name: "banner_asset_id", type: "varchar", isNullable: true }),
- new TableColumn({ name: "cover_sticker_id", type: "varchar", isNullable: true }),
- ],
- foreignKeys: [
- new TableForeignKey({
- columnNames: ["cover_sticker_id"],
- referencedColumnNames: ["id"],
- referencedTableName: "stickers",
- }),
- ],
- })
- );
- }
-
- public async down(queryRunner: QueryRunner): Promise<void> {}
-}
diff --git a/util/src/migrations/1634424361103-Presence.ts b/util/src/migrations/1634424361103-Presence.ts
deleted file mode 100644
index 729955b8..00000000
--- a/util/src/migrations/1634424361103-Presence.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
-
-export class Presence1634424361103 implements MigrationInterface {
- name = "Presence1634424361103";
-
- public async up(queryRunner: QueryRunner): Promise<void> {
- queryRunner.addColumn("sessions", new TableColumn({ name: "activites", type: "text" }));
- }
-
- public async down(queryRunner: QueryRunner): Promise<void> {}
-}
diff --git a/util/src/migrations/1634426540271-MigrationTimestamp.ts b/util/src/migrations/1634426540271-MigrationTimestamp.ts
deleted file mode 100644
index 3208b25b..00000000
--- a/util/src/migrations/1634426540271-MigrationTimestamp.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
-
-export class MigrationTimestamp1634426540271 implements MigrationInterface {
- name = "MigrationTimestamp1634426540271";
-
- public async up(queryRunner: QueryRunner): Promise<void> {
- await queryRunner.changeColumn(
- "migrations",
- "timestamp",
- new TableColumn({ name: "timestampe", type: "bigint", isNullable: false })
- );
- }
-
- public async down(queryRunner: QueryRunner): Promise<void> {}
-}
diff --git a/util/src/migrations/1648643945733-ReleaseTypo.ts b/util/src/migrations/1648643945733-ReleaseTypo.ts
deleted file mode 100644
index 944b9dd9..00000000
--- a/util/src/migrations/1648643945733-ReleaseTypo.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { MigrationInterface, QueryRunner } from "typeorm";
-
-export class ReleaseTypo1648643945733 implements MigrationInterface {
- name = "ReleaseTypo1648643945733";
-
- public async up(queryRunner: QueryRunner): Promise<void> {
- //drop table first because typeorm creates it before migrations run
- await queryRunner.dropTable("client_release", true);
- await queryRunner.renameTable("client_relase", "client_release");
- }
-
- public async down(queryRunner: QueryRunner): Promise<void> {
- await queryRunner.dropTable("client_relase", true);
- await queryRunner.renameTable("client_release", "client_relase");
- }
-}
diff --git a/util/src/util/Config.ts b/util/src/util/Config.ts
deleted file mode 100644
index 31b8d97c..00000000
--- a/util/src/util/Config.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-import "missing-native-js-functions";
-import { ConfigValue, ConfigEntity, DefaultConfigOptions } from "../entities/Config";
-import path from "path";
-import fs from "fs";
-
-// TODO: yaml instead of json
-// const overridePath = path.join(process.cwd(), "config.json");
-
-var config: ConfigValue;
-var pairs: ConfigEntity[];
-
-// TODO: use events to inform about config updates
-// Config keys are separated with _
-
-export const Config = {
- init: async function init() {
- if (config) return config;
- pairs = await ConfigEntity.find();
- config = pairsToConfig(pairs);
- config = (config || {}).merge(DefaultConfigOptions);
-
- // try {
- // const overrideConfig = JSON.parse(fs.readFileSync(overridePath, { encoding: "utf8" }));
- // config = overrideConfig.merge(config);
- // } catch (error) {
- // fs.writeFileSync(overridePath, JSON.stringify(config, null, 4));
- // }
-
- return this.set(config);
- },
- get: function get() {
- return config;
- },
- set: function set(val: Partial<ConfigValue>) {
- if (!config || !val) return;
- config = val.merge(config);
-
- return applyConfig(config);
- },
-};
-
-function applyConfig(val: ConfigValue) {
- async function apply(obj: any, key = ""): Promise<any> {
- if (typeof obj === "object" && obj !== null)
- return Promise.all(Object.keys(obj).map((k) => apply(obj[k], key ? `${key}_${k}` : k)));
-
- let pair = pairs.find((x) => x.key === key);
- if (!pair) pair = new ConfigEntity();
-
- pair.key = key;
- pair.value = obj;
- return pair.save();
- }
- // fs.writeFileSync(overridePath, JSON.stringify(val, null, 4));
-
- return apply(val);
-}
-
-function pairsToConfig(pairs: ConfigEntity[]) {
- var value: any = {};
-
- pairs.forEach((p) => {
- const keys = p.key.split("_");
- let obj = value;
- let prev = "";
- let prevObj = obj;
- let i = 0;
-
- for (const key of keys) {
- if (!isNaN(Number(key)) && !prevObj[prev]?.length) prevObj[prev] = obj = [];
- if (i++ === keys.length - 1) obj[key] = p.value;
- else if (!obj[key]) obj[key] = {};
-
- prev = key;
- prevObj = obj;
- obj = obj[key];
- }
- });
-
- return value as ConfigValue;
-}
diff --git a/util/src/util/Database.ts b/util/src/util/Database.ts
deleted file mode 100644
index 9ab5d14c..00000000
--- a/util/src/util/Database.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import path from "path";
-import "reflect-metadata";
-import { Connection, createConnection } from "typeorm";
-import * as Models from "../entities";
-import { Migration } from "../entities/Migration";
-import { yellow, green, red } from "picocolors";
-
-// UUID extension option is only supported with postgres
-// We want to generate all id's with Snowflakes that's why we have our own BaseEntity class
-
-var promise: Promise<any>;
-var dbConnection: Connection | undefined;
-let dbConnectionString = process.env.DATABASE || path.join(process.cwd(), "database.db");
-
-export function initDatabase(): Promise<Connection> {
- if (promise) return promise; // prevent initalizing multiple times
-
- const type = dbConnectionString.includes("://") ? dbConnectionString.split(":")[0]?.replace("+srv", "") : "sqlite";
- const isSqlite = type.includes("sqlite");
-
- console.log(`[Database] ${yellow(`connecting to ${type} db`)}`);
- if(isSqlite) {
- console.log(`[Database] ${red(`You are running sqlite! Please keep in mind that we recommend setting up a dedicated database!`)}`);
- }
- // @ts-ignore
- promise = createConnection({
- type,
- charset: 'utf8mb4',
- url: isSqlite ? undefined : dbConnectionString,
- database: isSqlite ? dbConnectionString : undefined,
- // @ts-ignore
- entities: Object.values(Models).filter((x) => x.constructor.name !== "Object" && x.name),
- synchronize: type !== "mongodb",
- logging: false,
- cache: {
- duration: 1000 * 3, // cache all find queries for 3 seconds
- },
- bigNumberStrings: false,
- supportBigNumbers: true,
- name: "default",
- migrations: [path.join(__dirname, "..", "migrations", "*.js")],
- });
-
- promise.then(async (connection: Connection) => {
- dbConnection = connection;
-
- // run migrations, and if it is a new fresh database, set it to the last migration
- if (connection.migrations.length) {
- if (!(await Migration.findOne({}))) {
- let i = 0;
-
- await Migration.insert(
- connection.migrations.map((x) => ({
- id: i++,
- name: x.name,
- timestamp: Date.now(),
- }))
- );
- }
- }
- await connection.runMigrations();
- console.log(`[Database] ${green("connected")}`);
- });
-
- return promise;
-}
-
-export { dbConnection };
-
-export function closeDatabase() {
- dbConnection?.close();
-}
diff --git a/util/src/util/InvisibleCharacters.ts b/util/src/util/InvisibleCharacters.ts
deleted file mode 100644
index 2b014e14..00000000
--- a/util/src/util/InvisibleCharacters.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-// List from https://invisible-characters.com/
-export const InvisibleCharacters = [
- '\u{9}', //Tab
- '\u{20}', //Space
- '\u{ad}', //Soft hyphen
- '\u{34f}', //Combining grapheme joiner
- '\u{61c}', //Arabic letter mark
- '\u{115f}', //Hangul choseong filler
- '\u{1160}', //Hangul jungseong filler
- '\u{17b4}', //Khmer vowel inherent AQ
- '\u{17b5}', //Khmer vowel inherent AA
- '\u{180e}', //Mongolian vowel separator
- '\u{2000}', //En quad
- '\u{2001}', //Em quad
- '\u{2002}', //En space
- '\u{2003}', //Em space
- '\u{2004}', //Three-per-em space
- '\u{2005}', //Four-per-em space
- '\u{2006}', //Six-per-em space
- '\u{2007}', //Figure space
- '\u{2008}', //Punctuation space
- '\u{2009}', //Thin space
- '\u{200a}', //Hair space
- '\u{200b}', //Zero width space
- '\u{200c}', //Zero width non-joiner
- '\u{200d}', //Zero width joiner
- '\u{200e}', //Left-to-right mark
- '\u{200f}', //Right-to-left mark
- '\u{202f}', //Narrow no-break space
- '\u{205f}', //Medium mathematical space
- '\u{2060}', //Word joiner
- '\u{2061}', //Function application
- '\u{2062}', //Invisible times
- '\u{2063}', //Invisible separator
- '\u{2064}', //Invisible plus
- '\u{206a}', //Inhibit symmetric swapping
- '\u{206b}', //Activate symmetric swapping
- '\u{206c}', //Inhibit arabic form shaping
- '\u{206d}', //Activate arabic form shaping
- '\u{206e}', //National digit shapes
- '\u{206f}', //Nominal digit shapes
- '\u{3000}', //Ideographic space
- '\u{2800}', //Braille pattern blank
- '\u{3164}', //Hangul filler
- '\u{feff}', //Zero width no-break space
- '\u{ffa0}', //Haldwidth hangul filler
- '\u{1d159}', //Musical symbol null notehead
- '\u{1d173}', //Musical symbol begin beam
- '\u{1d174}', //Musical symbol end beam
- '\u{1d175}', //Musical symbol begin tie
- '\u{1d176}', //Musical symbol end tie
- '\u{1d177}', //Musical symbol begin slur
- '\u{1d178}', //Musical symbol end slur
- '\u{1d179}', //Musical symbol begin phrase
- '\u{1d17a}' //Musical symbol end phrase
-];
\ No newline at end of file
|