From c72661a98782c1316038382a4989b3a05bdab731 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Thu, 12 Aug 2021 20:33:02 +0200 Subject: [PATCH] :sparkles: util --- util/.gitignore | 107 +++++++ util/.npmignore | 1 + util/.prettierrc | 5 + LICENSE => util/LICENSE | 0 util/README.md | 29 ++ util/package-lock.json | Bin 0 -> 47678 bytes util/package.json | 48 +++ util/src/index.ts | 10 + util/src/models/Activity.ts | 132 ++++++++ util/src/models/Application.ts | 67 ++++ util/src/models/AuditLog.ts | 220 ++++++++++++++ util/src/models/Ban.ts | 32 ++ util/src/models/Channel.ts | 109 +++++++ util/src/models/Emoji.ts | 29 ++ util/src/models/Event.ts | 540 +++++++++++++++++++++++++++++++++ util/src/models/Guild.ts | 161 ++++++++++ util/src/models/Interaction.ts | 32 ++ util/src/models/Invite.ts | 95 ++++++ util/src/models/Member.ts | 109 +++++++ util/src/models/Message.ts | 368 ++++++++++++++++++++++ util/src/models/RateLimit.ts | 25 ++ util/src/models/ReadState.ts | 26 ++ util/src/models/Role.ts | 42 +++ util/src/models/Status.ts | 13 + util/src/models/Team.ts | 17 ++ util/src/models/Template.ts | 51 ++++ util/src/models/User.ts | 252 +++++++++++++++ util/src/models/VoiceState.ts | 34 +++ util/src/models/Webhook.ts | 84 +++++ util/src/models/index.ts | 89 ++++++ util/src/util/BitField.ts | 143 +++++++++ util/src/util/Config.ts | 284 +++++++++++++++++ util/src/util/Constants.ts | 28 ++ util/src/util/Database.ts | 151 +++++++++ util/src/util/Intents.ts | 21 ++ util/src/util/MessageFlags.ts | 14 + util/src/util/MongoBigInt.ts | 82 +++++ util/src/util/Permissions.ts | 262 ++++++++++++++++ util/src/util/RabbitMQ.ts | 18 ++ util/src/util/Regex.ts | 3 + util/src/util/Snowflake.ts | 127 ++++++++ util/src/util/String.ts | 7 + util/src/util/UserFlags.ts | 22 ++ util/src/util/checkToken.ts | 24 ++ util/src/util/index.ts | 9 + util/src/util/toBigInt.ts | 3 + util/tsconfig.json | 70 +++++ 47 files changed, 3995 insertions(+) create mode 100644 util/.gitignore create mode 100644 util/.npmignore create mode 100644 util/.prettierrc rename LICENSE => util/LICENSE (100%) create mode 100644 util/README.md create mode 100644 util/package-lock.json create mode 100644 util/package.json create mode 100644 util/src/index.ts create mode 100644 util/src/models/Activity.ts create mode 100644 util/src/models/Application.ts create mode 100644 util/src/models/AuditLog.ts create mode 100644 util/src/models/Ban.ts create mode 100644 util/src/models/Channel.ts create mode 100644 util/src/models/Emoji.ts create mode 100644 util/src/models/Event.ts create mode 100644 util/src/models/Guild.ts create mode 100644 util/src/models/Interaction.ts create mode 100644 util/src/models/Invite.ts create mode 100644 util/src/models/Member.ts create mode 100644 util/src/models/Message.ts create mode 100644 util/src/models/RateLimit.ts create mode 100644 util/src/models/ReadState.ts create mode 100644 util/src/models/Role.ts create mode 100644 util/src/models/Status.ts create mode 100644 util/src/models/Team.ts create mode 100644 util/src/models/Template.ts create mode 100644 util/src/models/User.ts create mode 100644 util/src/models/VoiceState.ts create mode 100644 util/src/models/Webhook.ts create mode 100644 util/src/models/index.ts create mode 100644 util/src/util/BitField.ts create mode 100644 util/src/util/Config.ts create mode 100644 util/src/util/Constants.ts create mode 100644 util/src/util/Database.ts create mode 100644 util/src/util/Intents.ts create mode 100644 util/src/util/MessageFlags.ts create mode 100644 util/src/util/MongoBigInt.ts create mode 100644 util/src/util/Permissions.ts create mode 100644 util/src/util/RabbitMQ.ts create mode 100644 util/src/util/Regex.ts create mode 100644 util/src/util/Snowflake.ts create mode 100644 util/src/util/String.ts create mode 100644 util/src/util/UserFlags.ts create mode 100644 util/src/util/checkToken.ts create mode 100644 util/src/util/index.ts create mode 100644 util/src/util/toBigInt.ts create mode 100644 util/tsconfig.json diff --git a/util/.gitignore b/util/.gitignore new file mode 100644 index 00000000..d7fc3f74 --- /dev/null +++ b/util/.gitignore @@ -0,0 +1,107 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and *not* Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port +.DS_Store + +# Compiled TypeScript code +dist/ \ No newline at end of file diff --git a/util/.npmignore b/util/.npmignore new file mode 100644 index 00000000..05a9d0cf --- /dev/null +++ b/util/.npmignore @@ -0,0 +1 @@ +!dist/ \ No newline at end of file diff --git a/util/.prettierrc b/util/.prettierrc new file mode 100644 index 00000000..d569c548 --- /dev/null +++ b/util/.prettierrc @@ -0,0 +1,5 @@ +{ + "tabWidth": 4, + "useTabs": true, + "printWidth": 120 +} diff --git a/LICENSE b/util/LICENSE similarity index 100% rename from LICENSE rename to util/LICENSE diff --git a/util/README.md b/util/README.md new file mode 100644 index 00000000..9c983acd --- /dev/null +++ b/util/README.md @@ -0,0 +1,29 @@ +

+ +

+

Fosscord server util

+ +

+ + + + + + + + +

