From 4f2596789875b894e5b45d7cfafcc7cf6ca46aef 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: :sparkles: util --- 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 + 39 files changed, 3735 insertions(+) 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 (limited to 'util/src') 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