1
0
mirror of https://github.com/spacebarchat/server.git synced 2024-09-19 17:21:35 +02:00

Merge branch 'master' into pr/darkhpp/261-2

This commit is contained in:
Flam3rboy 2021-08-13 13:03:18 +02:00
commit ebff2f4241
138 changed files with 3314 additions and 5731 deletions

1
.docker/env Normal file
View File

@ -0,0 +1 @@
MONGO_URL=mongodb://db:27017/fosscord?readPreference=secondaryPreferred

View File

@ -15,13 +15,13 @@ jobs:
include:
- os: windows
build: npm run bundle:windows
artifact: fosscord-api-windows.exe
artifact: fosscord-server-windows.exe
- os: macos
build: npm run bundle:macos
artifact: fosscord-api-macos.app.tgz
artifact: fosscord-server-macos.app.tgz
- os: ubuntu
build: npm run bundle:linux
artifact: fosscord-api-linux.tgz
artifact: fosscord-server-linux.tgz
runs-on: ${{ matrix.os }}-latest
steps:
- uses: actions/checkout@v2
@ -29,6 +29,8 @@ jobs:
with:
node-version: 14
- run: npm install
env:
MONGOMS_VERSION: 4.4.3
- run: npm run build
- run: ${{ matrix.build }}
- uses: actions/upload-artifact@v2
@ -46,43 +48,52 @@ jobs:
uses: Saionaro/extract-package-version@v1.0.6
- uses: actions/download-artifact@v2
with:
name: fosscord-api-windows-${{ github.sha }}.exe
name: fosscord-server-windows.exe
- uses: actions/download-artifact@v2
with:
name: fosscord-api-macos-${{ github.sha }}.app.tgz
name: fosscord-server-macos.app.tgz
- uses: actions/download-artifact@v2
with:
name: fosscord-api-linux-${{ github.sha }}.tgz
name: fosscord-server-linux.tgz
- uses: actions/create-release@v1
id: create-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ steps.extract_version.outputs.version }}
release_name: Release ${{ steps.extract_version.outputs.version }}
release_name: Server v${{ steps.extract_version.outputs.version }}
draft: false
prerelease: true # TODO: change this to false
body: >
## Download
- [Windows](https://github.com/fosscord/fosscord-server/releases/download/v${{ steps.extract_version.outputs.version }}/fosscord-server-windows.exe)
- [MacOS](https://github.com/fosscord/fosscord-server/releases/download/v${{ steps.extract_version.outputs.version }}/fosscord-server-macos.app.tgz)
- [Linux](https://github.com/fosscord/fosscord-server/releases/download/v${{ steps.extract_version.outputs.version }}/fosscord-server-linux.tgz)
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create-release.outputs.upload_url }}
asset_path: fosscord-api-windows-${{ github.sha }}.exe
asset_name: fosscord-api-windows.exe
asset_path: fosscord-server-windows.exe
asset_name: fosscord-server-windows.exe
asset_content_type: application/vnd.microsoft.portable-executable
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create-release.outputs.upload_url }}
asset_path: fosscord-api-macos-${{ github.sha }}.app.tgz
asset_name: fosscord-api-macos.app.tgz
asset_path: fosscord-server-macos.app.tgz
asset_name: fosscord-server-macos.app.tgz
asset_content_type: application/gzip
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create-release.outputs.upload_url }}
asset_path: fosscord-api-linux-${{ github.sha }}.tgz
asset_name: fosscord-api-linux.tgz
asset_content_type: application/gzip
asset_path: fosscord-server-linux.tgz
asset_name: fosscord-server-linux.tgz
asset_content_type: application/gzip

5
.gitignore vendored
View File

@ -1 +1,4 @@
node_modules/
.DS_STORE
db/
dist/
node_modules

17
.vscode/launch.json vendored
View File

@ -1,17 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"sourceMaps": true,
"type": "node",
"request": "launch",
"name": "Launch Server",
"program": "${workspaceFolder}/dist/index.js",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": ["${workspaceFolder}/dist/**/*.js"]
}
]
}

View File