+ +## [About](https://fosscord.com) + +Fosscord is a free open source selfhostable chat, voice and video discord-compatible platform. + +Fosscord server util contains all necessary logic that is shared between the [api](https://github.com/fosscord/fosscord-api), [gateway](https://github.com/fosscord/fosscord-gateway) and [cdn](https://github.com/fosscord/fosscord-cdn). + +It contains all mongoose database models and utility functions. + +## Installation + +```bash +npm install @fosscord/server-util +``` diff --git a/util/package-lock.json b/util/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..4977468dc5b18a77ddc1d0cd28ac95acaf85febf GIT binary patch literal 47678 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 E0p@i;%m4rY literal 0 HcmV?d00001 diff --git a/util/package.json b/util/package.json new file mode 100644 index 00000000..0478fe69 --- /dev/null +++ b/util/package.json @@ -0,0 +1,48 @@ +{ + "name": "@fosscord/server-util", + "version": "1.3.52", + "description": "Utility functions for the all server repositories", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "tsc -b .", + "prepublish": "npm run build" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/fosscord/fosscord-server-util.git" + }, + "keywords": [ + "discord", + "fosscord", + "fosscord-server-util", + "discord open source", + "discord-open-source" + ], + "author": "Fosscord", + "license": "GPLV3", + "bugs": { + "url": "https://github.com/fosscord/fosscord-server-util/issues" + }, + "homepage": "https://docs.fosscord.com/", + "dependencies": { + "@types/jsonwebtoken": "^8.5.0", + "@types/mongoose-autopopulate": "^0.10.1", + "@types/mongoose-lean-virtuals": "^0.5.1", + "@types/node": "^14.14.25", + "ajv": "^8.5.0", + "amqplib": "^0.8.0", + "dot-prop": "^6.0.1", + "env-paths": "^2.2.1", + "jsonwebtoken": "^8.5.1", + "missing-native-js-functions": "^1.2.2", + "mongodb": "^3.6.9", + "mongoose": "^5.12.3", + "mongoose-autopopulate": "^0.12.3", + "typescript": "^4.1.3" + }, + "devDependencies": { + "@types/amqplib": "^0.8.1" + } +} diff --git a/util/src/index.ts b/util/src/index.ts new file mode 100644 index 00000000..3565fb6b --- /dev/null +++ b/util/src/index.ts @@ -0,0 +1,10 @@ +export * from "./util/checkToken"; + +export * as Constants from "./util/Constants"; +export * from "./models/index"; +export * from "./util/index"; + +import Config from "./util/Config"; +import db, { MongooseCache, toObject } from "./util/Database"; + +export { Config, db, MongooseCache, toObject }; diff --git a/util/src/models/Activity.ts b/util/src/models/Activity.ts new file mode 100644 index 00000000..17abd1ca --- /dev/null +++ b/util/src/models/Activity.ts @@ -0,0 +1,132 @@ +import { User } from ".."; +import { ClientStatus, Status } from "./Status"; +import { Schema, model, Types, Document } from "mongoose"; +import toBigInt from "../util/toBigInt"; + +export interface Presence { + user: User; + guild_id?: string; + status: Status; + activities: Activity[]; + client_status: ClientStatus; +} + +export interface Activity { + name: string; + type: ActivityType; + url?: string; + created_at?: Date; + timestamps?: { + start?: number; + end?: number; + }[]; + application_id?: string; + details?: string; + state?: string; + emoji?: { + name: string; + id?: string; + amimated?: boolean; + }; + party?: { + id?: string; + size?: [number, number]; + }; + assets?: { + large_image?: string; + large_text?: string; + small_image?: string; + small_text?: string; + }; + secrets?: { + join?: string; + spectate?: string; + match?: string; + }; + instance?: boolean; + flags?: bigint; +} + +export const ActivitySchema = { + name: { type: String, required: true }, + type: { type: Number, required: true }, + url: String, + created_at: Date, + timestamps: [ + { + start: Number, + end: Number, + }, + ], + application_id: String, + details: String, + state: String, + emoji: { + name: String, + id: String, + amimated: Boolean, + }, + party: { + id: String, + size: [Number, Number], + }, + assets: { + large_image: String, + large_text: String, + small_image: String, + small_text: String, + }, + secrets: { + join: String, + spectate: String, + match: String, + }, + instance: Boolean, + flags: { type: String, get: toBigInt }, +}; + +export const ActivityBodySchema = { + name: String, + type: Number, + $url: String, + $created_at: Date, + $timestamps: [ + { + $start: Number, + $end: Number, + }, + ], + $application_id: String, + $details: String, + $state: String, + $emoji: { + $name: String, + $id: String, + $amimated: Boolean, + }, + $party: { + $id: String, + $size: [Number, Number], + }, + $assets: { + $large_image: String, + $large_text: String, + $small_image: String, + $small_text: String, + }, + $secrets: { + $join: String, + $spectate: String, + $match: String, + }, + $instance: Boolean, + $flags: BigInt, +}; + +export enum ActivityType { + GAME = 0, + STREAMING = 1, + LISTENING = 2, + CUSTOM = 4, + COMPETING = 5, +} diff --git a/util/src/models/Application.ts b/util/src/models/Application.ts new file mode 100644 index 00000000..fae6e8db --- /dev/null +++ b/util/src/models/Application.ts @@ -0,0 +1,67 @@ +import { Team } from "./Team"; + +export interface Application { + id: string; + name: string; + icon: string | null; + description: string; + rpc_origins: string[] | null; + bot_public: boolean; + bot_require_code_grant: boolean; + terms_of_service_url: string | null; + privacy_policy_url: string | null; + owner_id: string; + summary: string | null; + verify_key: string; + team: Team | null; + guild_id: string; // if this application is a game sold on Discord, this field will be the guild to which it has been linked + primary_sku_id: string | null; // if this application is a game sold on Discord, this field will be the id of the "Game SKU" that is created, if exists + slug: string | null; // if this application is a game sold on Discord, this field will be the URL slug that links to the store page + cover_image: string | null; // the application's default rich presence invite cover image hash + flags: number; // the application's public flags +} + +export interface ApplicationCommand { + id: string; + application_id: string; + name: string; + description: string; + options?: ApplicationCommandOption[]; +} + +export interface ApplicationCommandOption { + type: ApplicationCommandOptionType; + name: string; + description: string; + required?: boolean; + choices?: ApplicationCommandOptionChoice[]; + options?: ApplicationCommandOption[]; +} + +export interface ApplicationCommandOptionChoice { + name: string; + value: string | number; +} + +export enum ApplicationCommandOptionType { + SUB_COMMAND = 1, + SUB_COMMAND_GROUP = 2, + STRING = 3, + INTEGER = 4, + BOOLEAN = 5, + USER = 6, + CHANNEL = 7, + ROLE = 8, +} + +export interface ApplicationCommandInteractionData { + id: string; + name: string; + options?: ApplicationCommandInteractionDataOption[]; +} + +export interface ApplicationCommandInteractionDataOption { + name: string; + value?: any; + options?: ApplicationCommandInteractionDataOption[]; +} diff --git a/util/src/models/AuditLog.ts b/util/src/models/AuditLog.ts new file mode 100644 index 00000000..02b2c444 --- /dev/null +++ b/util/src/models/AuditLog.ts @@ -0,0 +1,220 @@ +import { Schema, Document, Types } from "mongoose"; +import db from "../util/Database"; +import { ChannelPermissionOverwrite } from "./Channel"; +import { PublicUser } from "./User"; + +export interface AuditLogResponse { + webhooks: []; // TODO: + users: PublicUser[]; + audit_log_entries: AuditLogEntries[]; + integrations: []; // TODO: +} + +export interface AuditLogEntries { + target_id?: string; + user_id: string; + id: string; + action_type: AuditLogEvents; + options?: { + delete_member_days?: string; + members_removed?: string; + channel_id?: string; + messaged_id?: string; + count?: string; + id?: string; + type?: string; + role_name?: string; + }; + changes: AuditLogChange[]; + reason?: string; +} + +export interface AuditLogChange { + new_value?: AuditLogChangeValue; + old_value?: AuditLogChangeValue; + key: string; +} + +export interface AuditLogChangeValue { + name?: string; + description?: string; + icon_hash?: string; + splash_hash?: string; + discovery_splash_hash?: string; + banner_hash?: string; + owner_id?: string; + region?: string; + preferred_locale?: string; + afk_channel_id?: string; + afk_timeout?: number; + rules_channel_id?: string; + public_updates_channel_id?: string; + mfa_level?: number; + verification_level?: number; + explicit_content_filter?: number; + default_message_notifications?: number; + vanity_url_code?: string; + $add?: {}[]; + $remove?: {}[]; + prune_delete_days?: number; + widget_enabled?: boolean; + widget_channel_id?: string; + system_channel_id?: string; + position?: number; + topic?: string; + bitrate?: number; + permission_overwrites?: ChannelPermissionOverwrite[]; + nsfw?: boolean; + application_id?: string; + rate_limit_per_user?: number; + permissions?: string; + color?: number; + hoist?: boolean; + mentionable?: boolean; + allow?: string; + deny?: string; + code?: string; + channel_id?: string; + inviter_id?: string; + max_uses?: number; + uses?: number; + max_age?: number; + temporary?: boolean; + deaf?: boolean; + mute?: boolean; + nick?: string; + avatar_hash?: string; + id?: string; + type?: number; + enable_emoticons?: boolean; + expire_behavior?: number; + expire_grace_period?: number; + user_limit?: number; +} + +export interface AuditLogEntriesDocument extends Document, AuditLogEntries { + id: string; +} + +export const AuditLogChanges = { + name: String, + description: String, + icon_hash: String, + splash_hash: String, + discovery_splash_hash: String, + banner_hash: String, + owner_id: String, + region: String, + preferred_locale: String, + afk_channel_id: String, + afk_timeout: Number, + rules_channel_id: String, + public_updates_channel_id: String, + mfa_level: Number, + verification_level: Number, + explicit_content_filter: Number, + default_message_notifications: Number, + vanity_url_code: String, + $add: [{}], + $remove: [{}], + prune_delete_days: Number, + widget_enabled: Boolean, + widget_channel_id: String, + system_channel_id: String, + position: Number, + topic: String, + bitrate: Number, + permission_overwrites: [{}], + nsfw: Boolean, + application_id: String, + rate_limit_per_user: Number, + permissions: String, + color: Number, + hoist: Boolean, + mentionable: Boolean, + allow: String, + deny: String, + code: String, + channel_id: String, + inviter_id: String, + max_uses: Number, + uses: Number, + max_age: Number, + temporary: Boolean, + deaf: Boolean, + mute: Boolean, + nick: String, + avatar_hash: String, + id: String, + type: Number, + enable_emoticons: Boolean, + expire_behavior: Number, + expire_grace_period: Number, + user_limit: Number, +}; + +export const AuditLogSchema = new Schema({ + target_id: String, + user_id: { type: String, required: true }, + id: { type: String, required: true }, + action_type: { type: Number, required: true }, + options: { + delete_member_days: String, + members_removed: String, + channel_id: String, + messaged_id: String, + count: String, + id: String, + type: { type: Number }, + role_name: String, + }, + changes: [ + { + new_value: AuditLogChanges, + old_value: AuditLogChanges, + key: String, + }, + ], + reason: String, +}); + +// @ts-ignore +export const AuditLogModel = db.model("AuditLog", AuditLogSchema, "auditlogs"); + +export enum AuditLogEvents { + GUILD_UPDATE = 1, + CHANNEL_CREATE = 10, + CHANNEL_UPDATE = 11, + CHANNEL_DELETE = 12, + CHANNEL_OVERWRITE_CREATE = 13, + CHANNEL_OVERWRITE_UPDATE = 14, + CHANNEL_OVERWRITE_DELETE = 15, + MEMBER_KICK = 20, + MEMBER_PRUNE = 21, + MEMBER_BAN_ADD = 22, + MEMBER_BAN_REMOVE = 23, + MEMBER_UPDATE = 24, + MEMBER_ROLE_UPDATE = 25, + MEMBER_MOVE = 26, + MEMBER_DISCONNECT = 27, + BOT_ADD = 28, + ROLE_CREATE = 30, + ROLE_UPDATE = 31, + ROLE_DELETE = 32, + INVITE_CREATE = 40, + INVITE_UPDATE = 41, + INVITE_DELETE = 42, + WEBHOOK_CREATE = 50, + WEBHOOK_UPDATE = 51, + WEBHOOK_DELETE = 52, + EMOJI_CREATE = 60, + EMOJI_UPDATE = 61, + EMOJI_DELETE = 62, + MESSAGE_DELETE = 72, + MESSAGE_BULK_DELETE = 73, + MESSAGE_PIN = 74, + MESSAGE_UNPIN = 75, + INTEGRATION_CREATE = 80, + INTEGRATION_UPDATE = 81, + INTEGRATION_DELETE = 82, +} diff --git a/util/src/models/Ban.ts b/util/src/models/Ban.ts new file mode 100644 index 00000000..f09950ee --- /dev/null +++ b/util/src/models/Ban.ts @@ -0,0 +1,32 @@ +import { Schema, model, Types, Document } from "mongoose"; +import db from "../util/Database"; +import { PublicUserProjection, UserModel } from "./User"; + +export interface Ban extends Document { + user_id: string; + guild_id: string; + executor_id: string; + ip: string; + reason?: string; +} + +export const BanSchema = new Schema({ + user_id: { type: String, required: true }, + guild_id: { type: String, required: true }, + executor_id: { type: String, required: true }, + reason: String, + ip: String, // ? Should we store this in here, or in the UserModel? +}); + +BanSchema.virtual("user", { + ref: UserModel, + localField: "user_id", + foreignField: "id", + justOne: true, + autopopulate: { select: PublicUserProjection }, +}); + +BanSchema.set("removeResponse", ["user_id"]); + +// @ts-ignore +export const BanModel = db.model("Ban", BanSchema, "bans"); diff --git a/util/src/models/Channel.ts b/util/src/models/Channel.ts new file mode 100644 index 00000000..1dd05896 --- /dev/null +++ b/util/src/models/Channel.ts @@ -0,0 +1,109 @@ +import { Schema, model, Types, Document } from "mongoose"; +import db from "../util/Database"; +import toBigInt from "../util/toBigInt"; +import { PublicUserProjection, UserModel } from "./User"; + +// @ts-ignore +export interface AnyChannel extends Channel, DMChannel, TextChannel, VoiceChannel { + recipient_ids: null | string[]; +} + +export interface ChannelDocument extends Document, AnyChannel { + id: string; +} + +export const ChannelSchema = new Schema({ + id: String, + created_at: { type: Schema.Types.Date, required: true }, + name: String, // can't be required for dm channels + type: { type: Number, required: true }, + guild_id: String, + owner_id: String, + parent_id: String, + recipient_ids: [String], + position: Number, + last_message_id: String, + last_pin_timestamp: Date, + nsfw: Boolean, + rate_limit_per_user: Number, + topic: String, + permission_overwrites: [ + { + allow: { type: String, get: toBigInt }, + deny: { type: String, get: toBigInt }, + id: String, + type: { type: Number }, + }, + ], +}); + +ChannelSchema.virtual("recipients", { + ref: UserModel, + localField: "recipient_ids", + foreignField: "id", + justOne: false, + autopopulate: { select: PublicUserProjection }, +}); + +ChannelSchema.set("removeResponse", ["recipient_ids"]); + +// @ts-ignore +export const ChannelModel = db.model("Channel", ChannelSchema, "channels"); + +export interface Channel { + id: string; + created_at: Date; + name: string; + type: number; +} + +export interface TextBasedChannel { + last_message_id?: string; + last_pin_timestamp?: number; +} + +export interface GuildChannel extends Channel { + guild_id: string; + position: number; + parent_id?: string; + permission_overwrites: ChannelPermissionOverwrite[]; +} + +export interface ChannelPermissionOverwrite { + allow: bigint; // for bitfields we use bigints + deny: bigint; // for bitfields we use bigints + id: string; + type: ChannelPermissionOverwriteType; +} + +export enum ChannelPermissionOverwriteType { + role = 0, + member = 1, +} + +export interface VoiceChannel extends GuildChannel { + video_quality_mode?: number; + bitrate?: number; + user_limit?: number; +} + +export interface TextChannel extends GuildChannel, TextBasedChannel { + nsfw: boolean; + rate_limit_per_user: number; + topic?: string; +} +// @ts-ignore +export interface DMChannel extends Channel, TextBasedChannel { + owner_id: string; + recipient_ids: string[]; +} + +export enum ChannelType { + GUILD_TEXT = 0, // a text channel within a server + DM = 1, // a direct message between users + GUILD_VOICE = 2, // a voice channel within a server + GROUP_DM = 3, // a direct message between multiple users + GUILD_CATEGORY = 4, // an organizational category that contains up to 50 channels + GUILD_NEWS = 5, // a channel that users can follow and crosspost into their own server + GUILD_STORE = 6, // a channel in which game developers can sell their game on Discord +} diff --git a/util/src/models/Emoji.ts b/util/src/models/Emoji.ts new file mode 100644 index 00000000..3e5cad53 --- /dev/null +++ b/util/src/models/Emoji.ts @@ -0,0 +1,29 @@ +import { Schema, model, Types, Document } from "mongoose"; +import db from "../util/Database"; + +export interface Emoji extends Document { + id: string; + animated: boolean; + available: boolean; + guild_id: string; + managed: boolean; + name: string; + require_colons: boolean; + url: string; + roles: string[]; // roles this emoji is whitelisted to (new discord feature?) +} + +export const EmojiSchema = new Schema({ + id: { type: String, required: true }, + animated: Boolean, + available: Boolean, + guild_id: String, + managed: Boolean, + name: String, + require_colons: Boolean, + url: String, + roles: [String], +}); + +// @ts-ignore +export const EmojiModel = db.model("Emoji", EmojiSchema, "emojis"); diff --git a/util/src/models/Event.ts b/util/src/models/Event.ts new file mode 100644 index 00000000..1564107d --- /dev/null +++ b/util/src/models/Event.ts @@ -0,0 +1,540 @@ +import { ConnectedAccount, PublicUser, Relationship, User, UserSettings } from "./User"; +import { DMChannel, Channel } from "./Channel"; +import { Guild } from "./Guild"; +import { Member, PublicMember, UserGuildSettings } from "./Member"; +import { Emoji } from "./Emoji"; +import { Presence } from "./Activity"; +import { Role } from "./Role"; +import { Invite } from "./Invite"; +import { Message, PartialEmoji } from "./Message"; +import { VoiceState } from "./VoiceState"; +import { ApplicationCommand } from "./Application"; +import { Interaction } from "./Interaction"; +import { Schema, model, Types, Document } from "mongoose"; +import db from "../util/Database"; + +export interface Event { + guild_id?: string; + user_id?: string; + channel_id?: string; + created_at?: Date; + event: EVENT; + data?: any; +} + +export interface EventDocument extends Event, Document {} + +export const EventSchema = new Schema({ + guild_id: String, + user_id: String, + channel_id: String, + created_at: { type: Date, required: true }, + event: { type: String, required: true }, + data: Object, +}); + +// @ts-ignore +export const EventModel = db.model("Event", EventSchema, "events"); + +// ! Custom Events that shouldn't get sent to the client but processed by the server + +export interface InvalidatedEvent extends Event { + event: "INVALIDATED"; +} + +// ! END Custom Events that shouldn't get sent to the client but processed by the server + +export interface ReadyEventData { + v: number; + user: PublicUser & { + mobile: boolean; + desktop: boolean; + email: string | null; + flags: bigint; + mfa_enabled: boolean; + nsfw_allowed: boolean; + phone: string | null; + premium: boolean; + premium_type: number; + verified: boolean; + bot: boolean; + }; + private_channels: DMChannel[]; // this will be empty for bots + session_id: string; // resuming + guilds: Guild[]; + analytics_token?: string; + connected_accounts?: ConnectedAccount[]; + consents?: { + personalization?: { + consented?: boolean; + }; + }; + country_code?: string; // e.g. DE + friend_suggestion_count?: number; + geo_ordered_rtc_regions?: string[]; // ["europe","russie","india","us-east","us-central"] + experiments?: [number, number, number, number, number][]; + guild_experiments?: [ + // ? what are guild_experiments? + // this is the structure of it: + number, + null, + number, + [[number, { e: number; s: number }[]]], + [number, [[number, [number, number]]]], + { b: number; k: bigint[] }[] + ][]; + guild_join_requests?: []; // ? what is this? this is new + shard?: [number, number]; + user_settings?: UserSettings; + relationships?: Relationship[]; // TODO + read_state: { + entries: []; // TODO + partial: boolean; + version: number; + }; + user_guild_settings?: { + entries: UserGuildSettings[]; + version: number; + partial: boolean; + }; + application?: { + id: string; + flags: bigint; + }; + merged_members?: Omit[][]; + // probably all users who the user is in contact with + users?: { + avatar: string | null; + discriminator: string; + id: string; + username: string; + bot: boolean; + public_flags: bigint; + }[]; +} + +export interface ReadyEvent extends Event { + event: "READY"; + data: ReadyEventData; +} + +export interface ChannelCreateEvent extends Event { + event: "CHANNEL_CREATE"; + data: Channel; +} + +export interface ChannelUpdateEvent extends Event { + event: "CHANNEL_UPDATE"; + data: Channel; +} + +export interface ChannelDeleteEvent extends Event { + event: "CHANNEL_DELETE"; + data: Channel; +} + +export interface ChannelPinsUpdateEvent extends Event { + event: "CHANNEL_PINS_UPDATE"; + data: { + guild_id?: string; + channel_id: string; + last_pin_timestamp?: number; + }; +} + +export interface GuildCreateEvent extends Event { + event: "GUILD_CREATE"; + data: Guild; +} + +export interface GuildUpdateEvent extends Event { + event: "GUILD_UPDATE"; + data: Guild; +} + +export interface GuildDeleteEvent extends Event { + event: "GUILD_DELETE"; + data: { + id: string; + unavailable?: boolean; + }; +} + +export interface GuildBanAddEvent extends Event { + event: "GUILD_BAN_ADD"; + data: { + guild_id: string; + user: User; + }; +} + +export interface GuildBanRemoveEvent extends Event { + event: "GUILD_BAN_REMOVE"; + data: { + guild_id: string; + user: User; + }; +} + +export interface GuildEmojiUpdateEvent extends Event { + event: "GUILD_EMOJI_UPDATE"; + data: { + guild_id: string; + emojis: Emoji[]; + }; +} + +export interface GuildIntegrationUpdateEvent extends Event { + event: "GUILD_INTEGRATIONS_UPDATE"; + data: { + guild_id: string; + }; +} + +export interface GuildMemberAddEvent extends Event { + event: "GUILD_MEMBER_ADD"; + data: PublicMember & { + guild_id: string; + }; +} + +export interface GuildMemberRemoveEvent extends Event { + event: "GUILD_MEMBER_REMOVE"; + data: { + guild_id: string; + user: User; + }; +} + +export interface GuildMemberUpdateEvent extends Event { + event: "GUILD_MEMBER_UPDATE"; + data: { + guild_id: string; + roles: string[]; + user: User; + nick?: string; + joined_at?: Date; + premium_since?: number; + pending?: boolean; + }; +} + +export interface GuildMembersChunkEvent extends Event { + event: "GUILD_MEMBERS_CHUNK"; + data: { + guild_id: string; + members: PublicMember[]; + chunk_index: number; + chunk_count: number; + not_found: string[]; + presences: Presence[]; + nonce?: string; + }; +} + +export interface GuildRoleCreateEvent extends Event { + event: "GUILD_ROLE_CREATE"; + data: { + guild_id: string; + role: Role; + }; +} + +export interface GuildRoleUpdateEvent extends Event { + event: "GUILD_ROLE_UPDATE"; + data: { + guild_id: string; + role: Role; + }; +} + +export interface GuildRoleDeleteEvent extends Event { + event: "GUILD_ROLE_DELETE"; + data: { + guild_id: string; + role_id: string; + }; +} + +export interface InviteCreateEvent extends Event { + event: "INVITE_CREATE"; + data: Omit & { + channel_id: string; + guild_id?: string; + }; +} + +export interface InviteDeleteEvent extends Event { + event: "INVITE_DELETE"; + data: { + channel_id: string; + guild_id?: string; + code: string; + }; +} + +export type MessagePayload = Omit & { + channel_id: string; + guild_id?: string; + author: PublicUser; + member: PublicMember; + mentions: (PublicUser & { member: PublicMember })[]; +}; + +export interface MessageCreateEvent extends Event { + event: "MESSAGE_CREATE"; + data: MessagePayload; +} + +export interface MessageUpdateEvent extends Event { + event: "MESSAGE_UPDATE"; + data: MessagePayload; +} + +export interface MessageDeleteEvent extends Event { + event: "MESSAGE_DELETE"; + data: { + id: string; + channel_id: string; + guild_id?: string; + }; +} + +export interface MessageDeleteBulkEvent extends Event { + event: "MESSAGE_DELETE_BULK"; + data: { + ids: string[]; + channel_id: string; + guild_id?: string; + }; +} + +export interface MessageReactionAddEvent extends Event { + event: "MESSAGE_REACTION_ADD"; + data: { + user_id: string; + channel_id: string; + message_id: string; + guild_id?: string; + member?: PublicMember; + emoji: PartialEmoji; + }; +} + +export interface MessageReactionRemoveEvent extends Event { + event: "MESSAGE_REACTION_REMOVE"; + data: { + user_id: string; + channel_id: string; + message_id: string; + guild_id?: string; + emoji: PartialEmoji; + }; +} + +export interface MessageReactionRemoveAllEvent extends Event { + event: "MESSAGE_REACTION_REMOVE_ALL"; + data: { + channel_id: string; + message_id: string; + guild_id?: string; + }; +} + +export interface MessageReactionRemoveEmojiEvent extends Event { + event: "MESSAGE_REACTION_REMOVE_EMOJI"; + data: { + channel_id: string; + message_id: string; + guild_id?: string; + emoji: PartialEmoji; + }; +} + +export interface PresenceUpdateEvent extends Event { + event: "PRESENCE_UPDATE"; + data: Presence; +} + +export interface TypingStartEvent extends Event { + event: "TYPING_START"; + data: { + channel_id: string; + user_id: string; + timestamp: number; + guild_id?: string; + member?: PublicMember; + }; +} + +export interface UserUpdateEvent extends Event { + event: "USER_UPDATE"; + data: User; +} + +export interface VoiceStateUpdateEvent extends Event { + event: "VOICE_STATE_UPDATE"; + data: VoiceState & { + member: PublicMember; + }; +} + +export interface VoiceServerUpdateEvent extends Event { + event: "VOICE_SERVER_UPDATE"; + data: { + token: string; + guild_id: string; + endpoint: string; + }; +} + +export interface WebhooksUpdateEvent extends Event { + event: "WEBHOOKS_UPDATE"; + data: { + guild_id: string; + channel_id: string; + }; +} + +export type ApplicationCommandPayload = ApplicationCommand & { + guild_id: string; +}; + +export interface ApplicationCommandCreateEvent extends Event { + event: "APPLICATION_COMMAND_CREATE"; + data: ApplicationCommandPayload; +} + +export interface ApplicationCommandUpdateEvent extends Event { + event: "APPLICATION_COMMAND_UPDATE"; + data: ApplicationCommandPayload; +} + +export interface ApplicationCommandDeleteEvent extends Event { + event: "APPLICATION_COMMAND_DELETE"; + data: ApplicationCommandPayload; +} + +export interface InteractionCreateEvent extends Event { + event: "INTERACTION_CREATE"; + data: Interaction; +} + +export interface MessageAckEvent extends Event { + event: "MESSAGE_ACK"; + data: { + channel_id: string; + message_id: string; + version?: number; + manual?: boolean; + mention_count?: number; + }; +} + +export interface RelationshipAddEvent extends Event { + event: "RELATIONSHIP_ADD"; + data: Relationship & { + should_notify?: boolean; + user: PublicUser; + }; +} + +export interface RelationshipRemoveEvent extends Event { + event: "RELATIONSHIP_REMOVE"; + data: Omit; +} + +// located in collection events + +export enum EVENTEnum { + Ready = "READY", + ChannelCreate = "CHANNEL_CREATE", + ChannelUpdate = "CHANNEL_UPDATE", + ChannelDelete = "CHANNEL_DELETE", + ChannelPinsUpdate = "CHANNEL_PINS_UPDATE", + GuildCreate = "GUILD_CREATE", + GuildUpdate = "GUILD_UPDATE", + GuildDelete = "GUILD_DELETE", + GuildBanAdd = "GUILD_BAN_ADD", + GuildBanRemove = "GUILD_BAN_REMOVE", + GuildEmojUpdate = "GUILD_EMOJI_UPDATE", + GuildIntegrationsUpdate = "GUILD_INTEGRATIONS_UPDATE", + GuildMemberAdd = "GUILD_MEMBER_ADD", + GuildMemberRempve = "GUILD_MEMBER_REMOVE", + GuildMemberUpdate = "GUILD_MEMBER_UPDATE", + GuildMemberSpeaking = "GUILD_MEMBER_SPEAKING", + GuildMembersChunk = "GUILD_MEMBERS_CHUNK", + GuildRoleCreate = "GUILD_ROLE_CREATE", + GuildRoleDelete = "GUILD_ROLE_DELETE", + GuildRoleUpdate = "GUILD_ROLE_UPDATE", + InviteCreate = "INVITE_CREATE", + InviteDelete = "INVITE_DELETE", + MessageCreate = "MESSAGE_CREATE", + MessageUpdate = "MESSAGE_UPDATE", + MessageDelete = "MESSAGE_DELETE", + MessageDeleteBulk = "MESSAGE_DELETE_BULK", + MessageReactionAdd = "MESSAGE_REACTION_ADD", + MessageReactionRemove = "MESSAGE_REACTION_REMOVE", + MessageReactionRemoveAll = "MESSAGE_REACTION_REMOVE_ALL", + MessageReactionRemoveEmoji = "MESSAGE_REACTION_REMOVE_EMOJI", + PresenceUpdate = "PRESENCE_UPDATE", + TypingStart = "TYPING_START", + UserUpdate = "USER_UPDATE", + WebhooksUpdate = "WEBHOOKS_UPDATE", + InteractionCreate = "INTERACTION_CREATE", + VoiceStateUpdate = "VOICE_STATE_UPDATE", + VoiceServerUpdate = "VOICE_SERVER_UPDATE", + ApplicationCommandCreate = "APPLICATION_COMMAND_CREATE", + ApplicationCommandUpdate = "APPLICATION_COMMAND_UPDATE", + ApplicationCommandDelete = "APPLICATION_COMMAND_DELETE", +} + +export type EVENT = + | "READY" + | "CHANNEL_CREATE" + | "CHANNEL_UPDATE" + | "CHANNEL_DELETE" + | "CHANNEL_PINS_UPDATE" + | "GUILD_CREATE" + | "GUILD_UPDATE" + | "GUILD_DELETE" + | "GUILD_BAN_ADD" + | "GUILD_BAN_REMOVE" + | "GUILD_EMOJI_UPDATE" + | "GUILD_INTEGRATIONS_UPDATE" + | "GUILD_MEMBER_ADD" + | "GUILD_MEMBER_REMOVE" + | "GUILD_MEMBER_UPDATE" + | "GUILD_MEMBER_SPEAKING" + | "GUILD_MEMBERS_CHUNK" + | "GUILD_ROLE_CREATE" + | "GUILD_ROLE_DELETE" + | "GUILD_ROLE_UPDATE" + | "INVITE_CREATE" + | "INVITE_DELETE" + | "MESSAGE_CREATE" + | "MESSAGE_UPDATE" + | "MESSAGE_DELETE" + | "MESSAGE_DELETE_BULK" + | "MESSAGE_REACTION_ADD" + // TODO: add a new event: bulk add reaction: + // | "MESSAGE_REACTION_BULK_ADD" + | "MESSAGE_REACTION_REMOVE" + | "MESSAGE_REACTION_REMOVE_ALL" + | "MESSAGE_REACTION_REMOVE_EMOJI" + | "PRESENCE_UPDATE" + | "TYPING_START" + | "USER_UPDATE" + | "WEBHOOKS_UPDATE" + | "INTERACTION_CREATE" + | "VOICE_STATE_UPDATE" + | "VOICE_SERVER_UPDATE" + | "APPLICATION_COMMAND_CREATE" + | "APPLICATION_COMMAND_UPDATE" + | "APPLICATION_COMMAND_DELETE" + | "MESSAGE_ACK" + | "RELATIONSHIP_ADD" + | "RELATIONSHIP_REMOVE" + | CUSTOMEVENTS; + +export type CUSTOMEVENTS = "INVALIDATED"; diff --git a/util/src/models/Guild.ts b/util/src/models/Guild.ts new file mode 100644 index 00000000..13a7d078 --- /dev/null +++ b/util/src/models/Guild.ts @@ -0,0 +1,161 @@ +import { Schema, model, Types, Document } from "mongoose"; +import db from "../util/Database"; +import { ChannelModel } from "./Channel"; +import { EmojiModel } from "./Emoji"; +import { MemberModel } from "./Member"; +import { RoleModel } from "./Role"; + +export interface GuildDocument extends Document, Guild { + id: string; +} + +export interface Guild { + id: string; + afk_channel_id?: string; + afk_timeout?: number; + application_id?: string; + banner?: string; + default_message_notifications?: number; + description?: string; + discovery_splash?: string; + explicit_content_filter?: number; + features: string[]; + icon?: string; + large?: boolean; + max_members?: number; // e.g. default 100.000 + max_presences?: number; + max_video_channel_users?: number; // ? default: 25, is this max 25 streaming or watching + member_count?: number; + presence_count?: number; // users online + // members?: Member[]; // * Members are stored in a seperate collection + // roles: Role[]; // * Role are stored in a seperate collection + // channels: GuildChannel[]; // * Channels are stored in a seperate collection + // emojis: Emoji[]; // * Emojis are stored in a seperate collection + // voice_states: []; // * voice_states are stored in a seperate collection + //TODO: + presences?: object[]; + mfa_level?: number; + name: string; + owner_id: string; + preferred_locale?: string; // only community guilds can choose this + premium_subscription_count?: number; + premium_tier?: number; // nitro boost level + public_updates_channel_id?: string; + region?: string; + rules_channel_id?: string; + splash?: string; + system_channel_flags?: number; + system_channel_id?: string; + unavailable?: boolean; + vanity_url?: { + code: string; + uses: number; + }; + verification_level?: number; + welcome_screen: { + enabled: boolean; + description: string; + welcome_channels: { + description: string; + emoji_id?: string; + emoji_name: string; + channel_id: string }[]; + }; + widget_channel_id?: string; + widget_enabled?: boolean; +} + +export const GuildSchema = new Schema({ + id: { type: String, required: true }, + afk_channel_id: String, + afk_timeout: Number, + application_id: String, + banner: String, + default_message_notifications: Number, + description: String, + discovery_splash: String, + explicit_content_filter: Number, + features: { type: [String], default: [] }, + icon: String, + large: Boolean, + max_members: { type: Number, default: 100000 }, + max_presences: Number, + max_video_channel_users: { type: Number, default: 25 }, + member_count: Number, + presences: { type: [Object], default: [] }, + presence_count: Number, + mfa_level: Number, + name: { type: String, required: true }, + owner_id: { type: String, required: true }, + preferred_locale: String, + premium_subscription_count: Number, + premium_tier: Number, + public_updates_channel_id: String, + region: String, + rules_channel_id: String, + splash: String, + system_channel_flags: Number, + system_channel_id: String, + unavailable: Boolean, + vanity_url: { + code: String, + uses: Number + }, + verification_level: Number, + voice_states: { type: [Object], default: [] }, + welcome_screen: { + enabled: Boolean, + description: String, + welcome_channels: [{ + description: String, + emoji_id: String, + emoji_name: String, + channel_id: String }], + }, + widget_channel_id: String, + widget_enabled: Boolean, +}); + +GuildSchema.virtual("channels", { + ref: ChannelModel, + localField: "id", + foreignField: "guild_id", + justOne: false, + autopopulate: true, +}); + +GuildSchema.virtual("roles", { + ref: RoleModel, + localField: "id", + foreignField: "guild_id", + justOne: false, + autopopulate: true, +}); + +// nested populate is needed for member users: https://gist.github.com/yangsu/5312204 +GuildSchema.virtual("members", { + ref: MemberModel, + localField: "id", + foreignField: "guild_id", + justOne: false, +}); + +GuildSchema.virtual("emojis", { + ref: EmojiModel, + localField: "id", + foreignField: "guild_id", + justOne: false, + autopopulate: true, +}); + +GuildSchema.virtual("joined_at", { + ref: MemberModel, + localField: "id", + foreignField: "guild_id", + justOne: true, +}).get((member: any, virtual: any, doc: any) => { + return member?.joined_at; +}); + +// @ts-ignore +export const GuildModel = db.model("Guild", GuildSchema, "guilds"); diff --git a/util/src/models/Interaction.ts b/util/src/models/Interaction.ts new file mode 100644 index 00000000..764247a5 --- /dev/null +++ b/util/src/models/Interaction.ts @@ -0,0 +1,32 @@ +import { AllowedMentions, Embed } from "./Message"; + +export interface Interaction { + id: string; + type: InteractionType; + data?: {}; + guild_id: string; + channel_id: string; + member_id: string; + token: string; + version: number; +} + +export enum InteractionType { + Ping = 1, + ApplicationCommand = 2, +} + +export enum InteractionResponseType { + Pong = 1, + Acknowledge = 2, + ChannelMessage = 3, + ChannelMessageWithSource = 4, + AcknowledgeWithSource = 5, +} + +export interface InteractionApplicationCommandCallbackData { + tts?: boolean; + content: string; + embeds?: Embed[]; + allowed_mentions?: AllowedMentions; +} diff --git a/util/src/models/Invite.ts b/util/src/models/Invite.ts new file mode 100644 index 00000000..01f12003 --- /dev/null +++ b/util/src/models/Invite.ts @@ -0,0 +1,95 @@ +import { Schema, Document, Types } from "mongoose"; +import db from "../util/Database"; +import { ChannelModel } from "./Channel"; +import { PublicUserProjection, UserModel } from "./User"; +import { GuildModel } from "./Guild"; + +export interface Invite { + code: string; + temporary: boolean; + uses: number; + max_uses: number; + max_age: number; + created_at: Date; + expires_at: Date; + guild_id: string; + channel_id: string; + inviter_id: string; + + // ? What is this? + target_user_id?: string; + target_user_type?: number; +} + +export interface InviteDocument extends Invite, Document {} + +export const InviteSchema = new Schema({ + code: String, + temporary: Boolean, + uses: Number, + max_uses: Number, + max_age: Number, + created_at: Date, + expires_at: Date, + guild_id: String, + channel_id: String, + inviter_id: String, + + // ? What is this? + target_user_id: String, + target_user_type: Number, +}); + +InviteSchema.virtual("channel", { + ref: ChannelModel, + localField: "channel_id", + foreignField: "id", + justOne: true, + autopopulate: { + select: { + id: true, + name: true, + type: true, + }, + }, +}); + +InviteSchema.virtual("inviter", { + ref: UserModel, + localField: "inviter_id", + foreignField: "id", + justOne: true, + autopopulate: { + select: PublicUserProjection, + }, +}); + +InviteSchema.virtual("guild", { + ref: GuildModel, + localField: "guild_id", + foreignField: "id", + justOne: true, + autopopulate: { + select: { + id: true, + name: true, + splash: true, + banner: true, + description: true, + icon: true, + features: true, + verification_level: true, + vanity_url_code: true, + welcome_screen: true, + nsfw: true, + + // TODO: hide the following entries: + // channels: false, + // roles: false, + // emojis: false, + }, + }, +}); + +// @ts-ignore +export const InviteModel = db.model("Invite", InviteSchema, "invites"); diff --git a/util/src/models/Member.ts b/util/src/models/Member.ts new file mode 100644 index 00000000..d1c9ad9b --- /dev/null +++ b/util/src/models/Member.ts @@ -0,0 +1,109 @@ +import { PublicUser, PublicUserProjection, User, UserModel } from "./User"; +import { Schema, Types, Document } from "mongoose"; +import db from "../util/Database"; + +export const PublicMemberProjection = { + id: true, + guild_id: true, + nick: true, + roles: true, + joined_at: true, + pending: true, + deaf: true, + mute: true, + premium_since: true, +}; + +export interface Member { + id: string; + guild_id: string; + nick?: string; + roles: string[]; + joined_at: Date; + premium_since?: number; + deaf: boolean; + mute: boolean; + pending: boolean; + settings: UserGuildSettings; + read_state: Record; + // virtual + user?: User; +} + +export interface MemberDocument extends Member, Document { + id: string; +} + +export interface UserGuildSettings { + channel_overrides: { + channel_id: string; + message_notifications: number; + mute_config: MuteConfig; + muted: boolean; + }[]; + message_notifications: number; + mobile_push: boolean; + mute_config: MuteConfig; + muted: boolean; + suppress_everyone: boolean; + suppress_roles: boolean; + version: number; +} + +export interface MuteConfig { + end_time: number; + selected_time_window: number; +} + +const MuteConfig = { + end_time: Number, + selected_time_window: Number, +}; + +export const MemberSchema = new Schema({ + id: { type: String, required: true }, + guild_id: String, + nick: String, + roles: [String], + joined_at: Date, + premium_since: Number, + deaf: Boolean, + mute: Boolean, + pending: Boolean, + read_state: Object, + settings: { + channel_overrides: [ + { + channel_id: String, + message_notifications: Number, + mute_config: MuteConfig, + muted: Boolean, + }, + ], + message_notifications: Number, + mobile_push: Boolean, + mute_config: MuteConfig, + muted: Boolean, + suppress_everyone: Boolean, + suppress_roles: Boolean, + version: Number, + }, +}); + +MemberSchema.virtual("user", { + ref: UserModel, + localField: "id", + foreignField: "id", + justOne: true, + autopopulate: { + select: PublicUserProjection, + }, +}); + +// @ts-ignore +export const MemberModel = db.model("Member", MemberSchema, "members"); + +// @ts-ignore +export interface PublicMember extends Omit { + user: PublicUser; +} diff --git a/util/src/models/Message.ts b/util/src/models/Message.ts new file mode 100644 index 00000000..15a6f40d --- /dev/null +++ b/util/src/models/Message.ts @@ -0,0 +1,368 @@ +import { Schema, Types, Document } from "mongoose"; +import db from "../util/Database"; +import { PublicUser, PublicUserProjection, UserModel } from "./User"; +import { MemberModel, PublicMember } from "./Member"; +import { Role, RoleModel } from "./Role"; +import { Channel } from "./Channel"; +import { Snowflake } from "../util"; +import { InteractionType } from "./Interaction"; + +export interface Message { + id: string; + channel_id: string; + guild_id?: string; + author_id?: string; + webhook_id?: string; + application_id?: string; + content?: string; + timestamp: Date; + edited_timestamp: Date | null; + tts?: boolean; + mention_everyone?: boolean; + mention_user_ids: string[]; + mention_role_ids: string[]; + mention_channels_ids: string[]; + attachments: Attachment[]; + embeds: Embed[]; + reactions: Reaction[]; + nonce?: string | number; + pinned?: boolean; + type: MessageType; + activity?: { + type: number; + party_id: string; + }; + flags?: bigint; + stickers?: any[]; + message_reference?: { + message_id: string; + channel_id?: string; + guild_id?: string; + }; + interaction?: { + id: string; + type: InteractionType; + name: string; + user_id: string; // the user who invoked the interaction + // user: User; // TODO: autopopulate user + }; + components: MessageComponent[]; + + // * mongoose virtuals: + // TODO: + // application: Application; // TODO: auto pouplate application + author?: PublicUser; + member?: PublicMember; + mentions?: (PublicUser & { + member: PublicMember; + })[]; + mention_roles?: Role[]; + mention_channels?: Channel[]; + created_at?: Date; + // thread // TODO +} + +const PartialEmoji = { + id: String, + name: { type: String, required: true }, + animated: { type: Boolean, required: true }, +}; + +const MessageComponent: any = { + type: { type: Number, required: true }, + style: Number, + label: String, + emoji: PartialEmoji, + custom_id: String, + url: String, + disabled: Boolean, + components: [Object], +}; + +export interface MessageComponent { + type: number; + style?: number; + label?: string; + emoji?: PartialEmoji; + custom_id?: string; + url?: string; + disabled?: boolean; + components: MessageComponent[]; +} + +export enum MessageComponentType { + ActionRow = 1, + Button = 2, +} + +export interface MessageDocument extends Document, Message { + id: string; +} + +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, + REPLY = 19, + APPLICATION_COMMAND = 20, +} + +export interface Attachment { + id: string; // attachment id + filename: string; // name of file attached + size: number; // size of file in bytes + url: string; // source url of file + proxy_url: string; // a proxied url of file + height?: number; // height of file (if image) + width?: number; // width of file (if image) + content_type?: string; +} + +export interface Embed { + title?: string; //title of embed + type?: EmbedType; // type of embed (always "rich" for webhook embeds) + description?: string; // description of embed + url?: string; // url of embed + timestamp?: Date; // timestamp of embed content + color?: number; // color code of the embed + footer?: { + text: string; + icon_url?: string; + proxy_icon_url?: string; + }; // footer object footer information + image?: EmbedImage; // image object image information + thumbnail?: EmbedImage; // thumbnail object thumbnail information + video?: EmbedImage; // video object video information + provider?: { + name?: string; + url?: string; + }; // provider object provider information + author?: { + name?: string; + url?: string; + icon_url?: string; + proxy_icon_url?: string; + }; // author object author information + fields?: { + name: string; + value: string; + inline?: boolean; + }[]; +} + +export enum EmbedType { + rich = "rich", + image = "image", + video = "video", + gifv = "gifv", + article = "article", + link = "link", +} + +export interface EmbedImage { + url?: string; + proxy_url?: string; + height?: number; + width?: number; +} + +export interface Reaction { + count: number; + //// not saved in the database // me: boolean; // whether the current user reacted using this emoji + emoji: PartialEmoji; + user_ids: string[]; +} + +export interface PartialEmoji { + id?: string; + name: string; + animated?: boolean; +} + +export interface AllowedMentions { + parse?: ("users" | "roles" | "everyone")[]; + roles?: string[]; + users?: string[]; + replied_user?: boolean; +} + +export const Attachment = { + id: String, // attachment id + filename: String, // name of file attached + size: Number, // size of file in bytes + url: String, // source url of file + proxy_url: String, // a proxied url of file + height: Number, // height of file (if image) + width: Number, // width of file (if image) + content_type: String, +}; + +export const EmbedImage = { + url: String, + proxy_url: String, + height: Number, + width: Number, +}; + +const Reaction = { + count: Number, + user_ids: [String], + emoji: { + id: String, + name: String, + animated: Boolean, + }, +}; + +export const Embed = { + title: String, //title of embed + type: { type: String }, // type of embed (always "rich" for webhook embeds) + description: String, // description of embed + url: String, // url of embed + timestamp: Date, // timestamp of embed content + color: Number, // color code of the embed + footer: { + text: String, + icon_url: String, + proxy_icon_url: String, + }, // footer object footer information + image: EmbedImage, // image object image information + thumbnail: EmbedImage, // thumbnail object thumbnail information + video: EmbedImage, // video object video information + provider: { + name: String, + url: String, + }, // provider object provider information + author: { + name: String, + url: String, + icon_url: String, + proxy_icon_url: String, + }, // author object author information + fields: [ + { + name: String, + value: String, + inline: Boolean, + }, + ], +}; + +export const MessageSchema = new Schema({ + id: String, + channel_id: String, + author_id: String, + webhook_id: String, + guild_id: String, + application_id: String, + content: String, + timestamp: Date, + edited_timestamp: Date, + tts: Boolean, + mention_everyone: Boolean, + mention_user_ids: [String], + mention_role_ids: [String], + mention_channel_ids: [String], + attachments: [Attachment], + embeds: [Embed], + reactions: [Reaction], + nonce: Schema.Types.Mixed, // can be a long or a string + pinned: Boolean, + type: { type: Number }, + activity: { + type: { type: Number }, + party_id: String, + }, + flags: Types.Long, + stickers: [], + message_reference: { + message_id: String, + channel_id: String, + guild_id: String, + }, + components: [MessageComponent], + // virtual: + // author: { + // ref: UserModel, + // localField: "author_id", + // foreignField: "id", + // justOne: true, + // autopopulate: { select: { id: true, user_data: false } }, + // }, +}); + +MessageSchema.virtual("author", { + ref: UserModel, + localField: "author_id", + foreignField: "id", + justOne: true, + autopopulate: { select: PublicUserProjection }, +}); + +MessageSchema.virtual("member", { + ref: MemberModel, + localField: "author_id", + foreignField: "id", + justOne: true, +}); + +MessageSchema.virtual("mentions", { + ref: UserModel, + localField: "mention_user_ids", + foreignField: "id", + justOne: false, + autopopulate: { select: PublicUserProjection }, +}); + +MessageSchema.virtual("mention_roles", { + ref: RoleModel, + localField: "mention_role_ids", + foreignField: "id", + justOne: false, + autopopulate: true, +}); + +MessageSchema.virtual("mention_channels", { + ref: RoleModel, + localField: "mention_channel_ids", + foreignField: "id", + justOne: false, + autopopulate: { select: { id: true, guild_id: true, type: true, name: true } }, +}); + +MessageSchema.virtual("referenced_message", { + ref: "Message", + localField: "message_reference.message_id", + foreignField: "id", + justOne: true, + autopopulate: true, +}); + +MessageSchema.virtual("created_at").get(function (this: MessageDocument) { + return new Date(Snowflake.deconstruct(this.id).timestamp); +}); + +MessageSchema.set("removeResponse", ["mention_channel_ids", "mention_role_ids", "mention_user_ids", "author_id"]); + +// TODO: missing Application Model +// MessageSchema.virtual("application", { +// ref: Application, +// localField: "mention_role_ids", +// foreignField: "id", +// justOne: true, +// }); + +// @ts-ignore +export const MessageModel = db.model("Message", MessageSchema, "messages"); diff --git a/util/src/models/RateLimit.ts b/util/src/models/RateLimit.ts new file mode 100644 index 00000000..6a0e1ffd --- /dev/null +++ b/util/src/models/RateLimit.ts @@ -0,0 +1,25 @@ +import { Schema, Document, Types } from "mongoose"; +import db from "../util/Database"; + +export interface Bucket { + id: "global" | "error" | string; // channel_239842397 | guild_238927349823 | webhook_238923423498 + user_id: string; + hits: number; + blocked: boolean; + expires_at: Date; +} + +export interface BucketDocument extends Bucket, Document { + id: string; +} + +export const BucketSchema = new Schema({ + id: { type: String, required: true }, + user_id: { type: String, required: true }, // bot, user, oauth_application, webhook + hits: { type: Number, required: true }, // Number of times the user hit this bucket + blocked: { type: Boolean, required: true }, + expires_at: { type: Date, required: true }, +}); + +// @ts-ignore +export const BucketModel = db.model("Bucket", BucketSchema, "ratelimits"); diff --git a/util/src/models/ReadState.ts b/util/src/models/ReadState.ts new file mode 100644 index 00000000..9c4fb323 --- /dev/null +++ b/util/src/models/ReadState.ts @@ -0,0 +1,26 @@ +import { PublicMember } from "./Member"; +import { Schema, model, Types, Document } from "mongoose"; +import db from "../util/Database"; + +export interface ReadState extends Document { + message_id: string; + channel_id: string; + user_id: string; + last_message_id?: string; + last_pin_timestamp?: Date; + mention_count: number; + manual: boolean; +} + +export const ReadStateSchema = new Schema({ + message_id: String, + channel_id: String, + user_id: String, + last_message_id: String, + last_pin_timestamp: Date, + mention_count: Number, + manual: Boolean, +}); + +// @ts-ignore +export const ReadStateModel = db.model("ReadState", ReadStateSchema, "readstates"); diff --git a/util/src/models/Role.ts b/util/src/models/Role.ts new file mode 100644 index 00000000..c1111c84 --- /dev/null +++ b/util/src/models/Role.ts @@ -0,0 +1,42 @@ +import { Schema, model, Types, Document } from "mongoose"; +import db from "../util/Database"; +import toBigInt from "../util/toBigInt"; + +export interface Role { + id: string; + guild_id: string; + color: number; + hoist: boolean; + managed: boolean; + mentionable: boolean; + name: string; + permissions: bigint; + position: number; + tags?: { + bot_id?: string; + }; +} + +export interface RoleDocument extends Document, Role { + id: string; +} + +export const RoleSchema = new Schema({ + id: String, + guild_id: String, + color: Number, + hoist: Boolean, + managed: Boolean, + mentionable: Boolean, + name: String, + permissions: { type: String, get: toBigInt }, + position: Number, + tags: { + bot_id: String, + }, +}); + +RoleSchema.set("removeResponse", ["guild_id"]); + +// @ts-ignore +export const RoleModel = db.model("Role", RoleSchema, "roles"); diff --git a/util/src/models/Status.ts b/util/src/models/Status.ts new file mode 100644 index 00000000..5a9bf2ca --- /dev/null +++ b/util/src/models/Status.ts @@ -0,0 +1,13 @@ +export type Status = "idle" | "dnd" | "online" | "offline"; + +export interface ClientStatus { + desktop?: string; // e.g. Windows/Linux/Mac + mobile?: string; // e.g. iOS/Android + web?: string; // e.g. browser, bot account +} + +export const ClientStatus = { + desktop: String, + mobile: String, + web: String, +}; diff --git a/util/src/models/Team.ts b/util/src/models/Team.ts new file mode 100644 index 00000000..795c82d2 --- /dev/null +++ b/util/src/models/Team.ts @@ -0,0 +1,17 @@ +export interface Team { + icon: string | null; + id: string; + members: { + membership_state: number; + permissions: string[]; + team_id: string; + user_id: string; + }[]; + name: string; + owner_user_id: string; +} + +export enum TeamMemberState { + INVITED = 1, + ACCEPTED = 2, +} diff --git a/util/src/models/Template.ts b/util/src/models/Template.ts new file mode 100644 index 00000000..ad0f9104 --- /dev/null +++ b/util/src/models/Template.ts @@ -0,0 +1,51 @@ +import { Schema, model, Types, Document } from "mongoose"; +import db from "../util/Database"; +import { PublicUser, User, UserModel, PublicUserProjection } from "./User"; +import { Guild, GuildModel } from "./Guild"; + +export interface Template extends Document { + id: string; + code: string; + name: string; + description?: string; + usage_count?: number; + creator_id: string; + creator: User; + created_at: Date; + updated_at: Date; + source_guild_id: String; + serialized_source_guild: Guild; +} + +export const TemplateSchema = new Schema({ + id: String, + code: String, + name: String, + description: String, + usage_count: Number, + creator_id: String, + created_at: Date, + updated_at: Date, + source_guild_id: String, +}); + +TemplateSchema.virtual("creator", { + ref: UserModel, + localField: "creator_id", + foreignField: "id", + justOne: true, + autopopulate: { + select: PublicUserProjection, + }, +}); + +TemplateSchema.virtual("serialized_source_guild", { + ref: GuildModel, + localField: "source_guild_id", + foreignField: "id", + justOne: true, + autopopulate: true, +}); + +// @ts-ignore +export const TemplateModel = db.model