mirror of
https://github.com/spacebarchat/server.git
synced 2024-11-11 05:02:37 +01:00
commit
1bbf789a7e
@ -514,6 +514,12 @@
|
|||||||
"attachments": {
|
"attachments": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {}
|
"items": {}
|
||||||
|
},
|
||||||
|
"sticker_ids": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
@ -5341,6 +5347,308 @@
|
|||||||
},
|
},
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||||
},
|
},
|
||||||
|
"ModifyGuildStickerSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"minLength": 2,
|
||||||
|
"maxLength": 30,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"maxLength": 100,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"maxLength": 200,
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"tags"
|
||||||
|
],
|
||||||
|
"definitions": {
|
||||||
|
"ChannelPermissionOverwriteType": {
|
||||||
|
"enum": [
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"Embed": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"article",
|
||||||
|
"gifv",
|
||||||
|
"image",
|
||||||
|
"link",
|
||||||
|
"rich",
|
||||||
|
"video"
|
||||||
|
],
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"timestamp": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"text": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"icon_url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"proxy_icon_url": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"$ref": "#/definitions/EmbedImage"
|
||||||
|
},
|
||||||
|
"thumbnail": {
|
||||||
|
"$ref": "#/definitions/EmbedImage"
|
||||||
|
},
|
||||||
|
"video": {
|
||||||
|
"$ref": "#/definitions/EmbedImage"
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"icon_url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"proxy_icon_url": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"inline": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"value"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"EmbedImage": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"proxy_url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"height": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"width": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ChannelModifySchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"maxLength": 100,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6
|
||||||
|
],
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"icon": {
|
||||||
|
"type": [
|
||||||
|
"null",
|
||||||
|
"string"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"bitrate": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"user_limit": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"rate_limit_per_user": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"permission_overwrites": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/definitions/ChannelPermissionOverwriteType"
|
||||||
|
},
|
||||||
|
"allow": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"deny": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"allow",
|
||||||
|
"deny",
|
||||||
|
"id",
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parent_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"nsfw": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"rtc_region": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default_auto_archive_duration": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"UserPublic": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"username": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"discriminator": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"public_flags": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"avatar": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"accent_color": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"banner": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"bio": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"bot": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"bio",
|
||||||
|
"bot",
|
||||||
|
"discriminator",
|
||||||
|
"id",
|
||||||
|
"public_flags",
|
||||||
|
"username"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"PublicConnectedAccount": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"verifie": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"type",
|
||||||
|
"verifie"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||||
|
},
|
||||||
"TemplateCreateSchema": {
|
"TemplateCreateSchema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
HTML_TIMESTAMP: Date.now(),
|
HTML_TIMESTAMP: Date.now(),
|
||||||
ALGOLIA_KEY: "aca0d7082e4e63af5ba5917d5e96bed0"
|
ALGOLIA_KEY: "aca0d7082e4e63af5ba5917d5e96bed0"
|
||||||
};
|
};
|
||||||
|
GLOBAL_ENV.MEDIA_PROXY_ENDPOINT = location.protocol + "//" + GLOBAL_ENV.CDN_HOST;
|
||||||
const localStorage = window.localStorage;
|
const localStorage = window.localStorage;
|
||||||
// TODO: remote auth
|
// TODO: remote auth
|
||||||
// window.GLOBAL_ENV.REMOTE_AUTH_ENDPOINT = window.GLOBAL_ENV.GATEWAY_ENDPOINT.replace(/wss?:/, "");
|
// window.GLOBAL_ENV.REMOTE_AUTH_ENDPOINT = window.GLOBAL_ENV.GATEWAY_ENDPOINT.replace(/wss?:/, "");
|
||||||
@ -105,7 +106,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const settings = JSON.parse(localStorage.getItem("UserSettingsStore"));
|
const settings = JSON.parse(localStorage.getItem("UserSettingsStore"));
|
||||||
if (settings && settings.locale === "en") {
|
if (settings && settings.locale.length <= 2) {
|
||||||
|
// fix client locale wrong and client not loading at all
|
||||||
settings.locale = "en-US";
|
settings.locale = "en-US";
|
||||||
localStorage.setItem("UserSettingsStore", JSON.stringify(settings));
|
localStorage.setItem("UserSettingsStore", JSON.stringify(settings));
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ export class FosscordServer extends Server {
|
|||||||
api.use("*", (error: any, req: Request, res: Response, next: NextFunction) => {
|
api.use("*", (error: any, req: Request, res: Response, next: NextFunction) => {
|
||||||
if (error) return next(error);
|
if (error) return next(error);
|
||||||
res.status(404).json({
|
res.status(404).json({
|
||||||
message: "404: Not Found",
|
message: "404 endpoint not found",
|
||||||
code: 0
|
code: 0
|
||||||
});
|
});
|
||||||
next();
|
next();
|
||||||
|
@ -64,6 +64,7 @@ export interface MessageCreateSchema {
|
|||||||
payload_json?: string;
|
payload_json?: string;
|
||||||
file?: any;
|
file?: any;
|
||||||
attachments?: any[]; //TODO we should create an interface for attachments
|
attachments?: any[]; //TODO we should create an interface for attachments
|
||||||
|
sticker_ids?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://discord.com/developers/docs/resources/channel#create-message
|
// https://discord.com/developers/docs/resources/channel#create-message
|
||||||
|
@ -40,17 +40,14 @@ router.post("/", route({ body: "EmojiCreateSchema", permission: "MANAGE_EMOJIS_A
|
|||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
const body = req.body as EmojiCreateSchema;
|
const body = req.body as EmojiCreateSchema;
|
||||||
|
|
||||||
|
const id = Snowflake.generate();
|
||||||
const emoji_count = await Emoji.count({ guild_id: guild_id });
|
const emoji_count = await Emoji.count({ guild_id: guild_id });
|
||||||
const { maxEmojis } = Config.get().limits.guild;
|
const { maxEmojis } = Config.get().limits.guild;
|
||||||
|
|
||||||
if (emoji_count >= maxEmojis) throw DiscordApiErrors.MAXIMUM_NUMBER_OF_EMOJIS_REACHED.withParams(maxEmojis);
|
if (emoji_count >= maxEmojis) throw DiscordApiErrors.MAXIMUM_NUMBER_OF_EMOJIS_REACHED.withParams(maxEmojis);
|
||||||
|
|
||||||
const id = Snowflake.generate();
|
|
||||||
|
|
||||||
if (body.require_colons == null) body.require_colons = true;
|
if (body.require_colons == null) body.require_colons = true;
|
||||||
|
|
||||||
const user = await User.findOneOrFail({ id: req.user_id });
|
const user = await User.findOneOrFail({ id: req.user_id });
|
||||||
|
|
||||||
body.image = (await handleFile(`/emojis/${id}`, body.image)) as string;
|
body.image = (await handleFile(`/emojis/${id}`, body.image)) as string;
|
||||||
|
|
||||||
const emoji = await new Emoji({
|
const emoji = await new Emoji({
|
||||||
|
10
api/src/routes/guilds/#guild_id/premium.ts
Normal file
10
api/src/routes/guilds/#guild_id/premium.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { Router, Request, Response } from "express";
|
||||||
|
import { route } from "@fosscord/api";
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get("/subscriptions", route({}), async (req: Request, res: Response) => {
|
||||||
|
// TODO:
|
||||||
|
res.json([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
135
api/src/routes/guilds/#guild_id/stickers.ts
Normal file
135
api/src/routes/guilds/#guild_id/stickers.ts
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
import {
|
||||||
|
emitEvent,
|
||||||
|
GuildStickersUpdateEvent,
|
||||||
|
handleFile,
|
||||||
|
Member,
|
||||||
|
Snowflake,
|
||||||
|
Sticker,
|
||||||
|
StickerFormatType,
|
||||||
|
StickerType,
|
||||||
|
uploadFile
|
||||||
|
} from "@fosscord/util";
|
||||||
|
import { Router, Request, Response } from "express";
|
||||||
|
import { route } from "@fosscord/api";
|
||||||
|
import multer from "multer";
|
||||||
|
import { HTTPError } from "lambert-server";
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||||
|
const { guild_id } = req.params;
|
||||||
|
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||||
|
|
||||||
|
res.json(await Sticker.find({ guild_id }));
|
||||||
|
});
|
||||||
|
|
||||||
|
const bodyParser = multer({
|
||||||
|
limits: {
|
||||||
|
fileSize: 1024 * 1024 * 100,
|
||||||
|
fields: 10,
|
||||||
|
files: 1
|
||||||
|
},
|
||||||
|
storage: multer.memoryStorage()
|
||||||
|
}).single("file");
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/",
|
||||||
|
bodyParser,
|
||||||
|
route({ permission: "MANAGE_EMOJIS_AND_STICKERS", body: "ModifyGuildStickerSchema" }),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
if (!req.file) throw new HTTPError("missing file");
|
||||||
|
|
||||||
|
const { guild_id } = req.params;
|
||||||
|
const body = req.body as ModifyGuildStickerSchema;
|
||||||
|
const id = Snowflake.generate();
|
||||||
|
|
||||||
|
const [sticker] = await Promise.all([
|
||||||
|
new Sticker({
|
||||||
|
...body,
|
||||||
|
guild_id,
|
||||||
|
id,
|
||||||
|
type: StickerType.GUILD,
|
||||||
|
format_type: getStickerFormat(req.file.mimetype),
|
||||||
|
available: true
|
||||||
|
}).save(),
|
||||||
|
uploadFile(`/stickers/${id}`, req.file)
|
||||||
|
]);
|
||||||
|
|
||||||
|
await sendStickerUpdateEvent(guild_id);
|
||||||
|
|
||||||
|
res.json(sticker);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export function getStickerFormat(mime_type: string) {
|
||||||
|
switch (mime_type) {
|
||||||
|
case "image/apng":
|
||||||
|
return StickerFormatType.APNG;
|
||||||
|
case "application/json":
|
||||||
|
return StickerFormatType.LOTTIE;
|
||||||
|
case "image/png":
|
||||||
|
return StickerFormatType.PNG;
|
||||||
|
case "image/gif":
|
||||||
|
return StickerFormatType.GIF;
|
||||||
|
default:
|
||||||
|
throw new HTTPError("invalid sticker format: must be png, apng or lottie");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
router.get("/:sticker_id", route({}), async (req: Request, res: Response) => {
|
||||||
|
const { guild_id, sticker_id } = req.params;
|
||||||
|
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||||
|
|
||||||
|
res.json(await Sticker.findOneOrFail({ guild_id, id: sticker_id }));
|
||||||
|
});
|
||||||
|
|
||||||
|
export interface ModifyGuildStickerSchema {
|
||||||
|
/**
|
||||||
|
* @minLength 2
|
||||||
|
* @maxLength 30
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* @maxLength 100
|
||||||
|
*/
|
||||||
|
description?: string;
|
||||||
|
/**
|
||||||
|
* @maxLength 200
|
||||||
|
*/
|
||||||
|
tags: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/:sticker_id",
|
||||||
|
route({ body: "ModifyGuildStickerSchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
const { guild_id, sticker_id } = req.params;
|
||||||
|
const body = req.body as ModifyGuildStickerSchema;
|
||||||
|
|
||||||
|
const sticker = await new Sticker({ ...body, guild_id, id: sticker_id }).save();
|
||||||
|
await sendStickerUpdateEvent(guild_id);
|
||||||
|
|
||||||
|
return res.json(sticker);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
async function sendStickerUpdateEvent(guild_id: string) {
|
||||||
|
return emitEvent({
|
||||||
|
event: "GUILD_STICKERS_UPDATE",
|
||||||
|
guild_id: guild_id,
|
||||||
|
data: {
|
||||||
|
guild_id: guild_id,
|
||||||
|
stickers: await Sticker.find({ guild_id: guild_id })
|
||||||
|
}
|
||||||
|
} as GuildStickersUpdateEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
router.delete("/:sticker_id", route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => {
|
||||||
|
const { guild_id, sticker_id } = req.params;
|
||||||
|
|
||||||
|
await Sticker.delete({ guild_id, id: sticker_id });
|
||||||
|
await sendStickerUpdateEvent(guild_id);
|
||||||
|
|
||||||
|
return res.sendStatus(204);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
@ -1,19 +0,0 @@
|
|||||||
import { Request, Response, Router } from "express";
|
|
||||||
import { route } from "@fosscord/api";
|
|
||||||
|
|
||||||
const router: Router = Router();
|
|
||||||
|
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
|
||||||
//TODO
|
|
||||||
res.json({
|
|
||||||
id: "",
|
|
||||||
stickers: [],
|
|
||||||
name: "",
|
|
||||||
sku_id: "",
|
|
||||||
cover_sticker_id: "",
|
|
||||||
description: "",
|
|
||||||
banner_asset_id: ""
|
|
||||||
}).status(200);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
|
@ -1,11 +1,13 @@
|
|||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
import { route } from "@fosscord/api";
|
import { route } from "@fosscord/api";
|
||||||
|
import { StickerPack } from "@fosscord/util";
|
||||||
|
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
|
||||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||||
//TODO
|
const sticker_packs = await StickerPack.find({ relations: ["stickers"] });
|
||||||
res.json({ sticker_packs: [] }).status(200);
|
|
||||||
|
res.json({ sticker_packs });
|
||||||
});
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
12
api/src/routes/stickers/#sticker_id/index.ts
Normal file
12
api/src/routes/stickers/#sticker_id/index.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { Sticker } from "@fosscord/util";
|
||||||
|
import { Router, Request, Response } from "express";
|
||||||
|
import { route } from "@fosscord/api";
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||||
|
const { sticker_id } = req.params;
|
||||||
|
|
||||||
|
res.json(await Sticker.find({ id: sticker_id }));
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
@ -1,10 +1,11 @@
|
|||||||
//TODO: this is a template for a generic route
|
//TODO: this is a template for a generic route
|
||||||
|
|
||||||
import { Router, Request, Response } from "express";
|
import { Router, Request, Response } from "express";
|
||||||
|
import { route } from "@fosscord/api";
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get("/", async (req: Request, res: Response) => {
|
router.get("/",route({}), async (req: Request, res: Response) => {
|
||||||
res.send({});
|
res.json({});
|
||||||
});
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -24,7 +24,7 @@ import fetch from "node-fetch";
|
|||||||
import cheerio from "cheerio";
|
import cheerio from "cheerio";
|
||||||
import { MessageCreateSchema } from "../routes/channels/#channel_id/messages";
|
import { MessageCreateSchema } from "../routes/channels/#channel_id/messages";
|
||||||
|
|
||||||
// TODO: check webhook, application, system author
|
// TODO: check webhook, application, system author, stickers
|
||||||
// TODO: embed gifs/videos/images
|
// TODO: embed gifs/videos/images
|
||||||
|
|
||||||
const LINK_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
|
const LINK_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
|
||||||
@ -46,6 +46,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
|||||||
|
|
||||||
const message = new Message({
|
const message = new Message({
|
||||||
...opts,
|
...opts,
|
||||||
|
sticker_items: opts.sticker_ids?.map((x) => ({ id: x })),
|
||||||
guild_id: channel.guild_id,
|
guild_id: channel.guild_id,
|
||||||
channel_id: opts.channel_id,
|
channel_id: opts.channel_id,
|
||||||
attachments: opts.attachments || [],
|
attachments: opts.attachments || [],
|
||||||
@ -82,7 +83,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: stickers/activity
|
// TODO: stickers/activity
|
||||||
if (!opts.content && !opts.embeds?.length && !opts.attachments?.length) {
|
if (!opts.content && !opts.embeds?.length && !opts.attachments?.length && !opts.sticker_ids?.length) {
|
||||||
throw new HTTPError("Empty messages are not allowed", 50006);
|
throw new HTTPError("Empty messages are not allowed", 50006);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,8 +67,7 @@
|
|||||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
|
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@fosscord/api": ["src/index"],
|
"@fosscord/api": ["src/index"]
|
||||||
"@fosscord/api/*": ["src/*"]
|
|
||||||
},
|
},
|
||||||
"plugins": [{ "transform": "@zerollup/ts-transform-paths" }],
|
"plugins": [{ "transform": "@zerollup/ts-transform-paths" }],
|
||||||
"experimentalDecorators": true
|
"experimentalDecorators": true
|
||||||
|
@ -3,7 +3,7 @@ const WebSocket = require("ws");
|
|||||||
const Constants = require("./dist/util/Constants");
|
const Constants = require("./dist/util/Constants");
|
||||||
|
|
||||||
// const ws = new WebSocket("ws://127.0.0.1:8080");
|
// const ws = new WebSocket("ws://127.0.0.1:8080");
|
||||||
const ws = new WebSocket("wss://gateway.discord.gg");
|
const ws = new WebSocket("wss://dev.fosscord.com");
|
||||||
|
|
||||||
ws.on("open", () => {
|
ws.on("open", () => {
|
||||||
// ws.send(JSON.stringify({ req_type: "new_auth" }));
|
// ws.send(JSON.stringify({ req_type: "new_auth" }));
|
||||||
|
@ -179,6 +179,8 @@ export async function onIdentify(this: WebSocket, data: Payload) {
|
|||||||
x.guild_hashes = {}; // @ts-ignore
|
x.guild_hashes = {}; // @ts-ignore
|
||||||
x.guild_scheduled_events = []; // @ts-ignore
|
x.guild_scheduled_events = []; // @ts-ignore
|
||||||
x.threads = [];
|
x.threads = [];
|
||||||
|
x.premium_subscription_count = 30;
|
||||||
|
x.premium_tier = 3;
|
||||||
return x;
|
return x;
|
||||||
}),
|
}),
|
||||||
guild_experiments: [], // TODO
|
guild_experiments: [], // TODO
|
||||||
|
@ -127,7 +127,7 @@ export class Message extends BaseClass {
|
|||||||
mention_channels: Channel[];
|
mention_channels: Channel[];
|
||||||
|
|
||||||
@JoinTable({ name: "message_stickers" })
|
@JoinTable({ name: "message_stickers" })
|
||||||
@ManyToMany(() => Sticker)
|
@ManyToMany(() => Sticker, { cascade: true, onDelete: "CASCADE" })
|
||||||
sticker_items?: Sticker[];
|
sticker_items?: Sticker[];
|
||||||
|
|
||||||
@OneToMany(() => Attachment, (attachment: Attachment) => attachment.message, {
|
@OneToMany(() => Attachment, (attachment: Attachment) => attachment.message, {
|
||||||
|
18
util/src/entities/Migration.ts
Normal file
18
util/src/entities/Migration.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { Column, Entity, ObjectIdColumn, PrimaryGeneratedColumn } from "typeorm";
|
||||||
|
import { BaseClassWithoutId } from ".";
|
||||||
|
|
||||||
|
export const PrimaryIdAutoGenerated = process.env.DATABASE?.startsWith("mongodb")
|
||||||
|
? ObjectIdColumn
|
||||||
|
: PrimaryGeneratedColumn;
|
||||||
|
|
||||||
|
@Entity("migrations")
|
||||||
|
export class Migration extends BaseClassWithoutId {
|
||||||
|
@PrimaryIdAutoGenerated()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
timestamp: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
name: string;
|
||||||
|
}
|
@ -32,13 +32,8 @@ export class ReadState extends BaseClass {
|
|||||||
user: User;
|
user: User;
|
||||||
|
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
@RelationId((read_state: ReadState) => read_state.last_message)
|
|
||||||
last_message_id: string;
|
last_message_id: string;
|
||||||
|
|
||||||
@JoinColumn({ name: "last_message_id" })
|
|
||||||
@ManyToOne(() => Message, { nullable: true })
|
|
||||||
last_message?: Message;
|
|
||||||
|
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
last_pin_timestamp?: Date;
|
last_pin_timestamp?: Date;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Column, Entity, JoinColumn, ManyToOne } from "typeorm";
|
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
|
||||||
|
import { User } from "./User";
|
||||||
import { BaseClass } from "./BaseClass";
|
import { BaseClass } from "./BaseClass";
|
||||||
import { Guild } from "./Guild";
|
import { Guild } from "./Guild";
|
||||||
|
|
||||||
@ -8,6 +9,7 @@ export enum StickerType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum StickerFormatType {
|
export enum StickerFormatType {
|
||||||
|
GIF = 0, // gif is a custom format type and not in discord spec
|
||||||
PNG = 1,
|
PNG = 1,
|
||||||
APNG = 2,
|
APNG = 2,
|
||||||
LOTTIE = 3,
|
LOTTIE = 3,
|
||||||
@ -21,11 +23,22 @@ export class Sticker extends BaseClass {
|
|||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
description?: string;
|
description?: string;
|
||||||
|
|
||||||
@Column()
|
@Column({ nullable: true })
|
||||||
tags: string;
|
available?: boolean;
|
||||||
|
|
||||||
@Column()
|
@Column({ nullable: true })
|
||||||
pack_id: string;
|
tags?: string;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
@RelationId((sticker: Sticker) => sticker.pack)
|
||||||
|
pack_id?: string;
|
||||||
|
|
||||||
|
@JoinColumn({ name: "pack_id" })
|
||||||
|
@ManyToOne(() => require("./StickerPack").StickerPack, {
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
pack: import("./StickerPack").StickerPack;
|
||||||
|
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
guild_id?: string;
|
guild_id?: string;
|
||||||
@ -36,6 +49,15 @@ export class Sticker extends BaseClass {
|
|||||||
})
|
})
|
||||||
guild?: Guild;
|
guild?: Guild;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
user_id?: string;
|
||||||
|
|
||||||
|
@JoinColumn({ name: "user_id" })
|
||||||
|
@ManyToOne(() => User, {
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
})
|
||||||
|
user?: User;
|
||||||
|
|
||||||
@Column({ type: "int" })
|
@Column({ type: "int" })
|
||||||
type: StickerType;
|
type: StickerType;
|
||||||
|
|
||||||
|
31
util/src/entities/StickerPack.ts
Normal file
31
util/src/entities/StickerPack.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { Column, Entity, JoinColumn, ManyToOne, OneToMany, OneToOne, RelationId } from "typeorm";
|
||||||
|
import { Sticker } from ".";
|
||||||
|
import { BaseClass } from "./BaseClass";
|
||||||
|
|
||||||
|
@Entity("sticker_packs")
|
||||||
|
export class StickerPack extends BaseClass {
|
||||||
|
@Column()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
banner_asset_id?: string;
|
||||||
|
|
||||||
|
@OneToMany(() => Sticker, (sticker: Sticker) => sticker.pack, {
|
||||||
|
cascade: true,
|
||||||
|
orphanedRowAction: "delete",
|
||||||
|
})
|
||||||
|
stickers: Sticker[];
|
||||||
|
|
||||||
|
// sku_id: string
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
@RelationId((pack: StickerPack) => pack.cover_sticker)
|
||||||
|
cover_sticker_id?: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => Sticker, { nullable: true })
|
||||||
|
@JoinColumn()
|
||||||
|
cover_sticker?: Sticker;
|
||||||
|
}
|
@ -11,6 +11,7 @@ export * from "./Guild";
|
|||||||
export * from "./Invite";
|
export * from "./Invite";
|
||||||
export * from "./Member";
|
export * from "./Member";
|
||||||
export * from "./Message";
|
export * from "./Message";
|
||||||
|
export * from "./Migration";
|
||||||
export * from "./RateLimit";
|
export * from "./RateLimit";
|
||||||
export * from "./ReadState";
|
export * from "./ReadState";
|
||||||
export * from "./Recipient";
|
export * from "./Recipient";
|
||||||
@ -18,6 +19,7 @@ export * from "./Relationship";
|
|||||||
export * from "./Role";
|
export * from "./Role";
|
||||||
export * from "./Session";
|
export * from "./Session";
|
||||||
export * from "./Sticker";
|
export * from "./Sticker";
|
||||||
|
export * from "./StickerPack";
|
||||||
export * from "./Team";
|
export * from "./Team";
|
||||||
export * from "./TeamMember";
|
export * from "./TeamMember";
|
||||||
export * from "./Template";
|
export * from "./Template";
|
||||||
|
@ -12,6 +12,7 @@ import { Interaction } from "./Interaction";
|
|||||||
import { ConnectedAccount } from "../entities/ConnectedAccount";
|
import { ConnectedAccount } from "../entities/ConnectedAccount";
|
||||||
import { Relationship, RelationshipType } from "../entities/Relationship";
|
import { Relationship, RelationshipType } from "../entities/Relationship";
|
||||||
import { Presence } from "./Presence";
|
import { Presence } from "./Presence";
|
||||||
|
import { Sticker } from "..";
|
||||||
|
|
||||||
export interface Event {
|
export interface Event {
|
||||||
guild_id?: string;
|
guild_id?: string;
|
||||||
@ -193,6 +194,14 @@ export interface GuildEmojisUpdateEvent extends Event {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GuildStickersUpdateEvent extends Event {
|
||||||
|
event: "GUILD_STICKERS_UPDATE";
|
||||||
|
data: {
|
||||||
|
guild_id: string;
|
||||||
|
stickers: Sticker[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface GuildIntegrationUpdateEvent extends Event {
|
export interface GuildIntegrationUpdateEvent extends Event {
|
||||||
event: "GUILD_INTEGRATIONS_UPDATE";
|
event: "GUILD_INTEGRATIONS_UPDATE";
|
||||||
data: {
|
data: {
|
||||||
@ -553,6 +562,7 @@ export type EVENT =
|
|||||||
| "GUILD_BAN_ADD"
|
| "GUILD_BAN_ADD"
|
||||||
| "GUILD_BAN_REMOVE"
|
| "GUILD_BAN_REMOVE"
|
||||||
| "GUILD_EMOJIS_UPDATE"
|
| "GUILD_EMOJIS_UPDATE"
|
||||||
|
| "GUILD_STICKERS_UPDATE"
|
||||||
| "GUILD_INTEGRATIONS_UPDATE"
|
| "GUILD_INTEGRATIONS_UPDATE"
|
||||||
| "GUILD_MEMBER_ADD"
|
| "GUILD_MEMBER_ADD"
|
||||||
| "GUILD_MEMBER_REMOVE"
|
| "GUILD_MEMBER_REMOVE"
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
export class VanityInvite1633881705509 implements MigrationInterface {
|
export class VanityInvite1633881705509 implements MigrationInterface {
|
||||||
|
name = "VanityInvite1633881705509";
|
||||||
|
|
||||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await queryRunner.query(`ALTER TABLE "emojis" DROP COLUMN vanity_url_code`);
|
await queryRunner.query(`ALTER TABLE "emojis" DROP COLUMN vanity_url_code`);
|
||||||
|
66
util/src/migrations/1634308884591-Stickers.ts
Normal file
66
util/src/migrations/1634308884591-Stickers.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { MigrationInterface, QueryRunner, Table, TableColumn, TableForeignKey } from "typeorm";
|
||||||
|
|
||||||
|
export class Stickers1634308884591 implements MigrationInterface {
|
||||||
|
name = "Stickers1634308884591";
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.dropForeignKey("read_states", "FK_6f255d873cfbfd7a93849b7ff74");
|
||||||
|
await queryRunner.changeColumn(
|
||||||
|
"stickers",
|
||||||
|
"tags",
|
||||||
|
new TableColumn({ name: "tags", type: "varchar", isNullable: true })
|
||||||
|
);
|
||||||
|
await queryRunner.changeColumn(
|
||||||
|
"stickers",
|
||||||
|
"pack_id",
|
||||||
|
new TableColumn({ name: "pack_id", type: "varchar", isNullable: true })
|
||||||
|
);
|
||||||
|
await queryRunner.changeColumn("stickers", "type", new TableColumn({ name: "type", type: "integer" }));
|
||||||
|
await queryRunner.changeColumn(
|
||||||
|
"stickers",
|
||||||
|
"format_type",
|
||||||
|
new TableColumn({ name: "format_type", type: "integer" })
|
||||||
|
);
|
||||||
|
await queryRunner.changeColumn(
|
||||||
|
"stickers",
|
||||||
|
"available",
|
||||||
|
new TableColumn({ name: "available", type: "boolean", isNullable: true })
|
||||||
|
);
|
||||||
|
await queryRunner.changeColumn(
|
||||||
|
"stickers",
|
||||||
|
"user_id",
|
||||||
|
new TableColumn({ name: "user_id", type: "boolean", isNullable: true })
|
||||||
|
);
|
||||||
|
await queryRunner.createForeignKey(
|
||||||
|
"stickers",
|
||||||
|
new TableForeignKey({
|
||||||
|
name: "FK_8f4ee73f2bb2325ff980502e158",
|
||||||
|
columnNames: ["user_id"],
|
||||||
|
referencedColumnNames: ["id"],
|
||||||
|
referencedTableName: "users",
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: "sticker_packs",
|
||||||
|
columns: [
|
||||||
|
new TableColumn({ name: "id", type: "varchar", isPrimary: true }),
|
||||||
|
new TableColumn({ name: "name", type: "varchar" }),
|
||||||
|
new TableColumn({ name: "description", type: "varchar", isNullable: true }),
|
||||||
|
new TableColumn({ name: "banner_asset_id", type: "varchar", isNullable: true }),
|
||||||
|
new TableColumn({ name: "cover_sticker_id", type: "varchar", isNullable: true }),
|
||||||
|
],
|
||||||
|
foreignKeys: [
|
||||||
|
new TableForeignKey({
|
||||||
|
columnNames: ["cover_sticker_id"],
|
||||||
|
referencedColumnNames: ["id"],
|
||||||
|
referencedTableName: "stickers",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {}
|
||||||
|
}
|
@ -2,6 +2,7 @@ import path from "path";
|
|||||||
import "reflect-metadata";
|
import "reflect-metadata";
|
||||||
import { Connection, createConnection } from "typeorm";
|
import { Connection, createConnection } from "typeorm";
|
||||||
import * as Models from "../entities";
|
import * as Models from "../entities";
|
||||||
|
import { Migration } from "../entities/Migration";
|
||||||
import { yellow, green } from "nanocolors";
|
import { yellow, green } from "nanocolors";
|
||||||
|
|
||||||
// UUID extension option is only supported with postgres
|
// UUID extension option is only supported with postgres
|
||||||
@ -33,10 +34,27 @@ export function initDatabase(): Promise<Connection> {
|
|||||||
bigNumberStrings: false,
|
bigNumberStrings: false,
|
||||||
supportBigNumbers: true,
|
supportBigNumbers: true,
|
||||||
name: "default",
|
name: "default",
|
||||||
|
migrations: [path.join(__dirname, "..", "migrations", "*.js")],
|
||||||
});
|
});
|
||||||
|
|
||||||
promise.then((connection) => {
|
promise.then(async (connection: Connection) => {
|
||||||
dbConnection = connection;
|
dbConnection = connection;
|
||||||
|
|
||||||
|
// run migrations, and if it is a new fresh database, set it to the last migration
|
||||||
|
if (connection.migrations.length) {
|
||||||
|
if (!(await Migration.findOne({}))) {
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
await Migration.insert(
|
||||||
|
connection.migrations.map((x) => ({
|
||||||
|
id: i++,
|
||||||
|
name: x.name,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await connection.runMigrations();
|
||||||
console.log(`[Database] ${green("connected")}`);
|
console.log(`[Database] ${green("connected")}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4,7 +4,9 @@ import fetch from "node-fetch";
|
|||||||
import { Config } from "./Config";
|
import { Config } from "./Config";
|
||||||
import multer from "multer";
|
import multer from "multer";
|
||||||
|
|
||||||
export async function uploadFile(path: string, file: Express.Multer.File) {
|
export async function uploadFile(path: string, file?: Express.Multer.File) {
|
||||||
|
if (!file?.buffer) throw new HTTPError("Missing file in body");
|
||||||
|
|
||||||
const form = new FormData();
|
const form = new FormData();
|
||||||
form.append("file", file.buffer, {
|
form.append("file", file.buffer, {
|
||||||
contentType: file.mimetype,
|
contentType: file.mimetype,
|
||||||
|
Loading…
Reference in New Issue
Block a user