@ -1,20 +0,0 @@
---
name: "[Feature] "
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -1,71 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '25 10 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@ -1,47 +0,0 @@
name: docker-publish
on:
push:
branches:
- 'master'
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Cache Docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: ${{ secrets.DOCKERHUB_TAGS }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
-
# Hackfix to cleanup cache; replace after buildx 0.6 and BuildKit 0.9 are released
# https://github.com/docker/build-push-action/pull/406#issuecomment-879184394
name: Move cache fix
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache

27
api/package-lock.json generated
View File

@ -6,9 +6,10 @@
"packages": {
"": {
"version": "1.0.0",
"hasInstallScript": true,
"license": "ISC",
"dependencies": {
"@fosscord/server-util": "file:../util",
"@fosscord/util": "file:../util",
"@types/jest": "^26.0.22",
"@types/json-schema": "^7.0.7",
"ajv": "^8.4.0",
@ -705,6 +706,10 @@
"resolved": "../util",
"link": true
},
"node_modules/@fosscord/util": {
"resolved": "../util",
"link": true
},
"node_modules/@istanbuljs/load-nyc-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@ -12031,6 +12036,26 @@
"typescript": "^4.1.3"
}
},
"@fosscord/util": {
"version": "file:../util",
"requires": {
"@types/amqplib": "^0.8.1",
"@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.13.7",
"mongoose-autopopulate": "^0.12.3",
"typescript": "^4.1.3"
}
},
"@istanbuljs/load-nyc-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",

View File

@ -5,6 +5,8 @@
"main": "dist/Server.js",
"types": "dist/Server.d.ts",
"scripts": {
"link": "npm run build && npm link",
"postinstall": "npm run --prefix ../util/ link && npm link @fosscord/util && npm run link",
"test": "jest",
"test:watch": "jest --watch",
"start": "npm run build && node dist/start",
@ -33,7 +35,7 @@
},
"homepage": "https://github.com/fosscord/fosscord-api#readme",
"dependencies": {
"@fosscord/server-util": "file:../util",
"@fosscord/util": "file:../util",
"@types/jest": "^26.0.22",
"@types/json-schema": "^7.0.7",
"ajv": "^8.4.0",

View File

@ -3,7 +3,7 @@ import fs from "fs";
import { Connection } from "mongoose";
import { Server, ServerOptions } from "lambert-server";
import { Authentication, CORS } from "./middlewares/";
import { Config, db, RabbitMQ } from "@fosscord/server-util";
import { Config, db, initEvent, RabbitMQ } from "@fosscord/util";
import i18next from "i18next";
import i18nextMiddleware, { I18next } from "i18next-http-middleware";
import i18nextBackend from "i18next-node-fs-backend";
@ -56,9 +56,8 @@ export class FosscordServer extends Server {
// @ts-ignore
await (db as Promise<Connection>);
await this.setupSchema();
console.log("[Database] connected");
await Config.init();
await RabbitMQ.init();
await initEvent();
this.app.use(CORS);
this.app.use(Authentication);
@ -93,11 +92,12 @@ export class FosscordServer extends Server {
app.use("/api/v9", api);
app.use("/api", api); // allow unversioned requests
api.get("*", (req: Request, res: Response) => {
api.get("*", (req: Request, res: Response, next) => {
res.status(404).json({
message: "404: Not Found",
code: 0
});
next();
});
this.app = app;

View File

@ -7,9 +7,7 @@ export * from "./schema/Invite";
export * from "./schema/Message";
export * from "./util/Config";
export * from "./util/Constants";
export * from "./util/Event";
export * from "./util/instanceOf";
export * from "./util/Event";
export * from "./util/instanceOf";
export * from "./util/Member";
export * from "./util/RandomInviteID";

View File

@ -1,15 +1,15 @@
import { NextFunction, Request, Response } from "express";
import { HTTPError } from "lambert-server";
import { checkToken, Config } from "@fosscord/server-util";
import { checkToken, Config } from "@fosscord/util";
export const NO_AUTHORIZATION_ROUTES = [
/^\/api(\/v\d+)?\/auth\/login/,
/^\/api(\/v\d+)?\/auth\/register/,
/^\/api(\/v\d+)?\/webhooks\//,
/^\/api(\/v\d+)?\/ping/,
/^\/api(\/v\d+)?\/gateway/,
/^\/api(\/v\d+)?\/experiments/,
/^\/api(\/v\d+)?\/guilds\/\d+\/widget\.(json|png)/
"/auth/login",
"/auth/register",
"/webhooks/",
"/ping",
"/gateway",
"/experiments"
// /^\/api(\/v\d+)?\/guilds\/\d+\/widget\.(json|png)/
];
export const API_PREFIX = /^\/api(\/v\d+)?/;
@ -24,13 +24,14 @@ declare global {
}
}
}
// TODO wenn client offen ist, wird http://localhost:8080/api/v9/users/@me/guild-events blockiert?
export async function Authentication(req: Request, res: Response, next: NextFunction) {
if (req.method === "OPTIONS") return res.sendStatus(204);
if (!req.url.startsWith("/api")) return next();
const apiPath = req.url.replace(API_PREFIX, "");
if (apiPath.startsWith("/invites") && req.method === "GET") return next();
if (NO_AUTHORIZATION_ROUTES.some((x) => x.test(req.url))) return next();
if (apiPath.startsWith("/invites") && req.method === "GET") return next(); // @ts-ignore
if (NO_AUTHORIZATION_ROUTES.some((x) => apiPath.startsWith(x) || x.test?.(req.url))) return next();
if (!req.headers.authorization) return next(new HTTPError("Missing Authorization Header", 401));
try {

View File

@ -25,8 +25,6 @@ export function ErrorHandler(error: Error, req: Request, res: Response, next: Ne
if (httpcode > 511) httpcode = 400;
res.status(httpcode).json({ code: code, message, errors });
return;
} catch (error) {
console.error(error);
return res.status(500).json({ code: 500, message: "Internal Server Error" });

View File

@ -1,16 +1,17 @@
import { db, MongooseCache, Bucket, Config } from "@fosscord/server-util";
// @ts-nocheck
import { db, Bucket, Config } from "@fosscord/util";
import { NextFunction, Request, Response, Router } from "express";
import { getIpAdress } from "../util/ipAddress";
import { API_PREFIX_TRAILING_SLASH } from "./Authentication";
const Cache = new MongooseCache(
db.collection("ratelimits"),
[
// TODO: uncomment $match and fix error: not receiving change events
// { $match: { blocked: true } }
],
{ onlyEvents: false, array: true }
);
// const Cache = new MongooseCache(
// db.collection("ratelimits"),
// [
// // TODO: uncomment $match and fix error: not receiving change events
// // { $match: { blocked: true } }
// ],
// { onlyEvents: false, array: true }
// );
// Docs: https://discord.com/developers/docs/topics/rate-limits
@ -31,6 +32,7 @@ TODO: use config values
*/
// TODO: FIX with new event handling
export default function RateLimit(opts: {
bucket?: string;
window: number;
@ -44,6 +46,7 @@ export default function RateLimit(opts: {
success?: boolean;
onlyIp?: boolean;
}): any {
return (req, res, next) => next();
Cache.init(); // will only initalize it once
return async (req: Request, res: Response, next: NextFunction): Promise<any> => {

View File

@ -2,7 +2,7 @@ import { Request, Response, Router } from "express";
import { check, FieldErrors, Length } from "../../util/instanceOf";
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import { Config, UserModel } from "@fosscord/server-util";
import { Config, UserModel } from "@fosscord/util";
import { adjustEmail } from "./register";
import RateLimit from "../../middlewares/RateLimit";

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
import { trimSpecial, User, Snowflake, UserModel, Config } from "@fosscord/server-util";
import { trimSpecial, User, Snowflake, UserModel, Config } from "@fosscord/util";
import bcrypt from "bcrypt";
import { check, Email, EMAIL_REGEX, FieldErrors, Length } from "../../util/instanceOf";
import "missing-native-js-functions";

View File

@ -1,8 +1,7 @@
import { ChannelDeleteEvent, ChannelModel, ChannelUpdateEvent, getPermission, GuildUpdateEvent, toObject } from "@fosscord/server-util";
import { ChannelDeleteEvent, ChannelModel, ChannelUpdateEvent, emitEvent, getPermission, GuildUpdateEvent, toObject } from "@fosscord/util";
import { Router, Response, Request } from "express";
import { HTTPError } from "lambert-server";
import { ChannelModifySchema } from "../../../schema/Channel";
import { emitEvent } from "../../../util/Event";
import { check } from "../../../util/instanceOf";
const router: Router = Router();
// TODO: delete channel

View File

@ -3,11 +3,10 @@ import { HTTPError } from "lambert-server";
import { check } from "../../../util/instanceOf";
import { random } from "../../../util/RandomInviteID";
import { emitEvent } from "../../../util/Event";
import { InviteCreateSchema } from "../../../schema/Invite";
import { getPermission, ChannelModel, InviteModel, InviteCreateEvent, toObject } from "@fosscord/server-util";
import { getPermission, ChannelModel, InviteModel, InviteCreateEvent, toObject, emitEvent } from "@fosscord/util";
const router: Router = Router();

View File

@ -1,6 +1,6 @@
import { getPermission, MessageAckEvent, ReadStateModel } from "@fosscord/server-util";
import { emitEvent, getPermission, MessageAckEvent, ReadStateModel } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { emitEvent } from "../../../../../util/Event";
import { check } from "../../../../../util/instanceOf";
const router = Router();

View File

@ -1,8 +1,8 @@
import { ChannelModel, getPermission, MessageDeleteEvent, MessageModel, MessageUpdateEvent, toObject } from "@fosscord/server-util";
import { ChannelModel, emitEvent, getPermission, MessageDeleteEvent, MessageModel, MessageUpdateEvent, toObject } from "@fosscord/util";
import { Router, Response, Request } from "express";
import { HTTPError } from "lambert-server";
import { MessageCreateSchema } from "../../../../../schema/Message";
import { emitEvent } from "../../../../../util/Event";
import { check } from "../../../../../util/instanceOf";
import { handleMessage, postHandleMessage } from "../../../../../util/Message";

View File

@ -1,5 +1,6 @@
import {
ChannelModel,
emitEvent,
EmojiModel,
getPermission,
MemberModel,
@ -12,10 +13,9 @@ import {
PublicUserProjection,
toObject,
UserModel
} from "@fosscord/server-util";
} from "@fosscord/util";
import { Router, Response, Request } from "express";
import { HTTPError } from "lambert-server";
import { emitEvent } from "../../../../../util/Event";
const router = Router();
// TODO: check if emoji is really an unicode emoji or a prperly encoded external emoji

View File

@ -1,7 +1,7 @@
import { Router, Response, Request } from "express";
import { ChannelModel, Config, getPermission, MessageDeleteBulkEvent, MessageModel } from "@fosscord/server-util";
import { ChannelModel, Config, emitEvent, getPermission, MessageDeleteBulkEvent, MessageModel } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { emitEvent } from "../../../../util/Event";
import { check } from "../../../../util/instanceOf";
const router: Router = Router();

View File

@ -1,5 +1,5 @@
import { Router, Response, Request } from "express";
import { Attachment, ChannelModel, ChannelType, getPermission, MessageDocument, MessageModel, toObject } from "@fosscord/server-util";
import { Attachment, ChannelModel, ChannelType, getPermission, MessageDocument, MessageModel, toObject } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { MessageCreateSchema } from "../../../../schema/Message";
import { check, instanceOf, Length } from "../../../../util/instanceOf";

View File

@ -1,7 +1,15 @@
import { ChannelModel, ChannelPermissionOverwrite, ChannelUpdateEvent, getPermission, MemberModel, RoleModel } from "@fosscord/server-util";
import {
ChannelModel,
ChannelPermissionOverwrite,
ChannelUpdateEvent,
emitEvent,
getPermission,
MemberModel,
RoleModel
} from "@fosscord/util";
import { Router, Response, Request } from "express";
import { HTTPError } from "lambert-server";
import { emitEvent } from "../../../util/Event";
import { check } from "../../../util/instanceOf";
const router: Router = Router();

View File

@ -2,14 +2,14 @@ import {
ChannelModel,
ChannelPinsUpdateEvent,
Config,
emitEvent,
getPermission,
MessageModel,
MessageUpdateEvent,
toObject
} from "@fosscord/server-util";
} from "@fosscord/util";
import { Router, Request, Response } from "express";
import { HTTPError } from "lambert-server";
import { emitEvent } from "../../../util/Event";
const router: Router = Router();

View File

@ -1,8 +1,7 @@
import { ChannelModel, MemberModel, toObject, TypingStartEvent } from "@fosscord/server-util";
import { ChannelModel, emitEvent, MemberModel, toObject, TypingStartEvent } from "@fosscord/util";
import { Router, Request, Response } from "express";
import { HTTPError } from "lambert-server";
import { emitEvent } from "../../../util/Event";
const router: Router = Router();

View File

@ -1,6 +1,6 @@
import { Router, Response, Request } from "express";
import { check, Length } from "../../../util/instanceOf";
import { ChannelModel, getPermission, trimSpecial } from "@fosscord/server-util";
import { ChannelModel, getPermission, trimSpecial } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { isTextChannel } from "./messages/index";

View File

@ -1,4 +1,4 @@
import { Config } from "@fosscord/server-util";
import { Config } from "@fosscord/util";
import { Router, Response, Request } from "express";
const router = Router();

View File

@ -1,9 +1,9 @@
import { Request, Response, Router } from "express";
import { BanModel, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, GuildModel, toObject } from "@fosscord/server-util";
import { BanModel, emitEvent, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, GuildModel, toObject } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { getIpAdress } from "../../../util/ipAddress";
import { BanCreateSchema } from "../../../schema/Ban";
import { emitEvent } from "../../../util/Event";
import { check } from "../../../util/instanceOf";
import { removeMember } from "../../../util/Member";
import { getPublicUser } from "../../../util/User";

View File

@ -8,11 +8,12 @@ import {
toObject,
ChannelUpdateEvent,
AnyChannel,
getPermission
} from "@fosscord/server-util";
getPermission,
emitEvent
} from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { ChannelModifySchema } from "../../../schema/Channel";
import { emitEvent } from "../../../util/Event";
import { check } from "../../../util/instanceOf";
import { createChannel } from "../../../util/Channel";
const router = Router();
@ -64,7 +65,7 @@ router.patch(
const channel = await ChannelModel.findOneAndUpdate({ id: req.body, guild_id }, opts).exec();
await emitEvent({ event: "CHANNEL_UPDATE", data: channel, channel_id: body.id, guild_id } as ChannelUpdateEvent);
await emitEvent({ event: "CHANNEL_UPDATE", data: toObject(channel), channel_id: body.id, guild_id } as ChannelUpdateEvent);
res.json(toObject(channel));
}

View File

@ -1,5 +1,6 @@
import {
ChannelModel,
emitEvent,
EmojiModel,
GuildDeleteEvent,
GuildModel,
@ -8,10 +9,9 @@ import {
MessageModel,
RoleModel,
UserModel
} from "@fosscord/server-util";
} from "@fosscord/util";
import { Router, Request, Response } from "express";
import { HTTPError } from "lambert-server";
import { emitEvent } from "../../../util/Event";
const router = Router();

View File

@ -1,6 +1,7 @@
import { Request, Response, Router } from "express";
import {
ChannelModel,
emitEvent,
EmojiModel,
getPermission,
GuildDeleteEvent,
@ -12,10 +13,10 @@ import {
RoleModel,
toObject,
UserModel
} from "@fosscord/server-util";
} from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { GuildUpdateSchema } from "../../../schema/Guild";
import { emitEvent } from "../../../util/Event";
import { check } from "../../../util/instanceOf";
import { handleFile } from "../../../util/cdn";
import "missing-native-js-functions";

View File

@ -1,4 +1,4 @@
import { getPermission, InviteModel, toObject } from "@fosscord/server-util";
import { getPermission, InviteModel, toObject } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router();

View File

@ -8,13 +8,13 @@ import {
getPermission,
PermissionResolvable,
RoleModel,
GuildMemberUpdateEvent
} from "@fosscord/server-util";
GuildMemberUpdateEvent,
emitEvent
} from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { addMember, isMember, removeMember } from "../../../../../util/Member";
import { check } from "../../../../../util/instanceOf";
import { MemberChangeSchema } from "../../../../../schema/Member";
import { emitEvent } from "../../../../../util/Event";
const router = Router();

View File

@ -1,4 +1,4 @@
import { getPermission, PermissionResolvable } from "@fosscord/server-util";
import { getPermission, PermissionResolvable } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { check } from "lambert-server";
import { MemberNickChangeSchema } from "../../../../../schema/Member";

View File

@ -1,4 +1,4 @@
import { getPermission } from "@fosscord/server-util";
import { getPermission } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { addRole, removeRole } from "../../../../../../../util/Member";

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
import { GuildModel, MemberModel, toObject } from "@fosscord/server-util";
import { GuildModel, MemberModel, toObject } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { instanceOf, Length } from "../../../../util/instanceOf";
import { PublicMemberProjection, isMember } from "../../../../util/Member";

View File

@ -1,4 +1,4 @@
import { Config } from "@fosscord/server-util";
import { Config } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router();
@ -7,4 +7,4 @@ router.get("/", async (req: Request, res: Response) => {
return res.json(Config.get().regions.available);
});
export default router;
export default router;

View File

@ -9,10 +9,11 @@ import {
MemberModel,
GuildRoleCreateEvent,
GuildRoleUpdateEvent,
GuildRoleDeleteEvent
} from "@fosscord/server-util";
GuildRoleDeleteEvent,
emitEvent
} from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { emitEvent } from "../../../util/Event";
import { check } from "../../../util/instanceOf";
import { RoleModifySchema } from "../../../schema/Roles";
import { getPublicUser } from "../../../util/User";

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
import { TemplateModel, GuildModel, getPermission, toObject, UserModel, Snowflake } from "@fosscord/server-util";
import { TemplateModel, GuildModel, getPermission, toObject, UserModel, Snowflake } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { TemplateCreateSchema, TemplateModifySchema } from "../../../schema/Template";
import { check } from "../../../util/instanceOf";

View File

@ -1,4 +1,4 @@
import { getPermission, GuildModel, InviteModel, trimSpecial } from "@fosscord/server-util";
import { getPermission, GuildModel, InviteModel, trimSpecial } from "@fosscord/util";
import { Router, Request, Response } from "express";
import { HTTPError } from "lambert-server";
import { check, Length } from "../../../util/instanceOf";

View File

@ -1,7 +1,7 @@
import { Request, Response, Router } from "express";
import { GuildModel, getPermission, toObject, Snowflake } from "@fosscord/server-util";
import { GuildModel, getPermission, toObject, Snowflake } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { emitEvent } from "../../../util/Event";
import { check } from "../../../util/instanceOf";
import { isMember } from "../../../util/Member";
import { GuildAddChannelToWelcomeScreenSchema } from "../../../schema/Guild";

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
import { Config, Permissions, GuildModel, InviteModel, ChannelModel, MemberModel } from "@fosscord/server-util";
import { Config, Permissions, GuildModel, InviteModel, ChannelModel, MemberModel } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { random } from "../../../util/RandomInviteID";

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
import { GuildModel } from "@fosscord/server-util";
import { GuildModel } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import fs from "fs";
import path from "path";

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
import { getPermission, GuildModel } from "@fosscord/server-util";
import { getPermission, GuildModel } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { check } from "../../../util/instanceOf";
import { WidgetModifySchema } from "../../../schema/Widget";

View File

@ -1,5 +1,5 @@
import { Router, Request, Response } from "express";
import { RoleModel, GuildModel, Snowflake, Guild, RoleDocument, Config } from "@fosscord/server-util";
import { RoleModel, GuildModel, Snowflake, Guild, RoleDocument, Config } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { check } from "./../../util/instanceOf";
import { GuildCreateSchema } from "../../schema/Guild";

View File

@ -1,6 +1,6 @@
import { Request, Response, Router } from "express";
const router: Router = Router();
import { TemplateModel, GuildModel, toObject, UserModel, RoleModel, Snowflake, Guild, Config } from "@fosscord/server-util";
import { TemplateModel, GuildModel, toObject, UserModel, RoleModel, Snowflake, Guild, Config } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { GuildTemplateCreateSchema } from "../../../schema/Guild";
import { getPublicUser } from "../../../util/User";

View File

@ -1,5 +1,5 @@
import { Router, Request, Response } from "express";
import { getPermission, InviteModel, toObject } from "@fosscord/server-util";
import { getPermission, InviteModel, toObject } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { addMember } from "../../util/Member";
const router: Router = Router();

View File

@ -8,10 +8,11 @@ import {
trimSpecial,
Channel,
DMChannel,
UserModel
} from "@fosscord/server-util";
UserModel,
emitEvent
} from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { emitEvent } from "../../../util/Event";
import { DmChannelCreateSchema } from "../../../schema/Channel";
import { check } from "../../../util/instanceOf";

View File

@ -1,5 +1,5 @@
import { Router, Request, Response } from "express";
import { GuildModel, MemberModel, UserModel } from "@fosscord/server-util";
import { GuildModel, MemberModel, UserModel } from "@fosscord/util";
import bcrypt from "bcrypt";
const router = Router();

View File

@ -1,4 +1,4 @@
import { UserModel } from "@fosscord/server-util";
import { UserModel } from "@fosscord/util";
import { Router, Response, Request } from "express";
import bcrypt from "bcrypt";

View File

@ -1,7 +1,7 @@
import { Router, Request, Response } from "express";
import { GuildModel, MemberModel, UserModel, GuildDeleteEvent, GuildMemberRemoveEvent, toObject } from "@fosscord/server-util";
import { GuildModel, MemberModel, UserModel, GuildDeleteEvent, GuildMemberRemoveEvent, toObject, emitEvent } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { emitEvent } from "../../../util/Event";
import { getPublicUser } from "../../../util/User";
const router: Router = Router();
@ -32,10 +32,10 @@ router.delete("/:id", async (req: Request, res: Response) => {
emitEvent({
event: "GUILD_DELETE",
data: {
id: guild_id,
id: guild_id
},
user_id: req.user_id,
} as GuildDeleteEvent),
user_id: req.user_id
} as GuildDeleteEvent)
]);
const user = await getPublicUser(req.user_id);
@ -44,9 +44,9 @@ router.delete("/:id", async (req: Request, res: Response) => {
event: "GUILD_MEMBER_REMOVE",
data: {
guild_id: guild_id,
user: user,
user: user
},
guild_id: guild_id,
guild_id: guild_id
} as GuildMemberRemoveEvent);
return res.sendStatus(204);

View File

@ -1,5 +1,5 @@
import { Router, Request, Response } from "express";
import { UserModel, toObject, PublicUserProjection } from "@fosscord/server-util";
import { UserModel, toObject, PublicUserProjection } from "@fosscord/util";
import { getPublicUser } from "../../../util/User";
import { UserModifySchema } from "../../../schema/User";
import { check } from "../../../util/instanceOf";

View File

@ -5,11 +5,12 @@ import {
toObject,
RelationshipType,
RelationshipRemoveEvent,
UserDocument
} from "@fosscord/server-util";
UserDocument,
emitEvent
} from "@fosscord/util";
import { Router, Response, Request } from "express";
import { HTTPError } from "lambert-server";
import { emitEvent } from "../../../util/Event";
import { check, Length } from "../../../util/instanceOf";
const router = Router();

View File

@ -1,4 +1,4 @@
import { ChannelType } from "@fosscord/server-util";
import { ChannelType } from "@fosscord/util";
import { Length } from "../util/instanceOf";
export const ChannelModifySchema = {

View File

@ -1,4 +1,4 @@
import { ChannelSchema, GuildChannel } from "@fosscord/server-util";
import { ChannelSchema, GuildChannel } from "@fosscord/util";
import { Length } from "../util/instanceOf";
export const GuildCreateSchema = {

View File

@ -1,4 +1,4 @@
import { Embed, EmbedImage } from "@fosscord/server-util";
import { Embed, EmbedImage } from "@fosscord/util";
import { Length } from "../util/instanceOf";
export const MessageCreateSchema = {

View File

@ -1,3 +1,3 @@
import { Snowflake } from "@fosscord/server-util";
import { Snowflake } from "@fosscord/util";
console.log(Snowflake.deconstruct("0"));

View File

@ -2,18 +2,18 @@ import {
ChannelCreateEvent,
ChannelModel,
ChannelType,
emitEvent,
getPermission,
GuildModel,
Snowflake,
TextChannel,
toObject,
VoiceChannel
} from "@fosscord/server-util";
} from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { emitEvent } from "./Event";
// TODO: DM channel
export async function createChannel(channel: Partial<TextChannel | VoiceChannel>, user_id: string = "0") {
// Always check if user has permission first
const permissions = await getPermission(user_id, channel.guild_id);
permissions.hasThrow("MANAGE_CHANNELS");
@ -50,7 +50,7 @@ export async function createChannel(channel: Partial<TextChannel | VoiceChannel>
recipient_ids: null
}).save();
await emitEvent({ event: "CHANNEL_CREATE", data: channel, guild_id: channel.guild_id } as ChannelCreateEvent);
await emitEvent({ event: "CHANNEL_CREATE", data: toObject(channel), guild_id: channel.guild_id } as ChannelCreateEvent);
return channel;
}

View File

@ -1,7 +1,7 @@
// @ts-nocheck
import Ajv, { JSONSchemaType } from "ajv";
import { getConfigPathForFile } from "@fosscord/server-util/dist/util/Config";
import { Config } from "@fosscord/server-util";
import { getConfigPathForFile } from "@fosscord/util/dist/util/Config";
import { Config } from "@fosscord/util";
export interface RateLimitOptions {
count: number;

View File

@ -1,26 +0,0 @@
import { Config, Event, EventModel, RabbitMQ } from "@fosscord/server-util";
export async function emitEvent(payload: Omit<Event, "created_at">) {
if (RabbitMQ.connection) {
const id = (payload.channel_id || payload.user_id || payload.guild_id) as string;
if (!id) console.error("event doesn't contain any id", payload);
const data = typeof payload.data === "object" ? JSON.stringify(payload.data) : payload.data; // use rabbitmq for event transmission
await RabbitMQ.channel?.assertExchange(id, "fanout", { durable: false });
// assertQueue isn't needed, because a queue will automatically created if it doesn't exist
const successful = RabbitMQ.channel?.publish(id, "", Buffer.from(`${data}`), { type: payload.event });
if (!successful) throw new Error("failed to send event");
} else {
// use mongodb for event transmission
// TODO: use event emitter for local server bundle
const obj = {
created_at: new Date(), // in seconds
...payload
};
// TODO: bigint isn't working
return await new EventModel(obj).save();
}
}
export async function emitAuditLog(payload: any) {}

View File

@ -11,11 +11,12 @@ import {
toObject,
UserModel,
GuildDocument,
Config
} from "@fosscord/server-util";
Config,
emitEvent
} from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { emitEvent } from "./Event";
import { getPublicUser } from "./User";
export const PublicMemberProjection = {

View File

@ -1,14 +1,14 @@
import { ChannelModel, Embed, Message, MessageCreateEvent, MessageUpdateEvent } from "@fosscord/server-util";
import { Snowflake } from "@fosscord/server-util";
import { MessageModel } from "@fosscord/server-util";
import { PublicMemberProjection } from "@fosscord/server-util";
import { toObject } from "@fosscord/server-util";
import { getPermission } from "@fosscord/server-util";
import { ChannelModel, Embed, emitEvent, Message, MessageCreateEvent, MessageUpdateEvent } from "@fosscord/util";
import { Snowflake } from "@fosscord/util";
import { MessageModel } from "@fosscord/util";
import { PublicMemberProjection } from "@fosscord/util";
import { toObject } from "@fosscord/util";
import { getPermission } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import fetch from "node-fetch";
import cheerio from "cheerio";
import { emitEvent } from "./Event";
import { MessageType } from "@fosscord/server-util/dist/util/Constants";
import { MessageType } from "@fosscord/util/dist/util/Constants";
// TODO: check webhook, application, system author
const LINK_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;

View File

@ -1,4 +1,4 @@
import { toObject, UserModel, PublicUserProjection } from "@fosscord/server-util";
import { toObject, UserModel, PublicUserProjection } from "@fosscord/util";
import { HTTPError } from "lambert-server";
export { PublicUserProjection };

View File

@ -1,4 +1,4 @@
import { Config } from "@fosscord/server-util";
import { Config } from "@fosscord/util";
import FormData from "form-data";
import { HTTPError } from "lambert-server";
import fetch from "node-fetch";

View File

@ -1,4 +1,4 @@
import { Config } from "@fosscord/server-util";
import { Config } from "@fosscord/util";
import { Request } from "express";
// use ipdata package instead of simple fetch because of integrated caching
import fetch from "node-fetch";

View File

@ -1,4 +1,4 @@
import { Config } from "@fosscord/server-util";
import { Config } from "@fosscord/util";
import "missing-native-js-functions";
const reNUMBER = /[0-9]/g;

2750
bundle/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

42
bundle/package.json Normal file
View File

@ -0,0 +1,42 @@
{
"name": "@fosscord/server",
"version": "1.0.0",
"description": "",
"main": "src/start.js",
"scripts": {
"linkInstall": "npm run --prefix ../util/ link && npm run --prefix ../api/ link && npm run --prefix ../cdn/ link && npm run --prefix ../gateway/ link",
"postinstall": "npm run linkInstall && npm link @fosscord/util && npm link @fosscord/api && npm link @fosscord/gateway && npm link @fosscord/cdn",
"build": "tsc -b .",
"start": "npm run build && node dist/start.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/fosscord/fosscord-api.git"
},
"keywords": [],
"author": "Fosscord",
"license": "AGPLV3",
"bugs": {
"url": "https://github.com/fosscord/fosscord-api/issues"
},
"homepage": "https://github.com/fosscord/fosscord-api#readme",
"devDependencies": {
"@types/async-exit-hook": "^2.0.0",
"@types/express": "^4.17.13",
"@types/node": "^16.6.1",
"@types/node-os-utils": "^1.2.0",
"typescript": "^4.3.5"
},
"dependencies": {
"@fosscord/api": "file:../api",
"@fosscord/cdn": "file:../cdn",
"@fosscord/gateway": "file:../gateway",
"@fosscord/util": "file:../util",
"async-exit-hook": "^2.0.1",
"express": "^4.17.1",
"link": "^0.1.5",
"mongodb-memory-server-global-4.4": "^7.3.6",
"node-os-utils": "^1.3.5"
}
}

33
bundle/src/Server.ts Normal file
View File

@ -0,0 +1,33 @@
process.on("unhandledRejection", console.error);
process.on("uncaughtException", console.error);
import http from "http";
import { FosscordServer as APIServer } from "@fosscord/api";
import { Server as GatewayServer } from "@fosscord/gateway";
import { CDNServer } from "@fosscord/cdn/";
import express from "express";
import { Config } from "../../util/dist";
const app = express();
const server = http.createServer();
const port = Number(process.env.PORT) || 8080;
const production = true;
server.on("request", app);
// @ts-ignore
const api = new APIServer({ server, port, production, app });
// @ts-ignore
const cdn = new CDNServer({ server, port, production, app });
// @ts-ignore
const gateway = new GatewayServer({ server, port, production });
async function main() {
await api.start();
await cdn.start();
await gateway.start();
if (!Config.get().gateway.endpoint) await Config.set({ gateway: { endpoint: `ws://localhost:${port}` } });
if (!Config.get().cdn.endpoint) await Config.set({ cdn: { endpoint: `http://localhost:${port}` } });
}
main().catch(console.error);

74
bundle/src/start.ts Normal file
View File

@ -0,0 +1,74 @@
import fs from "fs";
import { MongoMemoryServer } from "mongodb-memory-server-global-4.4";
import path from "path";
import cluster from "cluster";
import os from "os";
import osu from "node-os-utils";
import exitHook from "async-exit-hook";
const cores = Number(process.env.threads) || 1 || os.cpus().length;
if (cluster.isMaster && !process.env.masterStarted) {
const dbPath = path.join(__dirname, "..", "..", "db");
const dbName = "fosscord";
const storageEngine = "wiredTiger";
const port = 27020;
const ip = "127.0.0.1";
var mongod: MongoMemoryServer;
fs.mkdirSync(dbPath, { recursive: true });
exitHook((callback: any) => {
(async () => {
console.log(`Stopping MongoDB ...`);
await mongod.stop();
console.log(`Stopped MongoDB`);
callback();
})();
});
process.env.masterStarted = "true";
setInterval(async () => {
const [cpuUsed, memory, network] = await Promise.all([osu.cpu.usage(), osu.mem.info(), osu.netstat.inOut()]);
if (typeof network === "object") {
console.log(`Network: in ${network.total.inputMb}mb | out ${network.total.outputMb}mb`);
}
console.log(
`[CPU] ${cpuUsed.toFixed(2)}% | [Memory] ${memory.usedMemMb.toFixed(0)}mb/${memory.totalMemMb.toFixed(0)}mb`
);
}, 1000 * 60);
(async () => {
console.log(`[Database] starting ...`);
mongod = new MongoMemoryServer({
instance: {
port,
ip,
dbName,
dbPath,
storageEngine,
auth: false, // by default `mongod` is started with '--noauth', start `mongod` with '--auth'
},
});
await mongod.start();
process.env.MONGO_URL = mongod.getUri(dbName);
console.log(`[CPU] ${osu.cpu.model()} Cores x${osu.cpu.count()}`);
console.log(`[System] ${await osu.os.oos()} ${os.arch()}`);
console.log(`[Database] started`);
console.log(`[Process] running with pid: ${process.pid}`);
// Fork workers.
for (let i = 0; i < cores; i++) {
cluster.fork();
}
cluster.on("exit", (worker: any, code: any, signal: any) => {
console.log(`[Worker] died with pid: ${worker.process.pid} , restarting ...`);
cluster.fork();
});
})();
} else {
require("./Server.js");
}

View File

@ -5,7 +5,7 @@
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "ES2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
"target": "ESNext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
"lib": ["ES2020"] /* Specify library files to be included in the compilation. */,
"allowJs": true /* Allow javascript files to be compiled. */,
@ -43,8 +43,6 @@
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
"types": ["node"] /* Type declaration files to be included in compilation. */,

View File

@ -5,6 +5,8 @@
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"link": "npm run build && npm link",
"postinstall": "npm run --prefix ../util/ link && npm link @fosscord/util && npm run link",
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc -b .",
"start": "npm run build && node dist/start.js"
@ -21,7 +23,7 @@
},
"homepage": "https://github.com/discord-open-source/discord-cdn#readme",
"dependencies": {
"@fosscord/server-util": "file:../util",
"@fosscord/util": "file:../util",
"body-parser": "^1.19.0",
"btoa": "^1.2.1",
"cheerio": "^1.0.0-rc.5",

View File

@ -1,5 +1,5 @@
import { Server, ServerOptions } from "lambert-server";
import { Config, db } from "@fosscord/server-util";
import { Config, db } from "@fosscord/util";
import path from "path";
import avatarsRoute from "./routes/avatars";
@ -13,11 +13,9 @@ export class CDNServer extends Server {
}
async start() {
console.log("[Database] connecting ...");
// @ts-ignore
await (db as Promise<Connection>);
await Config.init();
console.log("[Database] connected");
this.app.use((req, res, next) => {
res.set("Access-Control-Allow-Origin", "*");
// TODO: use better CSP policy
@ -33,31 +31,31 @@ export class CDNServer extends Server {
await this.registerRoutes(path.join(__dirname, "routes/"));
this.app.use("/icons/", avatarsRoute);
this.log("info", "[Server] Route /icons registered");
this.log("verbose", "[Server] Route /icons registered");
this.app.use("/emojis/", avatarsRoute);
this.log("info", "[Server] Route /emojis registered");
this.log("verbose", "[Server] Route /emojis registered");
this.app.use("/stickers/", avatarsRoute);
this.log("info", "[Server] Route /stickers registered");
this.log("verbose", "[Server] Route /stickers registered");
this.app.use("/banners/", avatarsRoute);
this.log("info", "[Server] Route /banners registered");
this.log("verbose", "[Server] Route /banners registered");
this.app.use("/splashes/", avatarsRoute);
this.log("info", "[Server] Route /splashes registered");
this.log("verbose", "[Server] Route /splashes registered");
this.app.use("/app-icons/", avatarsRoute);
this.log("info", "[Server] Route /app-icons registered");
this.log("verbose", "[Server] Route /app-icons registered");
this.app.use("/app-assets/", avatarsRoute);
this.log("info", "[Server] Route /app-assets registered");
this.log("verbose", "[Server] Route /app-assets registered");
this.app.use("/discover-splashes/", avatarsRoute);
this.log("info", "[Server] Route /discover-splashes registered");
this.log("verbose", "[Server] Route /discover-splashes registered");
this.app.use("/team-icons/", avatarsRoute);
this.log("info", "[Server] Route /team-icons registered");
this.log("verbose", "[Server] Route /team-icons registered");
return super.start();
}

View File

@ -1,5 +1,5 @@
import { Router, Response, Request } from "express";
import { Config, Snowflake } from "@fosscord/server-util";
import { Config, Snowflake } from "@fosscord/util";
import { storage } from "../util/Storage";
import FileType from "file-type";
import { HTTPError } from "lambert-server";

View File

@ -1,5 +1,5 @@
import { Router, Response, Request } from "express";
import { Config, Snowflake } from "@fosscord/server-util";
import { Config, Snowflake } from "@fosscord/util";
import { storage } from "../util/Storage";
import FileType from "file-type";
import { HTTPError } from "lambert-server";

View File

@ -4,7 +4,7 @@ import { Router, Response, Request } from "express";
import fetch from "node-fetch";
import crypto from "crypto";
import { HTTPError } from "lambert-server";
import { Snowflake } from "@fosscord/server-util";
import { Snowflake } from "@fosscord/util";
import { storage } from "../util/Storage";
const router = Router();

24
docker-compose.yml Normal file
View File

@ -0,0 +1,24 @@
version: "3"
services:
db:
hostname: fosscord_db
image: mongo:latest
volumes:
- ./db:/data/db
restart: unless-stopped
api:
hostname: fosscord_api
image: fosscord/api
depends_on:
- db
ports:
- 3001:3001
env_file: ./.docker/env
gateway:
hostname: fosscord_gateway
image: fosscord/gateway
depends_on:
- db
ports:
- 3002:3002
env_file: ./.docker/env

View File

@ -1,47 +0,0 @@
name: docker-publish
on:
push:
branches:
- 'master'
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Cache Docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: ${{ secrets.DOCKERHUB_TAGS }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
-
# Hackfix to cleanup cache; replace after buildx 0.6 and BuildKit 0.9 are released
# https://github.com/docker/build-push-action/pull/406#issuecomment-879184394
name: Move cache fix
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache

View File

@ -4,16 +4,18 @@
"description": "",
"main": "dist/index.js",
"scripts": {
"link": "npm run build && npm link",
"postinstall": "npm run --prefix ../util/ link && npm link @fosscord/util && npm run link",
"test": "echo \"Error: no test specified\" && exit 1",
"start": "npm run build && node dist/start.js",
"build": "npx tsc -b .",
"build": "tsc -b .",
"dev": "tsnd --respawn src/start.ts"
},
"keywords": [],
"author": "Fosscord",
"license": "ISC",
"dependencies": {
"@fosscord/server-util": "file:../util",
"@fosscord/util": "file:../util",
"ajv": "^8.5.0",
"amqplib": "^0.8.0",
"dotenv": "^8.2.0",

View File

@ -1,7 +1,7 @@
import "missing-native-js-functions";
import dotenv from "dotenv";
dotenv.config();
import { Config, db, RabbitMQ } from "@fosscord/server-util";
import { Config, db, initEvent, RabbitMQ } from "@fosscord/util";
import { Server as WebSocketServer } from "ws";
import { Connection } from "./events/Connection";
import http from "http";
@ -40,8 +40,7 @@ export class Server {
await (db as Promise<Connection>);
await this.setupSchema();
await Config.init();
await RabbitMQ.init();
console.log("[Database] connected");
await initEvent();
if (!this.server.listening) {
this.server.listen(this.port);
console.log(`[Gateway] online on 0.0.0.0:${this.port}`);

View File

@ -40,6 +40,7 @@ export async function Connection(this: Server, socket: WebSocket, request: Incom
socket.deflate.on("data", (chunk) => socket.send(chunk));
}
socket.events = {};
socket.permissions = {};
socket.sequence = 0;

View File

@ -1,63 +1,31 @@
import {
db,
Event,
MongooseCache,
UserModel,
getPermission,
Permissions,
ChannelModel,
RabbitMQ,
EVENT,
} from "@fosscord/server-util";
listenEvent,
EventOpts,
ListenEventOpts,
} from "@fosscord/util";
import { OPCODES } from "../util/Constants";
import { Send } from "../util/Send";
import WebSocket from "../util/WebSocket";
import "missing-native-js-functions";
import { ConsumeMessage } from "amqplib";
import { Channel } from "amqplib";
// TODO: close connection on Invalidated Token
// TODO: check intent
// TODO: Guild Member Update is sent for current-user updates regardless of whether the GUILD_MEMBERS intent is set.
// ? How to resubscribe MongooseCache for new dm channel events? Maybe directly send them to the user_id regardless of the channel_id? -> max overhead of creating 10 events in database for dm user group. Or a new field in event -> recipient_ids?
// Sharding: calculate if the current shard id matches the formula: shard_id = (guild_id >> 22) % num_shards
// https://discord.com/developers/docs/topics/gateway#sharding
export interface DispatchOpts {
eventStream: MongooseCache;
guilds: Array<string>;
}
function getPipeline(this: WebSocket, guilds: string[], channels: string[] = []) {
if (this.shard_count) {
guilds = guilds.filter((x) => (BigInt(x) >> 22n) % this.shard_count! === this.shard_id);
}
return [
{
$match: {
$or: [
{ "fullDocument.guild_id": { $in: guilds } },
{ "fullDocument.user_id": this.user_id },
{ "fullDocument.channel_id": { $in: channels } },
],
},
},
];
}
async function rabbitListen(this: WebSocket, id: string) {
await this.rabbitCh!.assertExchange(id, "fanout", { durable: false });
const q = await this.rabbitCh!.assertQueue("", { exclusive: true, autoDelete: true });
this.rabbitCh!.bindQueue(q.queue, id, "");
this.rabbitCh!.consume(q.queue, consume.bind(this), {
noAck: false,
});
this.rabbitCh!.queues[id] = q.queue;
}
// TODO: use already required guilds/channels of Identify and don't fetch them again
// TODO: use already queried guilds/channels of Identify and don't fetch them again
export async function setupListener(this: WebSocket) {
const user = await UserModel.findOne({ id: this.user_id }, { guilds: true }).exec();
const channels = await ChannelModel.find(
@ -67,90 +35,77 @@ export async function setupListener(this: WebSocket) {
const dm_channels = channels.filter((x) => !x.guild_id);
const guild_channels = channels.filter((x) => x.guild_id);
const opts: { acknowledge: boolean; channel?: Channel } = { acknowledge: true };
const consumer = consume.bind(this);
if (RabbitMQ.connection) {
opts.channel = await RabbitMQ.connection.createChannel();
// @ts-ignore
this.rabbitCh = await RabbitMQ.connection.createChannel();
this.rabbitCh!.queues = {};
rabbitListen.call(this, this.user_id);
for (const channel of dm_channels) {
rabbitListen.call(this, channel.id);
}
for (const guild of user.guilds) {
// contains guild and dm channels
getPermission(this.user_id, guild)
.then((x) => {
this.permissions[guild] = x;
rabbitListen.call(this, guild);
for (const channel of guild_channels) {
if (x.overwriteChannel(channel.permission_overwrites).has("VIEW_CHANNEL")) {
rabbitListen.call(this, channel.id);
}
}
})
.catch((e) => {});
}
this.once("close", () => {
this.rabbitCh!.close();
});
} else {
const eventStream = new MongooseCache(
db.collection("events"),
getPipeline.call(
this,
user.guilds,
channels.map((x) => x.id)
),
{
onlyEvents: true,
}
);
await eventStream.init();
eventStream.on("insert", (document: Event) =>
dispatch.call(this, document, { eventStream, guilds: user.guilds })
);
this.once("close", () => eventStream.destroy());
opts.channel.queues = {};
}
this.events[this.user_id] = await listenEvent(this.user_id, consumer, opts);
for (const channel of dm_channels) {
this.events[channel.id] = await listenEvent(channel.id, consumer, opts);
}
for (const guild of user.guilds) {
// contains guild and dm channels
getPermission(this.user_id, guild)
.then(async (x) => {
this.permissions[guild] = x;
this.listeners;
this.events[guild] = await listenEvent(guild, consumer, opts);
for (const channel of guild_channels) {
if (x.overwriteChannel(channel.permission_overwrites).has("VIEW_CHANNEL")) {
this.events[channel.id] = await listenEvent(channel.id, consumer, opts);
}
}
})
.catch((e) => {});
}
this.once("close", () => {
if (opts.channel) opts.channel.close();
else Object.values(this.events).forEach((x) => x());
});
}
// TODO: use rabbitmq to only receive events that are included in intents
function consume(this: WebSocket, opts: ConsumeMessage | null) {
if (!opts) return;
if (!this.rabbitCh) return;
const data = JSON.parse(opts.content.toString());
// TODO: only subscribe for events that are in the connection intents
function consume(this: WebSocket, opts: EventOpts) {
const { data, event } = opts;
const id = data.id as string;
const event = opts.properties.type as EVENT;
const permission = this.permissions[id] || new Permissions("ADMINISTRATOR"); // default permission for dm
console.log("rabbitmq event", event);
const consumer = consume.bind(this);
const listenOpts = opts as ListenEventOpts;
console.log("event", event);
// subscription managment
switch (event) {
case "CHANNEL_DELETE":
case "GUILD_DELETE":
this.rabbitCh.cancel(id);
delete this.events[id];
opts.cancel();
break;
case "CHANNEL_CREATE":
if (!permission.overwriteChannel(data.permission_overwrites).has("VIEW_CHANNEL")) return;
// TODO: check if user has permission to channel
case "GUILD_CREATE":
rabbitListen.call(this, id);
listenEvent(id, consumer, listenOpts);
break;
case "CHANNEL_UPDATE":
const queue_id = this.rabbitCh.queues[id];
const exists = this.events[id];
// @ts-ignore
const exists = this.rabbitCh.consumers[id];
if (permission.overwriteChannel(data.permission_overwrites).has("VIEW_CHANNEL")) {
if (exists) break;
rabbitListen.call(this, id);
listenEvent(id, consumer, listenOpts);
} else {
if (!exists) break;
this.rabbitCh.cancel(queue_id);
this.rabbitCh.unbindQueue(queue_id, id, "");
if (!exists) return; // return -> do not send channel update events for hidden channels
opts.cancel(id);
delete this.events[id];
}
break;
}
@ -216,167 +171,5 @@ function consume(this: WebSocket, opts: ConsumeMessage | null) {
d: data,
s: this.sequence++,
});
this.rabbitCh.ack(opts);
}
// TODO: cache permission
// we shouldn't fetch the permission for every event, as a message send event with many channel members would result in many thousand db queries.
// instead we should calculate all (guild, channel) permissions once and dynamically update if it changes.
// TODO: only subscribe for events that are in the connection intents
// TODO: only subscribe for channel/guilds that the user has access to (and re-subscribe if it changes)
export async function dispatch(this: WebSocket, document: Event, { eventStream, guilds }: DispatchOpts) {
var permission = new Permissions("ADMINISTRATOR"); // default permission for dms
console.log("event", document);
var channel_id = document.channel_id || document.data?.channel_id;
// TODO: clean up
if (document.event === "GUILD_CREATE") {
guilds.push(document.data.id);
eventStream.changeStream(getPipeline.call(this, guilds));
} else if (document.event === "GUILD_DELETE") {
guilds.remove(document.guild_id!);
eventStream.changeStream(getPipeline.call(this, guilds));
} else if (document.event === "CHANNEL_DELETE") channel_id = null;
if (document.guild_id && !this.intents.has("GUILDS")) return;
try {
permission = await getPermission(this.user_id, document.guild_id, channel_id);
} catch (e) {
permission = new Permissions();
}
// check intents: https://discord.com/developers/docs/topics/gateway#gateway-intents
switch (document.event) {
case "GUILD_DELETE":
case "GUILD_CREATE":
case "GUILD_UPDATE":
case "GUILD_ROLE_CREATE":
case "GUILD_ROLE_UPDATE":
case "GUILD_ROLE_DELETE":
case "CHANNEL_CREATE":
case "CHANNEL_DELETE":
case "CHANNEL_UPDATE":
// gets sent if GUILDS intent is set (already checked in if document.guild_id)
break;
case "GUILD_INTEGRATIONS_UPDATE":
if (!this.intents.has("GUILD_INTEGRATIONS")) return;
break;
case "WEBHOOKS_UPDATE":
if (!this.intents.has("GUILD_WEBHOOKS")) return;
break;
case "GUILD_EMOJI_UPDATE":
if (!this.intents.has("GUILD_EMOJIS")) return;
break;
// only send them, if the user subscribed for this part of the member list, or is a bot
case "GUILD_MEMBER_ADD":
case "GUILD_MEMBER_REMOVE":
case "GUILD_MEMBER_UPDATE":
if (!this.intents.has("GUILD_MEMBERS")) return;
break;
case "VOICE_STATE_UPDATE":
if (!this.intents.has("GUILD_VOICE_STATES")) return;
break;
case "GUILD_BAN_ADD":
case "GUILD_BAN_REMOVE":
if (!this.intents.has("GUILD_BANS")) return;
break;
case "INVITE_CREATE":
case "INVITE_DELETE":
if (!this.intents.has("GUILD_INVITES")) return;
case "PRESENCE_UPDATE":
if (!this.intents.has("GUILD_PRESENCES")) return;
break;
case "MESSAGE_CREATE":
case "MESSAGE_DELETE":
case "MESSAGE_DELETE_BULK":
case "MESSAGE_UPDATE":
case "CHANNEL_PINS_UPDATE":
if (!this.intents.has("GUILD_MESSAGES") && document.guild_id) return;
if (!this.intents.has("DIRECT_MESSAGES") && !document.guild_id) return;
break;
case "MESSAGE_REACTION_ADD":
case "MESSAGE_REACTION_REMOVE":
case "MESSAGE_REACTION_REMOVE_ALL":
case "MESSAGE_REACTION_REMOVE_EMOJI":
if (!this.intents.has("GUILD_MESSAGE_REACTIONS") && document.guild_id) return;
if (!this.intents.has("DIRECT_MESSAGE_REACTIONS") && !document.guild_id) return;
break;
case "TYPING_START":
if (!this.intents.has("GUILD_MESSAGE_TYPING") && document.guild_id) return;
if (!this.intents.has("DIRECT_MESSAGE_TYPING") && !document.guild_id) return;
break;
case "READY": // will be sent by the gateway
case "USER_UPDATE":
case "APPLICATION_COMMAND_CREATE":
case "APPLICATION_COMMAND_DELETE":
case "APPLICATION_COMMAND_UPDATE":
default:
// Any events not defined in an intent are considered "passthrough" and will always be sent to you.
break;
}
// check permissions
switch (document.event) {
case "GUILD_INTEGRATIONS_UPDATE":
if (!permission.has("MANAGE_GUILD")) return;
break;
case "WEBHOOKS_UPDATE":
if (!permission.has("MANAGE_WEBHOOKS")) return;
break;
case "GUILD_MEMBER_ADD":
case "GUILD_MEMBER_REMOVE":
case "GUILD_MEMBER_UPDATE":
// only send them, if the user subscribed for this part of the member list, or is a bot
break;
case "GUILD_BAN_ADD":
case "GUILD_BAN_REMOVE":
if (!permission.has("BAN_MEMBERS")) break;
break;
case "INVITE_CREATE":
case "INVITE_DELETE":
if (!permission.has("MANAGE_GUILD")) break;
case "PRESENCE_UPDATE":
break;
case "VOICE_STATE_UPDATE":
case "MESSAGE_CREATE":
case "MESSAGE_DELETE":
case "MESSAGE_DELETE_BULK":
case "MESSAGE_UPDATE":
case "CHANNEL_PINS_UPDATE":
case "MESSAGE_REACTION_ADD":
case "MESSAGE_REACTION_REMOVE":
case "MESSAGE_REACTION_REMOVE_ALL":
case "MESSAGE_REACTION_REMOVE_EMOJI":
case "TYPING_START":
// only gets send if the user is alowed to view the current channel
if (!permission.has("VIEW_CHANNEL")) return;
break;
case "GUILD_CREATE":
case "GUILD_DELETE":
case "GUILD_UPDATE":
case "GUILD_ROLE_CREATE":
case "GUILD_ROLE_UPDATE":
case "GUILD_ROLE_DELETE":
case "CHANNEL_CREATE":
case "CHANNEL_DELETE":
case "CHANNEL_UPDATE":
case "GUILD_EMOJI_UPDATE":
case "READY": // will be sent by the gateway
case "USER_UPDATE":
case "APPLICATION_COMMAND_CREATE":
case "APPLICATION_COMMAND_DELETE":
case "APPLICATION_COMMAND_UPDATE":
default:
// always gets sent
// Any events not defined in an intent are considered "passthrough" and will always be sent
break;
}
return Send(this, {
op: OPCODES.Dispatch,
t: document.event,
d: document.data,
s: this.sequence++,
});
opts.acknowledge?.();
}

View File

@ -12,7 +12,7 @@ import {
toObject,
EVENTEnum,
Config,
} from "@fosscord/server-util";
} from "@fosscord/util";
import { setupListener } from "../listener/listener";
import { IdentifySchema } from "../schema/Identify";
import { Send } from "../util/Send";

View File

@ -1,13 +1,5 @@
// @ts-nocheck WIP
import {
db,
getPermission,
MemberModel,
MongooseCache,
PublicUserProjection,
RoleModel,
toObject,
} from "@fosscord/server-util";
import { db, getPermission, PublicUserProjection, toObject } from "@fosscord/util";
import { LazyRequest } from "../schema/LazyRequest";
import { OPCODES, Payload } from "../util/Constants";
import { Send } from "../util/Send";

View File

@ -1,4 +1,4 @@
import { ActivityBodySchema } from "@fosscord/server-util";
import { ActivityBodySchema } from "@fosscord/util";
import { EmojiSchema } from "./Emoji";
export const ActivitySchema = {

View File

@ -1,6 +1,6 @@
// @ts-nocheck
import { Config } from "@fosscord/server-util";
import { getConfigPathForFile } from "@fosscord/server-util/dist/util/Config";
import { Config } from "@fosscord/util";
import { getConfigPathForFile } from "@fosscord/util/dist/util/Config";
import Ajv, { JSONSchemaType } from "ajv";
export interface DefaultOptions {

View File

@ -1,4 +1,4 @@
import { Intents, Permissions } from "@fosscord/server-util";
import { Intents, Permissions } from "@fosscord/util";
import WS, { Server, Data } from "ws";
import { Deflate } from "zlib";
import { Channel } from "amqplib";
@ -15,8 +15,8 @@ interface WebSocket extends WS {
readyTimeout: NodeJS.Timeout;
intents: Intents;
sequence: number;
rabbitCh?: Channel & { queues: Record<string, string> };
permissions: Record<string, Permissions>;
events: Record<string, Function>;
}
export default WebSocket;

1268
rtc/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,48 +0,0 @@
{
"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"
}
}

View File

@ -1,10 +0,0 @@
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 };

View File

@ -1,132 +0,0 @@
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,
}

View File

@ -1,67 +0,0 @@
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[];
}

View File

@ -1,220 +0,0 @@
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<AuditLogEntries>("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,
}

View File

@ -1,32 +0,0 @@
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>("Ban", BanSchema, "bans");

View File

@ -1,109 +0,0 @@
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<ChannelDocument>("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
}

View File

@ -1,29 +0,0 @@
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>("Emoji", EmojiSchema, "emojis");

View File

@ -1,540 +0,0 @@
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<EventDocument>("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<Member, "settings" | "user">[][];
// 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<Invite, "guild" | "channel"> & {
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<Message, "author_id"> & {
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<Relationship, "nickname">;
}
// 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";

View File

@ -1,161 +0,0 @@
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<GuildDocument>("Guild", GuildSchema, "guilds");

Some files were not shown because too many files have changed in this diff Show More