summary refs log tree commit diff
path: root/api/src/routes/channels/#channel_id/messages/index.ts
diff options
context:
space:
mode:
Diffstat (limited to 'api/src/routes/channels/#channel_id/messages/index.ts')
-rw-r--r--api/src/routes/channels/#channel_id/messages/index.ts146
1 files changed, 146 insertions, 0 deletions
diff --git a/api/src/routes/channels/#channel_id/messages/index.ts b/api/src/routes/channels/#channel_id/messages/index.ts
new file mode 100644

index 00000000..fea4d6a4 --- /dev/null +++ b/api/src/routes/channels/#channel_id/messages/index.ts
@@ -0,0 +1,146 @@ +import { Router, Response, Request } from "express"; +import { Attachment, ChannelModel, ChannelType, getPermission, MessageDocument, MessageModel, toObject } from "@fosscord/server-util"; +import { HTTPError } from "lambert-server"; +import { MessageCreateSchema } from "../../../../schema/Message"; +import { check, instanceOf, Length } from "../../../../util/instanceOf"; +import multer from "multer"; +import { Query } from "mongoose"; +import { sendMessage } from "../../../../util/Message"; +import { uploadFile } from "../../../../util/cdn"; + +const router: Router = Router(); + +export default router; + +export function isTextChannel(type: ChannelType): boolean { + switch (type) { + case ChannelType.GUILD_VOICE: + case ChannelType.GUILD_CATEGORY: + throw new HTTPError("not a text channel", 400); + case ChannelType.DM: + case ChannelType.GROUP_DM: + case ChannelType.GUILD_NEWS: + case ChannelType.GUILD_STORE: + case ChannelType.GUILD_TEXT: + return true; + } +} + +// https://discord.com/developers/docs/resources/channel#create-message +// get messages +router.get("/", async (req: Request, res: Response) => { + const channel_id = req.params.channel_id; + const channel = await ChannelModel.findOne( + { id: channel_id }, + { guild_id: true, type: true, permission_overwrites: true, recipient_ids: true, owner_id: true } + ) + .lean() // lean is needed, because we don't want to populate .recipients that also auto deletes .recipient_ids + .exec(); + if (!channel) throw new HTTPError("Channel not found", 404); + + isTextChannel(channel.type); + + try { + instanceOf({ $around: String, $after: String, $before: String, $limit: new Length(Number, 1, 100) }, req.query, { + path: "query", + req + }); + } catch (error) { + return res.status(400).json({ code: 50035, message: "Invalid Query", success: false, errors: error }); + } + var { around, after, before, limit }: { around?: string; after?: string; before?: string; limit?: number } = req.query; + if (!limit) limit = 50; + var halfLimit = Math.floor(limit / 2); + + // @ts-ignore + const permissions = await getPermission(req.user_id, channel.guild_id, channel_id, { channel }); + permissions.hasThrow("VIEW_CHANNEL"); + if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]); + + var query: Query<MessageDocument[], MessageDocument>; + if (after) query = MessageModel.find({ channel_id, id: { $gt: after } }); + else if (before) query = MessageModel.find({ channel_id, id: { $lt: before } }); + else if (around) + query = MessageModel.find({ + channel_id, + id: { $gt: (BigInt(around) - BigInt(halfLimit)).toString(), $lt: (BigInt(around) + BigInt(halfLimit)).toString() } + }); + else { + query = MessageModel.find({ channel_id }); + } + + query = query.sort({ id: -1 }); + + const messages = await query.limit(limit).exec(); + + return res.json( + toObject(messages).map((x) => { + (x.reactions || []).forEach((x) => { + // @ts-ignore + if ((x.user_ids || []).includes(req.user_id)) x.me = true; + // @ts-ignore + delete x.user_ids; + }); + // @ts-ignore + if (!x.author) x.author = { discriminator: "0000", username: "Deleted User", public_flags: 0n, avatar: null }; + + return x; + }) + ); +}); + +// TODO: config max upload size +const messageUpload = multer({ + limits: { + fileSize: 1024 * 1024 * 100, + fields: 10, + files: 1 + }, + storage: multer.memoryStorage() +}); // max upload 50 mb + +// TODO: dynamically change limit of MessageCreateSchema with config +// TODO: check: sum of all characters in an embed structure must not exceed 6000 characters + +// https://discord.com/developers/docs/resources/channel#create-message +// TODO: text channel slowdown +// TODO: trim and replace message content and every embed field +// TODO: check allowed_mentions + +// Send message +router.post("/", messageUpload.single("file"), async (req: Request, res: Response) => { + const { channel_id } = req.params; + var body = req.body as MessageCreateSchema; + const attachments: Attachment[] = []; + + if (req.file) { + try { + const file = await uploadFile(`/attachments/${channel_id}`, req.file); + attachments.push({ ...file, proxy_url: file.url }); + } catch (error) { + return res.status(400).json(error); + } + } + + if (body.payload_json) { + body = JSON.parse(body.payload_json); + } + + const errors = instanceOf(MessageCreateSchema, body, { req }); + if (errors !== true) throw errors; + + const embeds = []; + if (body.embed) embeds.push(body.embed); + const data = await sendMessage({ + ...body, + type: 0, + pinned: false, + author_id: req.user_id, + embeds, + channel_id, + attachments, + edited_timestamp: null + }); + + return res.send(data); +});