diff --git a/util/src/entities/AuditLog.ts b/util/src/entities/AuditLog.ts
index ae9feb76..4b81ed6a 100644
--- a/util/src/entities/AuditLog.ts
+++ b/util/src/entities/AuditLog.ts
@@ -55,10 +55,7 @@ export class AuditLog extends BaseClass {
@ManyToOne(() => User, (user: User) => user.id)
user: User;
- @Column({
- type: "simple-enum",
- enum: AuditLogEvents,
- })
+ @Column({ type: "int" })
action_type: AuditLogEvents;
@Column({ type: "simple-json", nullable: true })
diff --git a/util/src/entities/BaseClass.ts b/util/src/entities/BaseClass.ts
index beccf04b..d20078e5 100644
--- a/util/src/entities/BaseClass.ts
+++ b/util/src/entities/BaseClass.ts
@@ -1,19 +1,8 @@
import "reflect-metadata";
-import {
- BaseEntity,
- BeforeInsert,
- BeforeUpdate,
- EntityMetadata,
- FindConditions,
- ObjectIdColumn,
- PrimaryColumn,
-} from "typeorm";
+import { BaseEntity, EntityMetadata, FindConditions, ObjectIdColumn, PrimaryColumn } from "typeorm";
import { Snowflake } from "../util/Snowflake";
import "missing-native-js-functions";
-// TODO use class-validator https://typeorm.io/#/validation with class annotators (isPhone/isEmail) combined with types from typescript-json-schema
-// btw. we don't use class-validator for everything, because we need to explicitly set the type instead of deriving it from typescript also it doesn't easily support nested objects
-
export class BaseClassWithoutId extends BaseEntity {
constructor(props?: any) {
super();
@@ -42,7 +31,7 @@ export class BaseClassWithoutId extends BaseEntity {
for (const key in props) {
if (!properties.has(key)) continue;
// @ts-ignore
- const setter = this[`set${key.capitalize()}`];
+ const setter = this[`set${key.capitalize()}`]; // use setter function if it exists
if (setter) {
setter.call(this, props[key]);
@@ -53,12 +42,6 @@ export class BaseClassWithoutId extends BaseEntity {
}
}
- @BeforeUpdate()
- @BeforeInsert()
- validate() {
- return this;
- }
-
toJSON(): any {
return Object.fromEntries(
this.metadata.columns // @ts-ignore
@@ -76,42 +59,6 @@ export class BaseClassWithoutId extends BaseEntity {
const repository = this.getRepository();
return repository.decrement(conditions, propertyPath, value);
}
-
- // static async delete<T>(criteria: FindConditions<T>, options?: RemoveOptions) {
- // if (!criteria) throw new Error("You need to specify delete criteria");
-
- // const repository = this.getRepository();
- // const promises = repository.metadata.relations.map(async (x) => {
- // if (x.orphanedRowAction !== "delete") return;
-
- // const foreignKey =
- // x.foreignKeys.find((key) => key.entityMetadata === repository.metadata) ||
- // x.inverseRelation?.foreignKeys[0]; // find foreign key for this entity
- // if (!foreignKey) {
- // throw new Error(
- // `Foreign key not found for entity ${repository.metadata.name} in relation ${x.propertyName}`
- // );
- // }
- // const id = (criteria as any)[foreignKey.referencedColumnNames[0]];
- // if (!id) throw new Error("id missing in criteria options " + foreignKey.referencedColumnNames);
-
- // if (x.relationType === "many-to-many") {
- // return getConnection()
- // .createQueryBuilder()
- // .relation(this, x.propertyName)
- // .of(id)
- // .remove({ [foreignKey.columnNames[0]]: id });
- // } else if (
- // x.relationType === "one-to-one" ||
- // x.relationType === "many-to-one" ||
- // x.relationType === "one-to-many"
- // ) {
- // return (x.inverseEntityMetadata.target as any).delete({ [foreignKey.columnNames[0]]: id });
- // }
- // });
- // await Promise.all(promises);
- // return super.delete(criteria, options);
- // }
}
export const PrimaryIdColumn = process.env.DATABASE?.startsWith("mongodb") ? ObjectIdColumn : PrimaryColumn;
diff --git a/util/src/entities/Channel.ts b/util/src/entities/Channel.ts
index 51d8b026..bd2e5a58 100644
--- a/util/src/entities/Channel.ts
+++ b/util/src/entities/Channel.ts
@@ -39,7 +39,7 @@ export class Channel extends BaseClass {
@Column({ type: "text", nullable: true })
icon?: string | null;
- @Column({ type: "simple-enum", enum: ChannelType })
+ @Column({ type: "int" })
type: ChannelType;
@OneToMany(() => Recipient, (recipient: Recipient) => recipient.channel, {
diff --git a/util/src/entities/Config.ts b/util/src/entities/Config.ts
index 813649ac..b3167ac7 100644
--- a/util/src/entities/Config.ts
+++ b/util/src/entities/Config.ts
@@ -51,11 +51,6 @@ export interface ConfigValue {
general: {
instanceId: string;
};
- permissions: {
- user: {
- createGuilds: boolean;
- };
- };
limits: {
user: {
maxGuilds: number;
@@ -64,6 +59,7 @@ export interface ConfigValue {
};
guild: {
maxRoles: number;
+ maxEmojis: number;
maxMembers: number;
maxChannels: number;
maxChannelsInCategory: number;
@@ -153,6 +149,11 @@ export interface ConfigValue {
canLeave: boolean;
};
};
+ gif: {
+ enabled: boolean;
+ provider: "tenor"; // more coming soon
+ apiKey?: string;
+ };
rabbitmq: {
host: string | null;
};
@@ -175,11 +176,6 @@ export const DefaultConfigOptions: ConfigValue = {
general: {
instanceId: Snowflake.generate(),
},
- permissions: {
- user: {
- createGuilds: true,
- },
- },
limits: {
user: {
maxGuilds: 100,
@@ -188,6 +184,7 @@ export const DefaultConfigOptions: ConfigValue = {
},
guild: {
maxRoles: 250,
+ maxEmojis: 50, // TODO: max emojis per guild per nitro level
maxMembers: 250000,
maxChannels: 500,
maxChannelsInCategory: 50,
@@ -305,7 +302,6 @@ export const DefaultConfigOptions: ConfigValue = {
},
],
},
-
guild: {
showAllGuildsInDiscovery: false,
autoJoin: {
@@ -314,6 +310,11 @@ export const DefaultConfigOptions: ConfigValue = {
guilds: [],
},
},
+ gif: {
+ enabled: true,
+ provider: "tenor",
+ apiKey: "LIVDSRZULELA",
+ },
rabbitmq: {
host: null,
},
diff --git a/util/src/entities/Emoji.ts b/util/src/entities/Emoji.ts
index a252d9f4..03218375 100644
--- a/util/src/entities/Emoji.ts
+++ b/util/src/entities/Emoji.ts
@@ -1,4 +1,5 @@
-import { Column, Entity, JoinColumn, ManyToOne } from "typeorm";
+import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
+import { User } from ".";
import { BaseClass } from "./BaseClass";
import { Guild } from "./Guild";
import { Role } from "./Role";
@@ -20,6 +21,14 @@ export class Emoji extends BaseClass {
})
guild: Guild;
+ @Column({ nullable: true })
+ @RelationId((emoji: Emoji) => emoji.user)
+ user_id: string;
+
+ @JoinColumn({ name: "user_id" })
+ @ManyToOne(() => User)
+ user: User;
+
@Column()
managed: boolean;
@@ -28,4 +37,7 @@ export class Emoji extends BaseClass {
@Column()
require_colons: boolean;
+
+ @Column({ type: "simple-array" })
+ roles: string[]; // roles this emoji is whitelisted to (new discord feature?)
}
diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts
index 35595191..157f0921 100644
--- a/util/src/entities/Guild.ts
+++ b/util/src/entities/Guild.ts
@@ -258,14 +258,6 @@ export class Guild extends BaseClass {
unavailable?: boolean;
@Column({ nullable: true })
- @RelationId((guild: Guild) => guild.vanity_url)
- vanity_url_code?: string;
-
- @JoinColumn({ name: "vanity_url_code" })
- @ManyToOne(() => Invite)
- vanity_url?: Invite;
-
- @Column({ nullable: true })
verification_level?: number;
@Column({ type: "simple-json" })
diff --git a/util/src/entities/Invite.ts b/util/src/entities/Invite.ts
index 82556fab..b3e00957 100644
--- a/util/src/entities/Invite.ts
+++ b/util/src/entities/Invite.ts
@@ -1,6 +1,6 @@
-import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
+import { Column, Entity, JoinColumn, ManyToOne, RelationId, PrimaryColumn } from "typeorm";
import { Member } from "./Member";
-import { BaseClass, PrimaryIdColumn } from "./BaseClass";
+import { BaseClassWithoutId } from "./BaseClass";
import { Channel } from "./Channel";
import { Guild } from "./Guild";
import { User } from "./User";
@@ -8,8 +8,8 @@ import { User } from "./User";
export const PublicInviteRelation = ["inviter", "guild", "channel"];
@Entity("invites")
-export class Invite extends BaseClass {
- @PrimaryIdColumn()
+export class Invite extends BaseClassWithoutId {
+ @PrimaryColumn()
code: string;
@Column()
@@ -71,6 +71,9 @@ export class Invite extends BaseClass {
@Column({ nullable: true })
target_user_type?: number;
+ @Column({ nullable: true})
+ vanity_url?: boolean;
+
static async joinGuild(user_id: string, code: string) {
const invite = await Invite.findOneOrFail({ code });
if (invite.uses++ >= invite.max_uses && invite.max_uses !== 0) await Invite.delete({ code });
diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts
index 04c3c7aa..63cd6ad3 100644
--- a/util/src/entities/Message.ts
+++ b/util/src/entities/Message.ts
@@ -46,9 +46,6 @@ export enum MessageType {
@Entity("messages")
export class Message extends BaseClass {
- @Column()
- id: string;
-
@Column({ nullable: true })
@RelationId((message: Message) => message.channel)
channel_id: string;
@@ -151,7 +148,7 @@ export class Message extends BaseClass {
@Column({ nullable: true })
pinned?: boolean;
- @Column({ type: "simple-enum", enum: MessageType })
+ @Column({ type: "int" })
type: MessageType;
@Column({ type: "simple-json", nullable: true })
diff --git a/util/src/entities/RateLimit.ts b/util/src/entities/RateLimit.ts
index fa9c32c1..f5916f6b 100644
--- a/util/src/entities/RateLimit.ts
+++ b/util/src/entities/RateLimit.ts
@@ -3,9 +3,6 @@ import { BaseClass } from "./BaseClass";
@Entity("rate_limits")
export class RateLimit extends BaseClass {
- @Column()
- id: "global" | "error" | string; // channel_239842397 | guild_238927349823 | webhook_238923423498
-
@Column() // no relation as it also
executor_id: string;
diff --git a/util/src/entities/ReadState.ts b/util/src/entities/ReadState.ts
index 68e867a0..89480e83 100644
--- a/util/src/entities/ReadState.ts
+++ b/util/src/entities/ReadState.ts
@@ -1,4 +1,4 @@
-import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
+import { Column, Entity, Index, JoinColumn, ManyToOne, RelationId } from "typeorm";
import { BaseClass } from "./BaseClass";
import { Channel } from "./Channel";
import { Message } from "./Message";
@@ -9,8 +9,9 @@ import { User } from "./User";
// public read receipt ≥ notification cursor ≥ private fully read marker
@Entity("read_states")
+@Index(["channel_id", "user_id"], { unique: true })
export class ReadState extends BaseClass {
- @Column({ nullable: true })
+ @Column()
@RelationId((read_state: ReadState) => read_state.channel)
channel_id: string;
@@ -20,7 +21,7 @@ export class ReadState extends BaseClass {
})
channel: Channel;
- @Column({ nullable: true })
+ @Column()
@RelationId((read_state: ReadState) => read_state.user)
user_id: string;
@@ -35,15 +36,15 @@ export class ReadState extends BaseClass {
last_message_id: string;
@JoinColumn({ name: "last_message_id" })
- @ManyToOne(() => Message)
+ @ManyToOne(() => Message, { nullable: true })
last_message?: Message;
@Column({ nullable: true })
last_pin_timestamp?: Date;
- @Column()
+ @Column({ nullable: true })
mention_count: number;
- @Column()
+ @Column({ nullable: true })
manual: boolean;
}
diff --git a/util/src/entities/Relationship.ts b/util/src/entities/Relationship.ts
index e016b36b..c3592c76 100644
--- a/util/src/entities/Relationship.ts
+++ b/util/src/entities/Relationship.ts
@@ -35,7 +35,7 @@ export class Relationship extends BaseClass {
@Column({ nullable: true })
nickname?: string;
- @Column({ type: "simple-enum", enum: RelationshipType })
+ @Column({ type: "int" })
type: RelationshipType;
toPublicRelationship() {
diff --git a/util/src/entities/Sticker.ts b/util/src/entities/Sticker.ts
index ab224d1d..036ff2d0 100644
--- a/util/src/entities/Sticker.ts
+++ b/util/src/entities/Sticker.ts
@@ -36,9 +36,9 @@ export class Sticker extends BaseClass {
})
guild?: Guild;
- @Column({ type: "simple-enum", enum: StickerType })
+ @Column({ type: "int" })
type: StickerType;
- @Column({ type: "simple-enum", enum: StickerFormatType })
+ @Column({ type: "int" })
format_type: StickerFormatType;
}
diff --git a/util/src/entities/TeamMember.ts b/util/src/entities/TeamMember.ts
index bdfdccf0..b726e1e8 100644
--- a/util/src/entities/TeamMember.ts
+++ b/util/src/entities/TeamMember.ts
@@ -9,7 +9,7 @@ export enum TeamMemberState {
@Entity("team_members")
export class TeamMember extends BaseClass {
- @Column({ type: "simple-enum", enum: TeamMemberState })
+ @Column({ type: "int" })
membership_state: TeamMemberState;
@Column({ type: "simple-array" })
diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts
index 97564af3..04f1e9cb 100644
--- a/util/src/entities/User.ts
+++ b/util/src/entities/User.ts
@@ -198,7 +198,7 @@ export class User extends BaseClass {
// randomly generates a discriminator between 1 and 9999 and checks max five times if it already exists
// if it all five times already exists, abort with USERNAME_TOO_MANY_USERS error
// else just continue
- // TODO: is there any better way to generate a random discriminator only once, without checking if it already exists in the mongodb database?
+ // TODO: is there any better way to generate a random discriminator only once, without checking if it already exists in the database?
for (let tries = 0; tries < 5; tries++) {
discriminator = Math.randomIntBetween(1, 9999).toString().padStart(4, "0");
exists = await User.findOne({ where: { discriminator, username: username }, select: ["id"] });
@@ -219,7 +219,7 @@ export class User extends BaseClass {
// if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false
const language = req.language === "en" ? "en-US" : req.language || "en-US";
- const user = await new User({
+ const user = new User({
created_at: new Date(),
username: username,
discriminator,
@@ -246,7 +246,9 @@ export class User extends BaseClass {
},
settings: { ...defaultSettings, locale: language },
fingerprints: [],
- }).save();
+ });
+
+ await user.save();
if (Config.get().guild.autoJoin.enabled) {
for (const guild of Config.get().guild.autoJoin.guilds || []) {
diff --git a/util/src/entities/Webhook.ts b/util/src/entities/Webhook.ts
index 8382435f..89538417 100644
--- a/util/src/entities/Webhook.ts
+++ b/util/src/entities/Webhook.ts
@@ -12,10 +12,7 @@ export enum WebhookType {
@Entity("webhooks")
export class Webhook extends BaseClass {
- @Column()
- id: string;
-
- @Column({ type: "simple-enum", enum: WebhookType })
+ @Column({ type: "int" })
type: WebhookType;
@Column({ nullable: true })
diff --git a/util/src/index.ts b/util/src/index.ts
index fc00d46b..ae0f7e54 100644
--- a/util/src/index.ts
+++ b/util/src/index.ts
@@ -1,12 +1,6 @@
import "reflect-metadata";
-// export * as Constants from "../util/Constants";
export * from "./util/index";
export * from "./interfaces/index";
export * from "./entities/index";
export * from "./dtos/index";
-
-// import Config from "../util/Config";
-// import db, { MongooseCache, toObject } from "./util/Database";
-
-// export { Config };
diff --git a/util/src/interfaces/Event.ts b/util/src/interfaces/Event.ts
index 03099bbb..3c8ab8ab 100644
--- a/util/src/interfaces/Event.ts
+++ b/util/src/interfaces/Event.ts
@@ -185,8 +185,8 @@ export interface GuildBanRemoveEvent extends Event {
};
}
-export interface GuildEmojiUpdateEvent extends Event {
- event: "GUILD_EMOJI_UPDATE";
+export interface GuildEmojisUpdateEvent extends Event {
+ event: "GUILD_EMOJIS_UPDATE";
data: {
guild_id: string;
emojis: Emoji[];
@@ -459,7 +459,7 @@ export type EventData =
| GuildDeleteEvent
| GuildBanAddEvent
| GuildBanRemoveEvent
- | GuildEmojiUpdateEvent
+ | GuildEmojisUpdateEvent
| GuildIntegrationUpdateEvent
| GuildMemberAddEvent
| GuildMemberRemoveEvent
@@ -552,7 +552,7 @@ export type EVENT =
| "GUILD_DELETE"
| "GUILD_BAN_ADD"
| "GUILD_BAN_REMOVE"
- | "GUILD_EMOJI_UPDATE"
+ | "GUILD_EMOJIS_UPDATE"
| "GUILD_INTEGRATIONS_UPDATE"
| "GUILD_MEMBER_ADD"
| "GUILD_MEMBER_REMOVE"
diff --git a/util/src/migrations/1633864260873-EmojiRoles.ts b/util/src/migrations/1633864260873-EmojiRoles.ts
new file mode 100644
index 00000000..f0d709f2
--- /dev/null
+++ b/util/src/migrations/1633864260873-EmojiRoles.ts
@@ -0,0 +1,13 @@
+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
new file mode 100644
index 00000000..982405d7
--- /dev/null
+++ b/util/src/migrations/1633864669243-EmojiUser.ts
@@ -0,0 +1,23 @@
+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
new file mode 100644
index 00000000..af9b98ae
--- /dev/null
+++ b/util/src/migrations/1633881705509-VanityInvite.ts
@@ -0,0 +1,17 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class VanityInvite1633881705509 implements MigrationInterface {
+ 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/migrate_db_engine.js b/util/src/migrations/migrate_db_engine.js
new file mode 100644
index 00000000..79e9d86f
--- /dev/null
+++ b/util/src/migrations/migrate_db_engine.js
@@ -0,0 +1,109 @@
+const { config } = require("dotenv");
+config();
+const { createConnection } = require("typeorm");
+const { initDatabase } = require("../../dist/util/Database");
+require("missing-native-js-functions");
+const {
+ Application,
+ Attachment,
+ Ban,
+ Channel,
+ ConfigEntity,
+ ConnectedAccount,
+ Emoji,
+ Guild,
+ Invite,
+ Member,
+ Message,
+ ReadState,
+ Recipient,
+ Relationship,
+ Role,
+ Sticker,
+ Team,
+ TeamMember,
+ Template,
+ User,
+ VoiceState,
+ Webhook,
+} = require("../../dist/entities/index");
+
+async function main() {
+ if (!process.env.TO) throw new Error("TO database env connection string not set");
+
+ // manually arrange them because of foreign keys
+ const entities = [
+ ConfigEntity,
+ User,
+ Guild,
+ Channel,
+ Invite,
+ Role,
+ Ban,
+ Application,
+ Emoji,
+ ConnectedAccount,
+ Member,
+ ReadState,
+ Recipient,
+ Relationship,
+ Sticker,
+ Team,
+ TeamMember,
+ Template,
+ VoiceState,
+ Webhook,
+ Message,
+ Attachment,
+ ];
+
+ const oldDB = await initDatabase();
+
+ const type = process.env.TO.includes("://") ? process.env.TO.split(":")[0]?.replace("+srv", "") : "sqlite";
+ const isSqlite = type.includes("sqlite");
+
+ // @ts-ignore
+ const newDB = await createConnection({
+ type,
+ url: isSqlite ? undefined : process.env.TO,
+ database: isSqlite ? process.env.TO : undefined,
+ entities,
+ name: "new",
+ synchronize: true,
+ });
+ let i = 0;
+
+ try {
+ for (const entity of entities) {
+ const entries = await oldDB.manager.find(entity);
+
+ // @ts-ignore
+ console.log("migrating " + entries.length + " " + entity.name + " ...");
+
+ for (const entry of entries) {
+ console.log(i++);
+
+ try {
+ await newDB.manager.insert(entity, entry);
+ } catch (error) {
+ try {
+ if (!entry.id) throw new Error("object doesn't have a unique id: " + entry);
+ await newDB.manager.update(entity, { id: entry.id }, entry);
+ } catch (error) {
+ console.error("couldn't migrate " + i + " " + entity.name, error);
+ }
+ }
+ }
+
+ // @ts-ignore
+ console.log("migrated " + entries.length + " " + entity.name);
+ }
+ } catch (error) {
+ console.error(error.message);
+ }
+
+ console.log("SUCCESS migrated all data");
+ await newDB.close();
+}
+
+main().caught();
diff --git a/util/src/migrations/migrate_db_engine.ts b/util/src/migrations/migrate_db_engine.ts
deleted file mode 100644
index 33024a8d..00000000
--- a/util/src/migrations/migrate_db_engine.ts
+++ /dev/null
@@ -1,129 +0,0 @@
-import { config } from "dotenv";
-config();
-import { BaseEntity, createConnection, EntityTarget } from "typeorm";
-import { initDatabase } from "../util/Database";
-import "missing-native-js-functions";
-import {
- Application,
- Attachment,
- Ban,
- Channel,
- ConnectedAccount,
- defaultSettings,
- Emoji,
- Guild,
- Invite,
- Member,
- Message,
- RateLimit,
- ReadState,
- Recipient,
- Relationship,
- Role,
- Sticker,
- Team,
- TeamMember,
- Template,
- User,
- VoiceState,
- Webhook,
-} from "..";
-
-async function main() {
- if (!process.env.FROM) throw new Error("FROM database env connection string not set");
-
- // manually arrange them because of foreign key
- const entities = [
- User,
- Guild,
- Channel,
- Invite,
- Role,
- Ban,
- Application,
- Emoji,
- ConnectedAccount,
- Member,
- ReadState,
- Recipient,
- Relationship,
- Sticker,
- Team,
- TeamMember,
- Template,
- VoiceState,
- Webhook,
- Message,
- Attachment,
- ];
-
- const newDB = await initDatabase();
-
- // @ts-ignore
- const oldDB = await createConnection({
- type: process.env.FROM.split(":")[0]?.replace("+srv", ""),
- url: process.env.FROM,
- entities,
- name: "old",
- });
- let i = 0;
-
- try {
- for (const e of entities) {
- const entity = e as EntityTarget<any>;
- const entries = await oldDB.manager.find(entity);
- //@ts-ignore
- console.log("migrated " + entries.length + " " + entity.name);
-
- for (const entry of entries) {
- console.log(i++);
-
- if (entry instanceof User) {
- console.log("instance of User");
- if (entry.bio == null) entry.bio = "";
- if (entry.rights == null) entry.rights = "0";
- if (entry.disabled == null) entry.disabled = false;
- if (entry.fingerprints == null) entry.fingerprints = [];
- if (entry.deleted == null) entry.deleted = false;
- if (entry.data == null) {
- entry.data = {
- valid_tokens_since: new Date(0),
- hash: undefined,
- };
- // @ts-ignore
- if (entry.user_data) {
- // TODO: relationships
- entry.data = {
- // @ts-ignore
- valid_tokens_since: entry.user_data.valid_tokens_since, // @ts-ignore
- hash: entry.user_data.hash,
- };
- }
- }
- // @ts-ignore
- if (entry.settings == null) {
- entry.settings = defaultSettings;
- // @ts-ignore
- if (entry.user_data) entry.settings = entry.user_settings;
- }
- }
-
- // try {
- await newDB.manager.insert(entity, entry);
- // } catch (error) {
- // if (!entry.id) throw new Error("object doesn't have a unique id: " + entry);
- // await newDB.manager.update(entity, { id: entry.id }, entry);
- // }
- }
- // @ts-ignore
- console.log("migrated all " + entity.name);
- }
- } catch (error) {
- console.error((error as any).message);
- }
-
- console.log("SUCCESS migrated all data");
- await newDB.close();
-}
-
-main().caught();
diff --git a/util/src/util/Config.ts b/util/src/util/Config.ts
index 255f425d..704f3f2f 100644
--- a/util/src/util/Config.ts
+++ b/util/src/util/Config.ts
@@ -47,16 +47,18 @@ function pairsToConfig(pairs: ConfigEntity[]) {
pairs.forEach((p) => {
const keys = p.key.split("_");
- let prev = "";
let obj = value;
+ let prev = "";
+ let prevObj = obj;
let i = 0;
for (const key of keys) {
- if (Number(key) && !obj[prev]) obj = obj[prev] = [];
+ 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];
}
});
diff --git a/util/src/util/Event.ts b/util/src/util/Event.ts
index bf9547b1..8ed009d5 100644
--- a/util/src/util/Event.ts
+++ b/util/src/util/Event.ts
@@ -46,7 +46,9 @@ export async function listenEvent(event: string, callback: (event: EventOpts) =>
} else {
const cancel = () => {
events.removeListener(event, callback);
+ events.setMaxListeners(events.getMaxListeners() - 1);
};
+ events.setMaxListeners(events.getMaxListeners() + 1);
events.addListener(event, (opts) => callback({ ...opts, cancel }));
return cancel;
diff --git a/util/src/util/Rights.ts b/util/src/util/Rights.ts
index a266e4f7..5edd9142 100644
--- a/util/src/util/Rights.ts
+++ b/util/src/util/Rights.ts
@@ -30,7 +30,7 @@ export class Rights extends BitField {
MANAGE_MESSAGES: BitFlag(3), // Can't see other messages but delete/edit them in channels that they can see
MANAGE_RATE_LIMITS: BitFlag(4),
MANAGE_ROUTING: BitFlag(5), // can create custom message routes to any channel/guild
- MANAGE_TICKETS: BitFlag(6),
+ MANAGE_TICKETS: BitFlag(6), // can respond to and resolve support tickets
MANAGE_USERS: BitFlag(7),
ADD_MEMBERS: BitFlag(8), // can manually add any members in their guilds
BYPASS_RATE_LIMITS: BitFlag(9),
@@ -39,7 +39,7 @@ export class Rights extends BitField {
CREATE_DMS: BitFlag(12),
CREATE_DM_GROUPS: BitFlag(13),
CREATE_GUILDS: BitFlag(14),
- CREATE_INVITES: BitFlag(15),
+ CREATE_INVITES: BitFlag(15), // can create mass invites in the guilds that they have CREATE_INSTANT_INVITE
CREATE_ROLES: BitFlag(16),
CREATE_TEMPLATES: BitFlag(17),
CREATE_WEBHOOKS: BitFlag(18),
@@ -50,9 +50,13 @@ export class Rights extends BitField {
SELF_EDIT_MESSAGES: BitFlag(23),
SELF_EDIT_NAME: BitFlag(24),
SEND_MESSAGES: BitFlag(25),
- USE_SCREEN: BitFlag(26),
+ USE_ACTIVITIES: BitFlag(26), // use (game) activities in voice channels (e.g. Watch together)
USE_VIDEO: BitFlag(27),
USE_VOICE: BitFlag(28),
+ INVITE_USERS: BitFlag(29), // can create user-specific invites in the guilds that they have INVITE_USERS
+ SELF_DELETE_DISABLE: BitFlag(30), // can disable/delete own account
+ DEBTABLE: BitFlag(31), // can use pay-to-use features
+ CREDITABLE: BitFlag(32) // can receive money from monetisation related features
};
any(permission: RightResolvable, checkOperator = true) {
diff --git a/util/src/util/TraverseDirectory.ts b/util/src/util/TraverseDirectory.ts
new file mode 100644
index 00000000..275b7dcc
--- /dev/null
+++ b/util/src/util/TraverseDirectory.ts
@@ -0,0 +1,10 @@
+import { Server, traverseDirectory } from "lambert-server";
+
+const DEFAULT_FILTER = /^([^\.].*)(?<!\.d)\.(js)$/;
+
+export function registerRoutes(server: Server, root: string) {
+ return traverseDirectory(
+ { dirname: root, recursive: true, filter: DEFAULT_FILTER },
+ server.registerRoute.bind(server, root)
+ );
+}
diff --git a/util/src/util/cdn.ts b/util/src/util/cdn.ts
index 2de23f5d..4dd0078a 100644
--- a/util/src/util/cdn.ts
+++ b/util/src/util/cdn.ts
@@ -26,7 +26,7 @@ export async function uploadFile(path: string, file: Express.Multer.File) {
}
export async function handleFile(path: string, body?: string): Promise<string | undefined> {
- if (!body || !body.startsWith("data:")) return body;
+ if (!body || !body.startsWith("data:")) return undefined;
try {
const mimetype = body.split(":")[1].split(";")[0];
const buffer = Buffer.from(body.split(",")[1], "base64");
diff --git a/util/src/util/index.ts b/util/src/util/index.ts
index 67583635..c5703468 100644
--- a/util/src/util/index.ts
+++ b/util/src/util/index.ts
@@ -17,3 +17,4 @@ export * from "./Rights";
export * from "./Snowflake";
export * from "./String";
export * from "./Array";
+export * from "./TraverseDirectory";
|