diff --git a/api/src/Server.ts b/api/src/Server.ts
index 1f11a295..a6887fd4 100644
--- a/api/src/Server.ts
+++ b/api/src/Server.ts
@@ -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();
diff --git a/api/src/routes/channels/#channel_id/messages/index.ts b/api/src/routes/channels/#channel_id/messages/index.ts
index b5a2d334..3e26e930 100644
--- a/api/src/routes/channels/#channel_id/messages/index.ts
+++ b/api/src/routes/channels/#channel_id/messages/index.ts
@@ -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
diff --git a/api/src/routes/guilds/#guild_id/emojis.ts b/api/src/routes/guilds/#guild_id/emojis.ts
index ff565cd4..85d7ac05 100644
--- a/api/src/routes/guilds/#guild_id/emojis.ts
+++ b/api/src/routes/guilds/#guild_id/emojis.ts
@@ -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({
diff --git a/api/src/routes/guilds/#guild_id/premium.ts b/api/src/routes/guilds/#guild_id/premium.ts
new file mode 100644
index 00000000..75361ac6
--- /dev/null
+++ b/api/src/routes/guilds/#guild_id/premium.ts
@@ -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;
diff --git a/api/src/routes/guilds/#guild_id/stickers.ts b/api/src/routes/guilds/#guild_id/stickers.ts
new file mode 100644
index 00000000..39095dc2
--- /dev/null
+++ b/api/src/routes/guilds/#guild_id/stickers.ts
@@ -0,0 +1,109 @@
+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) => {
+ 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)
+ ]);
+
+ 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();
+ return res.json(sticker);
+ }
+);
+
+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 });
+
+ return res.sendStatus(204);
+});
+
+export default router;
diff --git a/api/src/routes/sticker-packs/#id/index.ts b/api/src/routes/sticker-packs/#id/index.ts
deleted file mode 100644
index 7f723e97..00000000
--- a/api/src/routes/sticker-packs/#id/index.ts
+++ /dev/null
@@ -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;
diff --git a/api/src/routes/sticker-packs/index.ts b/api/src/routes/sticker-packs/index.ts
index d671c161..e6560d12 100644
--- a/api/src/routes/sticker-packs/index.ts
+++ b/api/src/routes/sticker-packs/index.ts
@@ -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;
diff --git a/api/src/routes/stickers/#sticker_id/index.ts b/api/src/routes/stickers/#sticker_id/index.ts
new file mode 100644
index 00000000..293ca089
--- /dev/null
+++ b/api/src/routes/stickers/#sticker_id/index.ts
@@ -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;
diff --git a/api/src/routes/template.ts.disabled b/api/src/routes/template.ts.disabled
index ad785f10..fcc59ef4 100644
--- a/api/src/routes/template.ts.disabled
+++ b/api/src/routes/template.ts.disabled
@@ -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;
diff --git a/api/src/util/Message.ts b/api/src/util/Message.ts
index 40d96b42..d14d3aa2 100644
--- a/api/src/util/Message.ts
+++ b/api/src/util/Message.ts
@@ -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);
}
|