summary refs log tree commit diff
path: root/src/routes/channels
diff options
context:
space:
mode:
authorFlam3rboy <34555296+Flam3rboy@users.noreply.github.com>2021-03-30 15:37:43 +0200
committerFlam3rboy <34555296+Flam3rboy@users.noreply.github.com>2021-03-30 15:37:43 +0200
commit0d0dd4f6b8433a3a90b5b0eebab7e3991bda2302 (patch)
tree890dce7954f4f673f5ac6c7f3e77bbce62f92022 /src/routes/channels
parentadded GET [#12] (diff)
downloadserver-0d0dd4f6b8433a3a90b5b0eebab7e3991bda2302.tar.xz
move routes to top level no more /api/v8 -> use reverse proxy instead
Diffstat (limited to 'src/routes/channels')
-rw-r--r--src/routes/channels/#channel_id/followers.ts4
-rw-r--r--src/routes/channels/#channel_id/index.ts4
-rw-r--r--src/routes/channels/#channel_id/invites.ts67
-rw-r--r--src/routes/channels/#channel_id/messages/bulk-delete.ts37
-rw-r--r--src/routes/channels/#channel_id/messages/index.ts136
-rw-r--r--src/routes/channels/#channel_id/permissions.ts4
-rw-r--r--src/routes/channels/#channel_id/pins.ts4
-rw-r--r--src/routes/channels/#channel_id/recipients.ts4
-rw-r--r--src/routes/channels/#channel_id/typing.ts4
-rw-r--r--src/routes/channels/#channel_id/webhooks.ts4
10 files changed, 268 insertions, 0 deletions
diff --git a/src/routes/channels/#channel_id/followers.ts b/src/routes/channels/#channel_id/followers.ts
new file mode 100644

index 00000000..9a4e81fa --- /dev/null +++ b/src/routes/channels/#channel_id/followers.ts
@@ -0,0 +1,4 @@ +import { Router } from "express"; +const router: Router = Router(); + +export default router; diff --git a/src/routes/channels/#channel_id/index.ts b/src/routes/channels/#channel_id/index.ts new file mode 100644
index 00000000..9a4e81fa --- /dev/null +++ b/src/routes/channels/#channel_id/index.ts
@@ -0,0 +1,4 @@ +import { Router } from "express"; +const router: Router = Router(); + +export default router; diff --git a/src/routes/channels/#channel_id/invites.ts b/src/routes/channels/#channel_id/invites.ts new file mode 100644
index 00000000..4c21e7d4 --- /dev/null +++ b/src/routes/channels/#channel_id/invites.ts
@@ -0,0 +1,67 @@ +import { Router, Request, Response } from "express"; +import { HTTPError } from "lambert-server"; + +import { check } from "../../../../../util/instanceOf"; +import { random } from "../../../../../util/RandomInviteID"; +import { emitEvent } from "../../../../../util/Event"; + +import { InviteCreateSchema } from "../../../../../schema/Invite"; + +import { getPermission, ChannelModel, InviteModel, InviteCreateEvent } from "fosscord-server-util"; + +const router: Router = Router(); + +router.post("/", check(InviteCreateSchema), async (req: Request, res: Response) => { + const usID = req.user_id; + const chID = BigInt(req.params.channel_id); + const channel = await ChannelModel.findOne({ id: chID }).exec(); + + if (!channel || !channel.guild_id) { + throw new HTTPError("This channel doesn't exist", 404); + } + const { guild_id: guID } = channel; + + const permission = await getPermission(usID, guID); + + if (!permission.has("CREATE_INSTANT_INVITE")) { + throw new HTTPError("You aren't authorised to access this endpoint", 401); + } + + const invite = { + code: random(), + temporary: req.body.temporary, + uses: 0, + max_uses: req.body.max_uses, + max_age: req.body.max_age, + created_at: new Date(), + guild_id: guID, + channel_id: chID, + inviter_id: usID, + }; + + await new InviteModel(invite).save(); + + await emitEvent({ event: "INVITE_CREATE", data: invite } as InviteCreateEvent); + res.status(201).send(invite); +}); + +router.get("/", async (req: Request, res: Response) => { + const usID = req.user_id; + const chID = BigInt(req.params.channel_id); + const channel = await ChannelModel.findOne({ id: chID }).exec(); + + if (!channel || !channel.guild_id) { + throw new HTTPError("This channel doesn't exist", 404); + } + const { guild_id: guID } = channel; + const permission = await getPermission(usID, guID); + + if (!permission.has("MANAGE_CHANNELS")) { + throw new HTTPError("You aren't authorised to access this endpoint", 401); + } + + const invites = await InviteModel.find({ guild_id: guID }).exec(); + res.status(200).send(invites); +}); + +export default router; diff --git a/src/routes/channels/#channel_id/messages/bulk-delete.ts b/src/routes/channels/#channel_id/messages/bulk-delete.ts new file mode 100644
index 00000000..c805cf08 --- /dev/null +++ b/src/routes/channels/#channel_id/messages/bulk-delete.ts
@@ -0,0 +1,37 @@ +import { Router } from "express"; +import { ChannelModel, getPermission, MessageDeleteBulkEvent, MessageModel } from "fosscord-server-util"; +import { HTTPError } from "lambert-server"; +import Config from "../../../../../../util/Config"; +import { emitEvent } from "../../../../../../util/Event"; +import { check } from "../../../../../../util/instanceOf"; + +const router: Router = Router(); + +export default router; + +// TODO: should users be able to bulk delete messages or only bots? +// TODO: should this request fail, if you provide messages older than 14 days/invalid ids? +// https://discord.com/developers/docs/resources/channel#bulk-delete-messages +router.post("/", check({ messages: [BigInt] }), async (req, res) => { + const channel_id = BigInt(req.params.channel_id); + const channel = await ChannelModel.findOne({ id: channel_id }, { permission_overwrites: true, guild_id: true }).exec(); + if (!channel?.guild_id) throw new HTTPError("Can't bulk delete dm channel messages", 400); + + const permission = await getPermission(req.user_id, channel?.guild_id, channel_id, { channel }); + if (!permission.has("MANAGE_MESSAGES")) throw new HTTPError("You are missing the MANAGE_MESSAGES permissions"); + + const { maxBulkDelete } = Config.get().limits.message; + + const { messages } = req.body as { messages: bigint[] }; + if (messages.length < 2) throw new HTTPError("You must at least specify 2 messages to bulk delete"); + if (messages.length > maxBulkDelete) throw new HTTPError(`You cannot delete more than ${maxBulkDelete} messages`); + + await MessageModel.deleteMany({ id: { $in: messages } }).exec(); + await emitEvent({ + event: "MESSAGE_DELETE_BULK", + channel_id, + data: { ids: messages, channel_id, guild_id: channel.guild_id }, + } as MessageDeleteBulkEvent); + + res.status(204).send(); +}); diff --git a/src/routes/channels/#channel_id/messages/index.ts b/src/routes/channels/#channel_id/messages/index.ts new file mode 100644
index 00000000..ade048a0 --- /dev/null +++ b/src/routes/channels/#channel_id/messages/index.ts
@@ -0,0 +1,136 @@ +import { Router } from "express"; +import { ChannelModel, ChannelType, getPermission, Message, MessageCreateEvent, MessageModel, Snowflake } from "fosscord-server-util"; +import { HTTPError } from "lambert-server"; +import { MessageCreateSchema } from "../../../../../../schema/Message"; +import { check, instanceOf, Length } from "../../../../../../util/instanceOf"; +import { PublicUserProjection } from "../../../../../../util/User"; +import multer from "multer"; +import { emitEvent } from "../../../../../../util/Event"; +const router: Router = Router(); + +export default router; + +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, res) => { + const channel_id = BigInt(req.params.channel_id); + const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true, type: true, permission_overwrites: true }).exec(); + if (!channel) throw new HTTPError("Channel not found", 404); + + isTextChannel(channel.type); + + try { + instanceOf({ $around: BigInt, $after: BigInt, $before: BigInt, $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?: bigint; after?: bigint; before?: bigint; limit?: number } = req.query; + if (!limit) limit = 50; + var halfLimit = BigInt(Math.floor(limit / 2)); + + if ([ChannelType.GUILD_VOICE, ChannelType.GUILD_CATEGORY, ChannelType.GUILD_STORE].includes(channel.type)) + throw new HTTPError("Not a text channel"); + + if (channel.guild_id) { + const permissions = await getPermission(req.user_id, channel.guild_id, channel_id, { channel }); + if (!permissions.has("VIEW_CHANNEL")) throw new HTTPError("You don't have permission to view this channel", 401); + if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]); + } else if (channel.recipients) { + // group/dm channel + if (!channel.recipients.includes(req.user_id)) throw new HTTPError("You don't have permission to view this channel", 401); + } + + var query: any; + 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: around - halfLimit, $lt: around + halfLimit } }); + else { + query = MessageModel.find({ channel_id }).sort({ id: -1 }); + } + + const messages = await query + .limit(limit) + .populate({ path: "author", select: PublicUserProjection }) + .populate({ path: "mentions", select: PublicUserProjection }) + .populate({ path: "mention_channels", select: { id: true, guild_id: true, type: true, name: true } }) + .populate("mention_roles") + // .populate({ path: "member", select: PublicMemberProjection }) + .exec(); + + return res.json(messages); +}); + +// TODO: config max upload size +const messageUpload = multer({ limits: { fieldSize: 1024 * 1024 * 1024 * 50 } }); // 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 +// Send message +router.post("/", check(MessageCreateSchema), async (req, res) => { + const channel_id = BigInt(req.params.channel_id); + const body = req.body as MessageCreateSchema; + + const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true, type: true, permission_overwrites: true }).exec(); + if (!channel) throw new HTTPError("Channel not found", 404); + + if (channel.guild_id) { + const permissions = await getPermission(req.user_id, channel.guild_id, channel_id, { channel }); + if (!permissions.has("SEND_MESSAGES")) throw new HTTPError("You don't have the SEND_MESSAGES permission"); + if (body.tts && !permissions.has("SEND_TTS_MESSAGES")) throw new HTTPError("You are missing the SEND_TTS_MESSAGES permission"); + if (body.message_reference) { + if (!permissions.has("READ_MESSAGE_HISTORY")) + throw new HTTPError("You are missing the READ_MESSAGE_HISTORY permission to reply"); + if (body.message_reference.guild_id !== channel.guild_id) + throw new HTTPError("You can only reference messages from this guild"); + } + } + + if (body.message_reference) { + if (body.message_reference.channel_id !== channel_id) throw new HTTPError("You can only reference messages from this channel"); + // TODO: should it be checked if the message exists? + } + + const embeds = []; + if (body.embed) embeds.push(body.embed); + + const message: Message = { + id: Snowflake.generate(), + channel_id, + guild_id: channel.guild_id, + author_id: req.user_id, + content: req.body, + timestamp: new Date(), + mention_channels_ids: [], + mention_role_ids: [], + mention_user_ids: [], + attachments: [], + embeds: [], + reactions: [], + type: 0, + }; + + await new MessageModel(message).save(); + + await emitEvent({ event: "MESSAGE_CREATE", channel_id, data: {} } as MessageCreateEvent); +}); diff --git a/src/routes/channels/#channel_id/permissions.ts b/src/routes/channels/#channel_id/permissions.ts new file mode 100644
index 00000000..9a4e81fa --- /dev/null +++ b/src/routes/channels/#channel_id/permissions.ts
@@ -0,0 +1,4 @@ +import { Router } from "express"; +const router: Router = Router(); + +export default router; diff --git a/src/routes/channels/#channel_id/pins.ts b/src/routes/channels/#channel_id/pins.ts new file mode 100644
index 00000000..9a4e81fa --- /dev/null +++ b/src/routes/channels/#channel_id/pins.ts
@@ -0,0 +1,4 @@ +import { Router } from "express"; +const router: Router = Router(); + +export default router; diff --git a/src/routes/channels/#channel_id/recipients.ts b/src/routes/channels/#channel_id/recipients.ts new file mode 100644
index 00000000..9a4e81fa --- /dev/null +++ b/src/routes/channels/#channel_id/recipients.ts
@@ -0,0 +1,4 @@ +import { Router } from "express"; +const router: Router = Router(); + +export default router; diff --git a/src/routes/channels/#channel_id/typing.ts b/src/routes/channels/#channel_id/typing.ts new file mode 100644
index 00000000..9a4e81fa --- /dev/null +++ b/src/routes/channels/#channel_id/typing.ts
@@ -0,0 +1,4 @@ +import { Router } from "express"; +const router: Router = Router(); + +export default router; diff --git a/src/routes/channels/#channel_id/webhooks.ts b/src/routes/channels/#channel_id/webhooks.ts new file mode 100644
index 00000000..9a4e81fa --- /dev/null +++ b/src/routes/channels/#channel_id/webhooks.ts
@@ -0,0 +1,4 @@ +import { Router } from "express"; +const router: Router = Router(); + +export default router;