mirror of
https://github.com/spacebarchat/server.git
synced 2026-05-29 18:14:14 +00:00
@@ -514,6 +514,12 @@
|
||||
"attachments": {
|
||||
"type": "array",
|
||||
"items": {}
|
||||
},
|
||||
"sticker_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
@@ -5341,6 +5347,308 @@
|
||||
},
|
||||
"$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": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
HTML_TIMESTAMP: Date.now(),
|
||||
ALGOLIA_KEY: "aca0d7082e4e63af5ba5917d5e96bed0"
|
||||
};
|
||||
GLOBAL_ENV.MEDIA_PROXY_ENDPOINT = location.protocol + "//" + GLOBAL_ENV.CDN_HOST;
|
||||
const localStorage = window.localStorage;
|
||||
// TODO: remote auth
|
||||
// window.GLOBAL_ENV.REMOTE_AUTH_ENDPOINT = window.GLOBAL_ENV.GATEWAY_ENDPOINT.replace(/wss?:/, "");
|
||||
@@ -105,7 +106,8 @@
|
||||
}
|
||||
|
||||
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";
|
||||
localStorage.setItem("UserSettingsStore", JSON.stringify(settings));
|
||||
}
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ export class FosscordServer extends Server {
|
||||
api.use("*", (error: any, req: Request, res: Response, next: NextFunction) => {
|
||||
if (error) return next(error);
|
||||
res.status(404).json({
|
||||
message: "404: Not Found",
|
||||
message: "404 endpoint not found",
|
||||
code: 0
|
||||
});
|
||||
next();
|
||||
|
||||
@@ -64,6 +64,7 @@ export interface MessageCreateSchema {
|
||||
payload_json?: string;
|
||||
file?: any;
|
||||
attachments?: any[]; //TODO we should create an interface for attachments
|
||||
sticker_ids?: string[];
|
||||
}
|
||||
|
||||
// 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 body = req.body as EmojiCreateSchema;
|
||||
|
||||
const id = Snowflake.generate();
|
||||
const emoji_count = await Emoji.count({ guild_id: guild_id });
|
||||
const { maxEmojis } = Config.get().limits.guild;
|
||||
|
||||
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;
|
||||
|
||||
const user = await User.findOneOrFail({ id: req.user_id });
|
||||
|
||||
body.image = (await handleFile(`/emojis/${id}`, body.image)) as string;
|
||||
|
||||
const emoji = await new Emoji({
|
||||
|
||||
@@ -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;
|
||||
@@ -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 { route } from "@fosscord/api";
|
||||
import { StickerPack } from "@fosscord/util";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
//TODO
|
||||
res.json({ sticker_packs: [] }).status(200);
|
||||
const sticker_packs = await StickerPack.find({ relations: ["stickers"] });
|
||||
|
||||
res.json({ sticker_packs });
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -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
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@fosscord/api";
|
||||
const router = Router();
|
||||
|
||||
router.get("/", async (req: Request, res: Response) => {
|
||||
res.send({});
|
||||
router.get("/",route({}), async (req: Request, res: Response) => {
|
||||
res.json({});
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -24,7 +24,7 @@ import fetch from "node-fetch";
|
||||
import cheerio from "cheerio";
|
||||
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
|
||||
|
||||
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({
|
||||
...opts,
|
||||
sticker_items: opts.sticker_ids?.map((x) => ({ id: x })),
|
||||
guild_id: channel.guild_id,
|
||||
channel_id: opts.channel_id,
|
||||
attachments: opts.attachments || [],
|
||||
@@ -82,7 +83,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -67,8 +67,7 @@
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@fosscord/api": ["src/index"],
|
||||
"@fosscord/api/*": ["src/*"]
|
||||
"@fosscord/api": ["src/index"]
|
||||
},
|
||||
"plugins": [{ "transform": "@zerollup/ts-transform-paths" }],
|
||||
"experimentalDecorators": true
|
||||
|
||||
Reference in New Issue
Block a user