diff options
author | Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> | 2021-08-12 20:33:42 +0200 |
---|---|---|
committer | Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> | 2021-08-12 20:33:42 +0200 |
commit | 613ef19d2119449d516555ea2d2036d7f98c298d (patch) | |
tree | 30c22d96aea3da6f859a4690ce9fadcc97cddc3c /src/util | |
parent | :sparkles: util (diff) | |
download | server-613ef19d2119449d516555ea2d2036d7f98c298d.tar.xz |
:sparkles: rtc
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/BitField.ts | 143 | ||||
-rw-r--r-- | src/util/Config.ts | 284 | ||||
-rw-r--r-- | src/util/Constants.ts | 28 | ||||
-rw-r--r-- | src/util/Database.ts | 151 | ||||
-rw-r--r-- | src/util/Intents.ts | 21 | ||||
-rw-r--r-- | src/util/MessageFlags.ts | 14 | ||||
-rw-r--r-- | src/util/MongoBigInt.ts | 82 | ||||
-rw-r--r-- | src/util/Permissions.ts | 262 | ||||
-rw-r--r-- | src/util/RabbitMQ.ts | 18 | ||||
-rw-r--r-- | src/util/Regex.ts | 3 | ||||
-rw-r--r-- | src/util/Snowflake.ts | 127 | ||||
-rw-r--r-- | src/util/String.ts | 7 | ||||
-rw-r--r-- | src/util/UserFlags.ts | 22 | ||||
-rw-r--r-- | src/util/checkToken.ts | 24 | ||||
-rw-r--r-- | src/util/index.ts | 9 | ||||
-rw-r--r-- | src/util/toBigInt.ts | 3 |
16 files changed, 0 insertions, 1198 deletions
diff --git a/src/util/BitField.ts b/src/util/BitField.ts deleted file mode 100644 index 728dc632..00000000 --- a/src/util/BitField.ts +++ /dev/null @@ -1,143 +0,0 @@ -"use strict"; - -// https://github.com/discordjs/discord.js/blob/master/src/util/BitField.js -// Apache License Version 2.0 Copyright 2015 - 2021 Amish Shah - -export type BitFieldResolvable = number | BigInt | BitField | string | BitFieldResolvable[]; - -/** - * Data structure that makes it easy to interact with a bitfield. - */ -export class BitField { - public bitfield: bigint = BigInt(0); - - public static FLAGS: Record<string, bigint> = {}; - - constructor(bits: BitFieldResolvable = 0) { - this.bitfield = BitField.resolve.call(this, bits); - } - - /** - * Checks whether the bitfield has a bit, or any of multiple bits. - */ - any(bit: BitFieldResolvable): boolean { - return (this.bitfield & BitField.resolve.call(this, bit)) !== 0n; - } - - /** - * Checks if this bitfield equals another - */ - equals(bit: BitFieldResolvable): boolean { - return this.bitfield === BitField.resolve.call(this, bit); - } - - /** - * Checks whether the bitfield has a bit, or multiple bits. - */ - has(bit: BitFieldResolvable): boolean { - if (Array.isArray(bit)) return bit.every((p) => this.has(p)); - const BIT = BitField.resolve.call(this, bit); - return (this.bitfield & BIT) === BIT; - } - - /** - * Gets all given bits that are missing from the bitfield. - */ - missing(bits: BitFieldResolvable) { - if (!Array.isArray(bits)) bits = new BitField(bits).toArray(); - return bits.filter((p) => !this.has(p)); - } - - /** - * Freezes these bits, making them immutable. - */ - freeze(): Readonly<BitField> { - return Object.freeze(this); - } - - /** - * Adds bits to these ones. - * @param {...BitFieldResolvable} [bits] Bits to add - * @returns {BitField} These bits or new BitField if the instance is frozen. - */ - add(...bits: BitFieldResolvable[]): BitField { - let total = 0n; - for (const bit of bits) { - total |= BitField.resolve.call(this, bit); - } - if (Object.isFrozen(this)) return new BitField(this.bitfield | total); - this.bitfield |= total; - return this; - } - - /** - * Removes bits from these. - * @param {...BitFieldResolvable} [bits] Bits to remove - */ - remove(...bits: BitFieldResolvable[]) { - let total = 0n; - for (const bit of bits) { - total |= BitField.resolve.call(this, bit); - } - if (Object.isFrozen(this)) return new BitField(this.bitfield & ~total); - this.bitfield &= ~total; - return this; - } - - /** - * Gets an object mapping field names to a {@link boolean} indicating whether the - * bit is available. - * @param {...*} hasParams Additional parameters for the has method, if any - */ - serialize() { - const serialized: Record<string, boolean> = {}; - for (const [flag, bit] of Object.entries(BitField.FLAGS)) serialized[flag] = this.has(bit); - return serialized; - } - - /** - * Gets an {@link Array} of bitfield names based on the bits available. - */ - toArray(): string[] { - return Object.keys(BitField.FLAGS).filter((bit) => this.has(bit)); - } - - toJSON() { - return this.bitfield; - } - - valueOf() { - return this.bitfield; - } - - *[Symbol.iterator]() { - yield* this.toArray(); - } - - /** - * Data that can be resolved to give a bitfield. This can be: - * * A bit number (this can be a number literal or a value taken from {@link BitField.FLAGS}) - * * An instance of BitField - * * An Array of BitFieldResolvable - * @typedef {number|BitField|BitFieldResolvable[]} BitFieldResolvable - */ - - /** - * Resolves bitfields to their numeric form. - * @param {BitFieldResolvable} [bit=0] - bit(s) to resolve - * @returns {number} - */ - static resolve(bit: BitFieldResolvable = 0n): bigint { - // @ts-ignore - const FLAGS = this.FLAGS || this.constructor?.FLAGS; - if ((typeof bit === "number" || typeof bit === "bigint") && bit >= 0n) return BigInt(bit); - if (bit instanceof BitField) return bit.bitfield; - if (Array.isArray(bit)) { - // @ts-ignore - const resolve = this.constructor?.resolve || this.resolve; - return bit.map((p) => resolve.call(this, p)).reduce((prev, p) => BigInt(prev) | BigInt(p), 0n); - } - if (typeof bit === "string" && typeof FLAGS[bit] !== "undefined") return FLAGS[bit]; - throw new RangeError("BITFIELD_INVALID: " + bit); - } -} diff --git a/src/util/Config.ts b/src/util/Config.ts deleted file mode 100644 index 78b44315..00000000 --- a/src/util/Config.ts +++ /dev/null @@ -1,284 +0,0 @@ -import { Schema, model, Types, Document } from "mongoose"; -import "missing-native-js-functions"; -import db, { MongooseCache } from "./Database"; -import { Snowflake } from "./Snowflake"; -import crypto from "crypto"; - -var config: any; - -export default { - init: async function init(defaultOpts: any = DefaultOptions) { - config = await db.collection("config").findOne({}); - return this.set((config || {}).merge(defaultOpts)); - }, - get: function get() { - return config as DefaultOptions; - }, - set: function set(val: any) { - return db.collection("config").updateOne({}, { $set: val }, { upsert: true }); - }, -}; - -export interface RateLimitOptions { - bot?: number; - count: number; - window: number; - onyIp?: boolean; -} - -export interface Region { - id: string; - name: string; - vip: boolean; - custom: boolean; - deprecated: boolean; - optimal: boolean; -} - -export interface KafkaBroker { - ip: string; - port: number; -} - -export interface DefaultOptions { - gateway: { - endpoint: string | null; - }; - cdn: { - endpoint: string | null; - }; - general: { - instance_id: string; - }; - permissions: { - user: { - createGuilds: boolean; - }; - }; - limits: { - user: { - maxGuilds: number; - maxUsername: number; - maxFriends: number; - }; - guild: { - maxRoles: number; - maxMembers: number; - maxChannels: number; - maxChannelsInCategory: number; - hideOfflineMember: number; - }; - message: { - maxCharacters: number; - maxTTSCharacters: number; - maxReactions: number; - maxAttachmentSize: number; - maxBulkDelete: number; - }; - channel: { - maxPins: number; - maxTopic: number; - }; - rate: { - ip: Omit<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: { - requestSignature: string; - jwtSecret: string; - forwadedFor: string | null; // header to get the real user ip address - captcha: { - enabled: boolean; - service: "recaptcha" | "hcaptcha" | null; // TODO: hcaptcha, custom - sitekey: string | null; - secret: string | null; - }; - ipdataApiKey: string | null; - }; - login: { - requireCaptcha: boolean; - }; - register: { - email: { - necessary: boolean; // we have to use necessary instead of required as the cli tool uses json schema and can't use required - allowlist: boolean; - blocklist: boolean; - domains: string[]; - }; - dateOfBirth: { - necessary: boolean; - minimum: number; // in years - }; - requireCaptcha: boolean; - requireInvite: boolean; - allowNewRegistration: boolean; - allowMultipleAccounts: boolean; - blockProxies: boolean; - password: { - minLength: number; - minNumbers: number; - minUpperCase: number; - minSymbols: number; - }; - }; - regions: { - default: string; - available: Region[]; - }; - rabbitmq: { - host: string | null; - }; - kafka: { - brokers: KafkaBroker[] | null; - }; -} - -export const DefaultOptions: DefaultOptions = { - gateway: { - endpoint: null, - }, - cdn: { - endpoint: null, - }, - general: { - instance_id: Snowflake.generate(), - }, - permissions: { - user: { - createGuilds: true, - }, - }, - limits: { - user: { - maxGuilds: 100, - maxUsername: 32, - maxFriends: 1000, - }, - guild: { - maxRoles: 250, - maxMembers: 250000, - maxChannels: 500, - maxChannelsInCategory: 50, - hideOfflineMember: 1000, - }, - message: { - maxCharacters: 2000, - maxTTSCharacters: 200, - maxReactions: 20, - maxAttachmentSize: 8388608, - maxBulkDelete: 100, - }, - channel: { - maxPins: 50, - maxTopic: 1024, - }, - rate: { - ip: { - count: 500, - window: 5, - }, - global: { - count: 20, - window: 5, - bot: 250, - }, - error: { - count: 10, - window: 5, - }, - routes: { - guild: { - count: 5, - window: 5, - }, - webhook: { - count: 5, - window: 5, - }, - channel: { - count: 5, - window: 5, - }, - auth: { - login: { - count: 5, - window: 60, - }, - register: { - count: 2, - window: 60 * 60 * 12, - }, - }, - }, - }, - }, - security: { - requestSignature: crypto.randomBytes(32).toString("base64"), - jwtSecret: crypto.randomBytes(256).toString("base64"), - forwadedFor: null, - // forwadedFor: "X-Forwarded-For" // nginx/reverse proxy - // forwadedFor: "CF-Connecting-IP" // cloudflare: - captcha: { - enabled: false, - service: null, - sitekey: null, - secret: null, - }, - ipdataApiKey: "eca677b284b3bac29eb72f5e496aa9047f26543605efe99ff2ce35c9", - }, - login: { - requireCaptcha: false, - }, - register: { - email: { - necessary: true, - allowlist: false, - blocklist: true, - domains: [], // TODO: efficiently save domain blocklist in database - // domains: fs.readFileSync(__dirname + "/blockedEmailDomains.txt", { encoding: "utf8" }).split("\n"), - }, - dateOfBirth: { - necessary: true, - minimum: 13, - }, - requireInvite: false, - requireCaptcha: true, - allowNewRegistration: true, - allowMultipleAccounts: true, - blockProxies: true, - password: { - minLength: 8, - minNumbers: 2, - minUpperCase: 2, - minSymbols: 0, - }, - }, - regions: { - default: "fosscord", - available: [{ id: "fosscord", name: "Fosscord", vip: false, custom: false, deprecated: false, optimal: false }], - }, - rabbitmq: { - host: null, - }, - kafka: { - brokers: null, - }, -}; - -export const ConfigSchema = new Schema({}, { strict: false }); - -export interface DefaultOptionsDocument extends DefaultOptions, Document {} - -export const ConfigModel = model<DefaultOptionsDocument>("Config", ConfigSchema, "config"); diff --git a/src/util/Constants.ts b/src/util/Constants.ts deleted file mode 100644 index a9978c51..00000000 --- a/src/util/Constants.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { VerifyOptions } from "jsonwebtoken"; - -export const JWTOptions: VerifyOptions = { algorithms: ["HS256"] }; - -export enum MessageType { - DEFAULT = 0, - RECIPIENT_ADD = 1, - RECIPIENT_REMOVE = 2, - CALL = 3, - CHANNEL_NAME_CHANGE = 4, - CHANNEL_ICON_CHANGE = 5, - CHANNEL_PINNED_MESSAGE = 6, - GUILD_MEMBER_JOIN = 7, - USER_PREMIUM_GUILD_SUBSCRIPTION = 8, - USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1 = 9, - USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2 = 10, - USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3 = 11, - CHANNEL_FOLLOW_ADD = 12, - GUILD_DISCOVERY_DISQUALIFIED = 14, - GUILD_DISCOVERY_REQUALIFIED = 15, - GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING = 16, - GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING = 17, - THREAD_CREATED = 18, - REPLY = 19, - APPLICATION_COMMAND = 20, - THREAD_STARTER_MESSAGE = 21, - GUILD_INVITE_REMINDER = 22, -} diff --git a/src/util/Database.ts b/src/util/Database.ts deleted file mode 100644 index 8c6847a8..00000000 --- a/src/util/Database.ts +++ /dev/null @@ -1,151 +0,0 @@ -import "./MongoBigInt"; -import mongoose, { Collection, Connection, LeanDocument } from "mongoose"; -import { ChangeStream, ChangeEvent, Long } from "mongodb"; -import EventEmitter from "events"; -const uri = process.env.MONGO_URL || "mongodb://localhost:27017/fosscord?readPreference=secondaryPreferred"; -import { URL } from "url"; - -const url = new URL(uri.replace("mongodb://", "http://")); - -const connection = mongoose.createConnection(uri, { - autoIndex: true, - useNewUrlParser: true, - useUnifiedTopology: true, - useFindAndModify: false, -}); -console.log(`[Database] connect: mongodb://${url.username}@${url.host}${url.pathname}${url.search}`); - -export default <Connection>connection; - -function transform<T>(document: T) { - // @ts-ignore - if (!document || !document.toObject) { - try { - // @ts-ignore - delete document._id; - // @ts-ignore - delete document.__v; - } catch (error) {} - return document; - } - // @ts-ignore - return document.toObject({ virtuals: true }); -} - -export function toObject<T>(document: T): LeanDocument<T> { - // @ts-ignore - return Array.isArray(document) ? document.map((x) => transform<T>(x)) : transform(document); -} - -export interface MongooseCache { - on(event: "delete", listener: (id: string) => void): this; - on(event: "change", listener: (data: any) => void): this; - on(event: "insert", listener: (data: any) => void): this; - on(event: "close", listener: () => void): this; -} - -export class MongooseCache extends EventEmitter { - public stream: ChangeStream; - public data: any; - public initalizing?: Promise<void>; - - constructor( - public collection: Collection, - public pipeline: Array<Record<string, unknown>>, - public opts: { - onlyEvents: boolean; - array?: boolean; - } - ) { - super(); - if (this.opts.array == null) this.opts.array = true; - } - - init = () => { - if (this.initalizing) return this.initalizing; - this.initalizing = new Promise(async (resolve, reject) => { - // @ts-ignore - this.stream = this.collection.watch(this.pipeline, { fullDocument: "updateLookup" }); - - this.stream.on("change", this.change); - this.stream.on("close", this.destroy); - this.stream.on("error", console.error); - - if (!this.opts.onlyEvents) { - const arr = await this.collection.aggregate(this.pipeline).toArray(); - if (this.opts.array) this.data = arr || []; - else this.data = arr?.[0]; - } - resolve(); - }); - return this.initalizing; - }; - - changeStream = (pipeline: any) => { - this.pipeline = pipeline; - this.destroy(); - this.init(); - }; - - convertResult = (obj: any) => { - if (obj instanceof Long) return BigInt(obj.toString()); - if (typeof obj === "object") { - Object.keys(obj).forEach((key) => { - obj[key] = this.convertResult(obj[key]); - }); - } - - return obj; - }; - - change = (doc: ChangeEvent) => { - try { - switch (doc.operationType) { - case "dropDatabase": - return this.destroy(); - case "drop": - return this.destroy(); - case "delete": - if (!this.opts.onlyEvents) { - if (this.opts.array) { - this.data = this.data.filter((x: any) => doc.documentKey?._id?.equals(x._id)); - } else this.data = null; - } - return this.emit("delete", doc.documentKey._id.toHexString()); - case "insert": - if (!this.opts.onlyEvents) { - if (this.opts.array) this.data.push(doc.fullDocument); - else this.data = doc.fullDocument; - } - return this.emit("insert", doc.fullDocument); - case "update": - case "replace": - if (!this.opts.onlyEvents) { - if (this.opts.array) { - const i = this.data.findIndex((x: any) => doc.fullDocument?._id?.equals(x._id)); - if (i == -1) this.data.push(doc.fullDocument); - else this.data[i] = doc.fullDocument; - } else this.data = doc.fullDocument; - } - - return this.emit("change", doc.fullDocument); - case "invalidate": - return this.destroy(); - default: - return; - } - } catch (error) { - this.emit("error", error); - } - }; - - destroy = () => { - this.data = null; - this.stream?.off("change", this.change); - this.emit("close"); - - if (this.stream.isClosed()) return; - - return this.stream.close(); - }; -} diff --git a/src/util/Intents.ts b/src/util/Intents.ts deleted file mode 100644 index 943b29cf..00000000 --- a/src/util/Intents.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { BitField } from "./BitField"; - -export class Intents extends BitField { - static FLAGS = { - GUILDS: BigInt(1) << BigInt(0), - GUILD_MEMBERS: BigInt(1) << BigInt(1), - GUILD_BANS: BigInt(1) << BigInt(2), - GUILD_EMOJIS: BigInt(1) << BigInt(3), - GUILD_INTEGRATIONS: BigInt(1) << BigInt(4), - GUILD_WEBHOOKS: BigInt(1) << BigInt(5), - GUILD_INVITES: BigInt(1) << BigInt(6), - GUILD_VOICE_STATES: BigInt(1) << BigInt(7), - GUILD_PRESENCES: BigInt(1) << BigInt(8), - GUILD_MESSAGES: BigInt(1) << BigInt(9), - GUILD_MESSAGE_REACTIONS: BigInt(1) << BigInt(10), - GUILD_MESSAGE_TYPING: BigInt(1) << BigInt(11), - DIRECT_MESSAGES: BigInt(1) << BigInt(12), - DIRECT_MESSAGE_REACTIONS: BigInt(1) << BigInt(13), - DIRECT_MESSAGE_TYPING: BigInt(1) << BigInt(14), - }; -} diff --git a/src/util/MessageFlags.ts b/src/util/MessageFlags.ts deleted file mode 100644 index c76be4c8..00000000 --- a/src/util/MessageFlags.ts +++ /dev/null @@ -1,14 +0,0 @@ -// https://github.com/discordjs/discord.js/blob/master/src/util/MessageFlags.js -// Apache License Version 2.0 Copyright 2015 - 2021 Amish Shah - -import { BitField } from "./BitField"; - -export class MessageFlags extends BitField { - static FLAGS = { - CROSSPOSTED: BigInt(1) << BigInt(0), - IS_CROSSPOST: BigInt(1) << BigInt(1), - SUPPRESS_EMBEDS: BigInt(1) << BigInt(2), - SOURCE_MESSAGE_DELETED: BigInt(1) << BigInt(3), - URGENT: BigInt(1) << BigInt(4), - }; -} diff --git a/src/util/MongoBigInt.ts b/src/util/MongoBigInt.ts deleted file mode 100644 index fc451925..00000000 --- a/src/util/MongoBigInt.ts +++ /dev/null @@ -1,82 +0,0 @@ -import mongoose from "mongoose"; - -class LongSchema extends mongoose.SchemaType { - public $conditionalHandlers = { - $lt: this.handleSingle, - $lte: this.handleSingle, - $gt: this.handleSingle, - $gte: this.handleSingle, - $ne: this.handleSingle, - $in: this.handleArray, - $nin: this.handleArray, - $mod: this.handleArray, - $all: this.handleArray, - $bitsAnySet: this.handleArray, - $bitsAllSet: this.handleArray, - }; - - handleSingle(val: any) { - return this.cast(val, null, null, "handle"); - } - - handleArray(val: any) { - var self = this; - return val.map(function (m: any) { - return self.cast(m, null, null, "handle"); - }); - } - - checkRequired(val: any) { - return null != val; - } - - cast(val: any, scope?: any, init?: any, type?: string) { - if (null === val) return val; - if ("" === val) return null; - if (typeof val === "bigint") { - return mongoose.mongo.Long.fromString(val.toString()); - } - - if (val instanceof mongoose.mongo.Long) { - if (type === "handle" || init == false) return val; - return BigInt(val.toString()); - } - if (val instanceof Number || "number" == typeof val) return BigInt(val); - if (!Array.isArray(val) && val.toString) return BigInt(val.toString()); - - //@ts-ignore - throw new SchemaType.CastError("Long", val); - } - - castForQuery($conditional: string, value: any) { - var handler; - if (2 === arguments.length) { - // @ts-ignore - handler = this.$conditionalHandlers[$conditional]; - if (!handler) { - throw new Error("Can't use " + $conditional + " with Long."); - } - return handler.call(this, value); - } else { - return this.cast($conditional, null, null, "query"); - } - } -} - -LongSchema.cast = mongoose.SchemaType.cast; -LongSchema.set = mongoose.SchemaType.set; -LongSchema.get = mongoose.SchemaType.get; - -declare module "mongoose" { - namespace Types { - class Long extends mongoose.mongo.Long {} - } - namespace Schema { - namespace Types { - class Long extends LongSchema {} - } - } -} - -mongoose.Schema.Types.Long = LongSchema; -mongoose.Types.Long = mongoose.mongo.Long; diff --git a/src/util/Permissions.ts b/src/util/Permissions.ts deleted file mode 100644 index 445e901f..00000000 --- a/src/util/Permissions.ts +++ /dev/null @@ -1,262 +0,0 @@ -// https://github.com/discordjs/discord.js/blob/master/src/util/Permissions.js -// Apache License Version 2.0 Copyright 2015 - 2021 Amish Shah -import { MemberDocument, MemberModel } from "../models/Member"; -import { ChannelDocument, ChannelModel } from "../models/Channel"; -import { ChannelPermissionOverwrite } from "../models/Channel"; -import { Role, RoleDocument, RoleModel } from "../models/Role"; -import { BitField } from "./BitField"; -import { GuildDocument, GuildModel } from "../models/Guild"; -// TODO: check role hierarchy permission - -var HTTPError: any; - -try { - HTTPError = require("lambert-server").HTTPError; -} catch (e) { - HTTPError = Error; -} - -export type PermissionResolvable = bigint | number | Permissions | PermissionResolvable[] | PermissionString; - -type PermissionString = - | "CREATE_INSTANT_INVITE" - | "KICK_MEMBERS" - | "BAN_MEMBERS" - | "ADMINISTRATOR" - | "MANAGE_CHANNELS" - | "MANAGE_GUILD" - | "ADD_REACTIONS" - | "VIEW_AUDIT_LOG" - | "PRIORITY_SPEAKER" - | "STREAM" - | "VIEW_CHANNEL" - | "SEND_MESSAGES" - | "SEND_TTS_MESSAGES" - | "MANAGE_MESSAGES" - | "EMBED_LINKS" - | "ATTACH_FILES" - | "READ_MESSAGE_HISTORY" - | "MENTION_EVERYONE" - | "USE_EXTERNAL_EMOJIS" - | "VIEW_GUILD_INSIGHTS" - | "CONNECT" - | "SPEAK" - | "MUTE_MEMBERS" - | "DEAFEN_MEMBERS" - | "MOVE_MEMBERS" - | "USE_VAD" - | "CHANGE_NICKNAME" - | "MANAGE_NICKNAMES" - | "MANAGE_ROLES" - | "MANAGE_WEBHOOKS" - | "MANAGE_EMOJIS"; - -const CUSTOM_PERMISSION_OFFSET = BigInt(1) << BigInt(48); // 16 free custom permission bits, and 16 for discord to add new ones - -export class Permissions extends BitField { - cache: PermissionCache = {}; - - static FLAGS = { - CREATE_INSTANT_INVITE: BigInt(1) << BigInt(0), - KICK_MEMBERS: BigInt(1) << BigInt(1), - BAN_MEMBERS: BigInt(1) << BigInt(2), - ADMINISTRATOR: BigInt(1) << BigInt(3), - MANAGE_CHANNELS: BigInt(1) << BigInt(4), - MANAGE_GUILD: BigInt(1) << BigInt(5), - ADD_REACTIONS: BigInt(1) << BigInt(6), - VIEW_AUDIT_LOG: BigInt(1) << BigInt(7), - PRIORITY_SPEAKER: BigInt(1) << BigInt(8), - STREAM: BigInt(1) << BigInt(9), - VIEW_CHANNEL: BigInt(1) << BigInt(10), - SEND_MESSAGES: BigInt(1) << BigInt(11), - SEND_TTS_MESSAGES: BigInt(1) << BigInt(12), - MANAGE_MESSAGES: BigInt(1) << BigInt(13), - EMBED_LINKS: BigInt(1) << BigInt(14), - ATTACH_FILES: BigInt(1) << BigInt(15), - READ_MESSAGE_HISTORY: BigInt(1) << BigInt(16), - MENTION_EVERYONE: BigInt(1) << BigInt(17), - USE_EXTERNAL_EMOJIS: BigInt(1) << BigInt(18), - VIEW_GUILD_INSIGHTS: BigInt(1) << BigInt(19), - CONNECT: BigInt(1) << BigInt(20), - SPEAK: BigInt(1) << BigInt(21), - MUTE_MEMBERS: BigInt(1) << BigInt(22), - DEAFEN_MEMBERS: BigInt(1) << BigInt(23), - MOVE_MEMBERS: BigInt(1) << BigInt(24), - USE_VAD: BigInt(1) << BigInt(25), - CHANGE_NICKNAME: BigInt(1) << BigInt(26), - MANAGE_NICKNAMES: BigInt(1) << BigInt(27), - MANAGE_ROLES: BigInt(1) << BigInt(28), - MANAGE_WEBHOOKS: BigInt(1) << BigInt(29), - MANAGE_EMOJIS: BigInt(1) << BigInt(30), - /** - * CUSTOM PERMISSIONS ideas: - * - allow user to dm members - * - allow user to pin messages (without MANAGE_MESSAGES) - * - allow user to publish messages (without MANAGE_MESSAGES) - */ - // CUSTOM_PERMISSION: BigInt(1) << BigInt(0) + CUSTOM_PERMISSION_OFFSET - }; - - any(permission: PermissionResolvable, checkAdmin = true) { - return (checkAdmin && super.any(Permissions.FLAGS.ADMINISTRATOR)) || super.any(permission); - } - - /** - * Checks whether the bitfield has a permission, or multiple permissions. - */ - has(permission: PermissionResolvable, checkAdmin = true) { - return (checkAdmin && super.has(Permissions.FLAGS.ADMINISTRATOR)) || super.has(permission); - } - - /** - * Checks whether the bitfield has a permission, or multiple permissions, but throws an Error if user fails to match auth criteria. - */ - hasThrow(permission: PermissionResolvable) { - if (this.has(permission) && this.has("VIEW_CHANNEL")) return true; - // @ts-ignore - throw new HTTPError(`You are missing the following permissions ${permission}`, 403); - } - - overwriteChannel(overwrites: ChannelPermissionOverwrite[]) { - if (!this.cache) throw new Error("permission chache not available"); - overwrites = overwrites.filter((x) => { - if (x.type === 0 && this.cache.roles?.some((r) => r.id === x.id)) return true; - if (x.type === 1 && x.id == this.cache.user_id) return true; - return false; - }); - return new Permissions(Permissions.channelPermission(overwrites, this.bitfield)); - } - - static channelPermission(overwrites: ChannelPermissionOverwrite[], init?: bigint) { - // TODO: do not deny any permissions if admin - return overwrites.reduce((permission, overwrite) => { - // apply disallowed permission - // * permission: current calculated permission (e.g. 010) - // * deny contains all denied permissions (e.g. 011) - // * allow contains all explicitly allowed permisions (e.g. 100) - return (permission & ~BigInt(overwrite.deny)) | BigInt(overwrite.allow); - // ~ operator inverts deny (e.g. 011 -> 100) - // & operator only allows 1 for both ~deny and permission (e.g. 010 & 100 -> 000) - // | operators adds both together (e.g. 000 + 100 -> 100) - }, init || 0n); - } - - static rolePermission(roles: Role[]) { - // adds all permissions of all roles together (Bit OR) - return roles.reduce((permission, role) => permission | BigInt(role.permissions), 0n); - } - - static finalPermission({ - user, - guild, - channel, - }: { - user: { id: string; roles: string[] }; - guild: { roles: Role[] }; - channel?: { - overwrites?: ChannelPermissionOverwrite[]; - recipient_ids?: string[] | null; - owner_id?: string; - }; - }) { - if (user.id === "0") return new Permissions("ADMINISTRATOR"); // system user id - - let roles = guild.roles.filter((x) => user.roles.includes(x.id)); - let permission = Permissions.rolePermission(roles); - - if (channel?.overwrites) { - let overwrites = channel.overwrites.filter((x) => { - if (x.type === 0 && user.roles.includes(x.id)) return true; - if (x.type === 1 && x.id == user.id) return true; - return false; - }); - permission = Permissions.channelPermission(overwrites, permission); - } - - if (channel?.recipient_ids) { - if (channel?.owner_id === user.id) return new Permissions("ADMINISTRATOR"); - if (channel.recipient_ids.includes(user.id)) { - // Default dm permissions - return new Permissions([ - "VIEW_CHANNEL", - "SEND_MESSAGES", - "STREAM", - "ADD_REACTIONS", - "EMBED_LINKS", - "ATTACH_FILES", - "READ_MESSAGE_HISTORY", - "MENTION_EVERYONE", - "USE_EXTERNAL_EMOJIS", - "CONNECT", - "SPEAK", - "MANAGE_CHANNELS", - ]); - } - - return new Permissions(); - } - - return new Permissions(permission); - } -} - -export type PermissionCache = { - channel?: ChannelDocument | null; - member?: MemberDocument | null; - guild?: GuildDocument | null; - roles?: RoleDocument[] | null; - user_id?: string; -}; - -export async function getPermission( - user_id?: string, - guild_id?: string, - channel_id?: string, - cache: PermissionCache = {} -) { - var { channel, member, guild, roles } = cache; - - if (!user_id) throw new HTTPError("User not found"); - - if (channel_id && !channel) { - channel = await ChannelModel.findOne( - { id: channel_id }, - { permission_overwrites: true, recipient_ids: true, owner_id: true, guild_id: true } - ).exec(); - if (!channel) throw new HTTPError("Channel not found", 404); - if (channel.guild_id) guild_id = channel.guild_id; - } - - if (guild_id) { - if (!guild) guild = await GuildModel.findOne({ id: guild_id }, { owner_id: true }).exec(); - if (!guild) throw new HTTPError("Guild not found"); - if (guild.owner_id === user_id) return new Permissions(Permissions.FLAGS.ADMINISTRATOR); - - if (!member) member = await MemberModel.findOne({ guild_id, id: user_id }, "roles").exec(); - if (!member) throw new HTTPError("Member not found"); - - if (!roles) roles = await RoleModel.find({ guild_id, id: { $in: member.roles } }).exec(); - } - - var permission = Permissions.finalPermission({ - user: { - id: user_id, - roles: member?.roles || [], - }, - guild: { - roles: roles || [], - }, - channel: { - overwrites: channel?.permission_overwrites, - owner_id: channel?.owner_id, - recipient_ids: channel?.recipient_ids, - }, - }); - - const obj = new Permissions(permission); - - // pass cache to permission for possible future getPermission calls - obj.cache = { guild, member, channel, roles, user_id }; - - return obj; -} diff --git a/src/util/RabbitMQ.ts b/src/util/RabbitMQ.ts deleted file mode 100644 index 9da41990..00000000 --- a/src/util/RabbitMQ.ts +++ /dev/null @@ -1,18 +0,0 @@ -import amqp, { Connection, Channel } from "amqplib"; -import Config from "./Config"; - -export const RabbitMQ: { connection: Connection | null; channel: Channel | null; init: () => Promise<void> } = { - connection: null, - channel: null, - init: async function () { - const host = Config.get().rabbitmq.host; - if (!host) return; - console.log(`[RabbitMQ] connect: ${host}`); - this.connection = await amqp.connect(host, { - timeout: 1000 * 60, - }); - console.log(`[RabbitMQ] connected`); - this.channel = await this.connection.createChannel(); - console.log(`[RabbitMQ] channel created`); - }, -}; diff --git a/src/util/Regex.ts b/src/util/Regex.ts deleted file mode 100644 index bbd48bca..00000000 --- a/src/util/Regex.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const DOUBLE_WHITE_SPACE = /\s\s+/g; -export const SPECIAL_CHAR = /[@#`:\r\n\t\f\v\p{C}]/gu; -export const CHANNEL_MENTION = /<#(\d+)>/g; diff --git a/src/util/Snowflake.ts b/src/util/Snowflake.ts deleted file mode 100644 index 1d725710..00000000 --- a/src/util/Snowflake.ts +++ /dev/null @@ -1,127 +0,0 @@ -// @ts-nocheck -import cluster from "cluster"; - -// https://github.com/discordjs/discord.js/blob/master/src/util/Snowflake.js -// Apache License Version 2.0 Copyright 2015 - 2021 Amish Shah -("use strict"); - -// Discord epoch (2015-01-01T00:00:00.000Z) - -/** - * A container for useful snowflake-related methods. - */ -export class Snowflake { - static readonly EPOCH = 1420070400000; - static INCREMENT = 0n; // max 4095 - static processId = BigInt(process.pid % 31); // max 31 - static workerId = BigInt((cluster.worker?.id || 0) % 31); // max 31 - - constructor() { - throw new Error(`The ${this.constructor.name} class may not be instantiated.`); - } - - /** - * A Twitter snowflake, except the epoch is 2015-01-01T00:00:00.000Z - * ``` - * If we have a snowflake '266241948824764416' we can represent it as binary: - * - * 64 22 17 12 0 - * 000000111011000111100001101001000101000000 00001 00000 000000000000 - * number of ms since Discord epoch worker pid increment - * ``` - * @typedef {string} Snowflake - */ - - /** - * Transforms a snowflake from a decimal string to a bit string. - * @param {Snowflake} num Snowflake to be transformed - * @returns {string} - * @private - */ - static idToBinary(num) { - let bin = ""; - let high = parseInt(num.slice(0, -10)) || 0; - let low = parseInt(num.slice(-10)); - while (low > 0 || high > 0) { - bin = String(low & 1) + bin; - low = Math.floor(low / 2); - if (high > 0) { - low += 5000000000 * (high % 2); - high = Math.floor(high / 2); - } - } - return bin; - } - - /** - * Transforms a snowflake from a bit string to a decimal string. - * @param {string} num Bit string to be transformed - * @returns {Snowflake} - * @private - */ - static binaryToID(num) { - let dec = ""; - - while (num.length > 50) { - const high = parseInt(num.slice(0, -32), 2); - const low = parseInt((high % 10).toString(2) + num.slice(-32), 2); - - dec = (low % 10).toString() + dec; - num = - Math.floor(high / 10).toString(2) + - Math.floor(low / 10) - .toString(2) - .padStart(32, "0"); - } - - num = parseInt(num, 2); - while (num > 0) { - dec = (num % 10).toString() + dec; - num = Math.floor(num / 10); - } - - return dec; - } - - static generate() { - var time = BigInt(Date.now() - Snowflake.EPOCH) << 22n; - var worker = Snowflake.workerId << 17n; - var process = Snowflake.processId << 12n; - var increment = Snowflake.INCREMENT++; - return (time | worker | process | increment).toString(); - } - - /** - * A deconstructed snowflake. - * @typedef {Object} DeconstructedSnowflake - * @property {number} timestamp Timestamp the snowflake was created - * @property {Date} date Date the snowflake was created - * @property {number} workerID Worker ID in the snowflake - * @property {number} processID Process ID in the snowflake - * @property {number} increment Increment in the snowflake - * @property {string} binary Binary representation of the snowflake - */ - - /** - * Deconstructs a Discord snowflake. - * @param {Snowflake} snowflake Snowflake to deconstruct - * @returns {DeconstructedSnowflake} Deconstructed snowflake - */ - static deconstruct(snowflake) { - const BINARY = Snowflake.idToBinary(snowflake).toString(2).padStart(64, "0"); - const res = { - timestamp: parseInt(BINARY.substring(0, 42), 2) + Snowflake.EPOCH, - workerID: parseInt(BINARY.substring(42, 47), 2), - processID: parseInt(BINARY.substring(47, 52), 2), - increment: parseInt(BINARY.substring(52, 64), 2), - binary: BINARY, - }; - Object.defineProperty(res, "date", { - get: function get() { - return new Date(this.timestamp); - }, - enumerable: true, - }); - return res; - } -} diff --git a/src/util/String.ts b/src/util/String.ts deleted file mode 100644 index 55f11e8d..00000000 --- a/src/util/String.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { SPECIAL_CHAR } from "./Regex"; - -export function trimSpecial(str?: string): string { - // @ts-ignore - if (!str) return; - return str.replace(SPECIAL_CHAR, "").trim(); -} diff --git a/src/util/UserFlags.ts b/src/util/UserFlags.ts deleted file mode 100644 index 72394eff..00000000 --- a/src/util/UserFlags.ts +++ /dev/null @@ -1,22 +0,0 @@ -// https://github.com/discordjs/discord.js/blob/master/src/util/UserFlags.js -// Apache License Version 2.0 Copyright 2015 - 2021 Amish Shah - -import { BitField } from "./BitField"; - -export class UserFlags extends BitField { - static FLAGS = { - DISCORD_EMPLOYEE: BigInt(1) << BigInt(0), - PARTNERED_SERVER_OWNER: BigInt(1) << BigInt(1), - HYPESQUAD_EVENTS: BigInt(1) << BigInt(2), - BUGHUNTER_LEVEL_1: BigInt(1) << BigInt(3), - HOUSE_BRAVERY: BigInt(1) << BigInt(6), - HOUSE_BRILLIANCE: BigInt(1) << BigInt(7), - HOUSE_BALANCE: BigInt(1) << BigInt(8), - EARLY_SUPPORTER: BigInt(1) << BigInt(9), - TEAM_USER: BigInt(1) << BigInt(10), - SYSTEM: BigInt(1) << BigInt(12), - BUGHUNTER_LEVEL_2: BigInt(1) << BigInt(14), - VERIFIED_BOT: BigInt(1) << BigInt(16), - EARLY_VERIFIED_BOT_DEVELOPER: BigInt(1) << BigInt(17), - }; -} diff --git a/src/util/checkToken.ts b/src/util/checkToken.ts deleted file mode 100644 index 91bf08d5..00000000 --- a/src/util/checkToken.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { JWTOptions } from "./Constants"; -import jwt from "jsonwebtoken"; -import { UserModel } from "../models"; - -export function checkToken(token: string, jwtSecret: string): Promise<any> { - return new Promise((res, rej) => { - token = token.replace("Bot ", ""); // TODO: proper bot support - jwt.verify(token, jwtSecret, JWTOptions, async (err, decoded: any) => { - if (err || !decoded) return rej("Invalid Token"); - - const user = await UserModel.findOne( - { id: decoded.id }, - { "user_data.valid_tokens_since": true, bot: true, disabled: true, deleted: true } - ).exec(); - if (!user) return rej("Invalid Token"); - // we need to round it to seconds as it saved as seconds in jwt iat and valid_tokens_since is stored in milliseconds - if (decoded.iat * 1000 < user.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"); - - return res({ decoded, user }); - }); - }); -} diff --git a/src/util/index.ts b/src/util/index.ts deleted file mode 100644 index 7523a6ad..00000000 --- a/src/util/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export * from "./String"; -export * from "./BitField"; -export * from "./Intents"; -export * from "./MessageFlags"; -export * from "./Permissions"; -export * from "./Snowflake"; -export * from "./UserFlags"; -export * from "./toBigInt"; -export * from "./RabbitMQ"; diff --git a/src/util/toBigInt.ts b/src/util/toBigInt.ts deleted file mode 100644 index d57c4568..00000000 --- a/src/util/toBigInt.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default function toBigInt(string: String): BigInt { - return BigInt(string); -} |