diff --git a/api/assets/schemas.json b/api/assets/schemas.json
index 2ceaa923..eb97112c 100644
--- a/api/assets/schemas.json
+++ b/api/assets/schemas.json
@@ -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": {
diff --git a/api/src/routes/guilds/#guild_id/stickers.ts b/api/src/routes/guilds/#guild_id/stickers.ts
index c122b72d..39095dc2 100644
--- a/api/src/routes/guilds/#guild_id/stickers.ts
+++ b/api/src/routes/guilds/#guild_id/stickers.ts
@@ -1,7 +1,8 @@
-import { Member, Sticker } from "@fosscord/util";
+import { 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) => {
@@ -20,12 +21,47 @@ const bodyParser = multer({
storage: multer.memoryStorage()
}).single("file");
-router.post("/", bodyParser, route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }), async (req: Request, res: Response) => {
- const { guild_id } = req.params;
- await Member.IsInGuildOrFail(req.user_id, guild_id);
+router.post(
+ "/",
+ bodyParser,
+ route({ permission: "MANAGE_EMOJIS_AND_STICKERS", body: "ModifyGuildStickerSchema" }),
+ async (req: Request, res: Response) => {
+ if (!req.file) throw new HTTPError("missing file");
- res.json(await Sticker.find({ guild_id }));
-});
+ 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)
+ ]);
+
+ 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;
@@ -41,7 +77,6 @@ export interface ModifyGuildStickerSchema {
*/
name: string;
/**
- * @minLength 2
* @maxLength 100
*/
description?: string;
diff --git a/util/src/entities/Sticker.ts b/util/src/entities/Sticker.ts
index 94761f60..37bc6fbe 100644
--- a/util/src/entities/Sticker.ts
+++ b/util/src/entities/Sticker.ts
@@ -9,6 +9,7 @@ export enum StickerType {
}
export enum StickerFormatType {
+ GIF = 0, // gif is a custom format type and not in discord spec
PNG = 1,
APNG = 2,
LOTTIE = 3,
@@ -22,16 +23,20 @@ export class Sticker extends BaseClass {
@Column({ nullable: true })
description?: string;
- @Column()
- tags: string;
+ @Column({ nullable: true })
+ available?: boolean;
- @Column()
+ @Column({ nullable: true })
+ tags?: string;
+
+ @Column({ nullable: true })
@RelationId((sticker: Sticker) => sticker.pack)
- pack_id: string;
+ pack_id?: string;
@JoinColumn({ name: "pack_id" })
@ManyToOne(() => require("./StickerPack").StickerPack, {
onDelete: "CASCADE",
+ nullable: true,
})
pack: import("./StickerPack").StickerPack;
|