summary refs log tree commit diff
path: root/src/routes/channels
diff options
context:
space:
mode:
authorDiego Magdaleno <diegomagdaleno@protonmail.com>2021-05-22 15:55:45 -0500
committerDiego Magdaleno <diegomagdaleno@protonmail.com>2021-05-22 15:55:45 -0500
commite4a4759e971387675d127990b53dfd8b3a443d82 (patch)
tree8f4e8101fafc01492ace38a5fa51e32f07b85e1d /src/routes/channels
parentConfig: Refactor config to be be in separate classes (diff)
downloadserver-e4a4759e971387675d127990b53dfd8b3a443d82.tar.xz
Fix merge issues, update to reflect config changes and package.json
Diffstat (limited to 'src/routes/channels')
-rw-r--r--src/routes/channels/#channel_id/invites.ts5
-rw-r--r--src/routes/channels/#channel_id/messages/#message_id/ack.ts29
-rw-r--r--src/routes/channels/#channel_id/messages/#message_id/index.ts50
-rw-r--r--src/routes/channels/#channel_id/messages/#message_id/reactions.ts202
-rw-r--r--src/routes/channels/#channel_id/messages/bulk-delete.ts4
-rw-r--r--src/routes/channels/#channel_id/messages/index.ts64
-rw-r--r--src/routes/channels/#channel_id/pins.ts57
7 files changed, 353 insertions, 58 deletions
diff --git a/src/routes/channels/#channel_id/invites.ts b/src/routes/channels/#channel_id/invites.ts

index 10d6ae3f..457e78ca 100644 --- a/src/routes/channels/#channel_id/invites.ts +++ b/src/routes/channels/#channel_id/invites.ts
@@ -24,16 +24,19 @@ router.post("/", check(InviteCreateSchema), async (req: Request, res: Response) const permission = await getPermission(user_id, guild_id); permission.hasThrow("CREATE_INSTANT_INVITE"); + const expires_at = new Date(req.body.max_age * 1000 + Date.now()); + const invite = { code: random(), temporary: req.body.temporary, uses: 0, max_uses: req.body.max_uses, max_age: req.body.max_age, + expires_at, created_at: new Date(), guild_id, channel_id: channel_id, - inviter_id: user_id, + inviter_id: user_id }; await new InviteModel(invite).save(); diff --git a/src/routes/channels/#channel_id/messages/#message_id/ack.ts b/src/routes/channels/#channel_id/messages/#message_id/ack.ts new file mode 100644
index 00000000..9782365f --- /dev/null +++ b/src/routes/channels/#channel_id/messages/#message_id/ack.ts
@@ -0,0 +1,29 @@ +import { getPermission } from "@fosscord/server-util"; +import { MessageModel } from "@fosscord/server-util"; +import { Event } from "@fosscord/server-util"; +import { ChannelModel } from "@fosscord/server-util"; +import { Request, Response, Router } from "express"; +import { HTTPError } from "lambert-server"; +import { emitEvent } from "../../../../../util/Event"; + +const router = Router(); + +// router.pot("/", async (req: Request, res: Response) => { +// const { channel_id, message_id } = req.params; + +// const permission = await getPermission(req.user_id, channel?.guild_id, channel_id, { channel }); +// permission.hasThrow("MANAGE_MESSAGES"); + +// await emitEvent({ +// event: "MESSAGE_ACK", +// channel_id, +// data: { +// channel_id, +// message_id +// } +// } as MessageAckEvent); + +// res.sendStatus(204); +// }); + +export default router; diff --git a/src/routes/channels/#channel_id/messages/#message_id/index.ts b/src/routes/channels/#channel_id/messages/#message_id/index.ts
index 19efc823..5a61b4ad 100644 --- a/src/routes/channels/#channel_id/messages/#message_id/index.ts +++ b/src/routes/channels/#channel_id/messages/#message_id/index.ts
@@ -1,20 +1,58 @@ -import { ChannelModel, getPermission, MessageDeleteEvent, MessageModel } from "@fosscord/server-util"; +import { ChannelModel, getPermission, MessageDeleteEvent, MessageModel, MessageUpdateEvent, toObject } from "@fosscord/server-util"; import { Router } from "express"; import { HTTPError } from "lambert-server"; +import { MessageCreateSchema } from "../../../../../schema/Message"; import { emitEvent } from "../../../../../util/Event"; import { check } from "../../../../../util/instanceOf"; +import { handleMessage } from "../../../../../util/Message"; const router = Router(); -// TODO: + +router.patch("/", check(MessageCreateSchema), async (req, res) => { + const { message_id, channel_id } = req.params; + var body = req.body as MessageCreateSchema; + + var message = await MessageModel.findOne({ id: message_id, channel_id }, { author_id: true }).exec(); + if (!message) throw new HTTPError("Message not found", 404); + + const permissions = await getPermission(req.user_id, undefined, channel_id); + + if (req.user_id !== message.author_id) { + permissions.hasThrow("MANAGE_MESSAGES"); + body = { flags: body.flags }; + } + + const opts = await handleMessage({ + ...body, + author_id: message.author_id, + channel_id, + id: message_id, + edited_timestamp: new Date() + }); + + message = await MessageModel.findOneAndUpdate({ id: message_id }, opts).populate("author").exec(); + if (!message) throw new HTTPError("Message not found", 404); + + await emitEvent({ + event: "MESSAGE_UPDATE", + channel_id, + guild_id: message.guild_id, + data: { ...toObject(message), nonce: undefined } + } as MessageUpdateEvent); + + return res.json(toObject(message)); +}); router.delete("/", async (req, res) => { const { message_id, channel_id } = req.params; const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true }); - if (!channel) throw new HTTPError("Channel doesn't exist", 404); + if (!channel) throw new HTTPError("Channel not found", 404); + const message = await MessageModel.findOne({ id: message_id }, { author_id: true }).exec(); + if (!message) throw new HTTPError("Message not found", 404); const permission = await getPermission(req.user_id, channel.guild_id, channel_id); - permission.hasThrow("MANAGE_MESSAGES"); + if (message.author_id !== req.user_id) permission.hasThrow("MANAGE_MESSAGES"); await MessageModel.deleteOne({ id: message_id }).exec(); @@ -25,8 +63,8 @@ router.delete("/", async (req, res) => { data: { id: message_id, channel_id, - guild_id: channel.guild_id, - }, + guild_id: channel.guild_id + } } as MessageDeleteEvent); res.sendStatus(204); diff --git a/src/routes/channels/#channel_id/messages/#message_id/reactions.ts b/src/routes/channels/#channel_id/messages/#message_id/reactions.ts
index 014daee7..1bfaae39 100644 --- a/src/routes/channels/#channel_id/messages/#message_id/reactions.ts +++ b/src/routes/channels/#channel_id/messages/#message_id/reactions.ts
@@ -1,6 +1,206 @@ +import { + ChannelModel, + EmojiModel, + getPermission, + MemberModel, + MessageModel, + MessageReactionAddEvent, + MessageReactionRemoveAllEvent, + MessageReactionRemoveEmojiEvent, + MessageReactionRemoveEvent, + PartialEmoji, + PublicUserProjection, + toObject, + UserModel +} from "@fosscord/server-util"; import { Router } from "express"; +import { HTTPError } from "lambert-server"; +import { emitEvent } from "../../../../../util/Event"; const router = Router(); -// TODO: +// TODO: check if emoji is really an unicode emoji or a prperly encoded external emoji + +function getEmoji(emoji: string): PartialEmoji { + emoji = decodeURIComponent(emoji); + const parts = emoji.includes(":") && emoji.split(":"); + if (parts) + return { + name: parts[0], + id: parts[1] + }; + + return { + id: undefined, + name: emoji + }; +} + +router.delete("/", async (req, res) => { + const { message_id, channel_id } = req.params; + + const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true }).exec(); + if (!channel) throw new HTTPError("Channel not found", 404); + + const permissions = await getPermission(req.user_id, undefined, channel_id); + permissions.hasThrow("MANAGE_MESSAGES"); + + const message = await MessageModel.findOneAndUpdate({ id: message_id, channel_id }, { reactions: [] }).exec(); + if (!message) throw new HTTPError("Message not found", 404); + + await emitEvent({ + event: "MESSAGE_REACTION_REMOVE_ALL", + channel_id, + guild_id: channel.guild_id, + data: { + channel_id, + message_id, + guild_id: channel.guild_id + } + } as MessageReactionRemoveAllEvent); + + res.sendStatus(204); +}); + +router.delete("/:emoji", async (req, res) => { + const { message_id, channel_id } = req.params; + const emoji = getEmoji(req.params.emoji); + + const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true }).exec(); + if (!channel) throw new HTTPError("Channel not found", 404); + + const permissions = await getPermission(req.user_id, undefined, channel_id); + permissions.hasThrow("MANAGE_MESSAGES"); + + const message = await MessageModel.findOne({ id: message_id, channel_id }).exec(); + if (!message) throw new HTTPError("Message not found", 404); + + const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name); + if (!already_added) throw new HTTPError("Reaction not found", 404); + message.reactions.remove(already_added); + + await MessageModel.updateOne({ id: message_id, channel_id }, message).exec(); + + await emitEvent({ + event: "MESSAGE_REACTION_REMOVE_EMOJI", + channel_id, + guild_id: channel.guild_id, + data: { + channel_id, + message_id, + guild_id: channel.guild_id, + emoji + } + } as MessageReactionRemoveEmojiEvent); + + res.sendStatus(204); +}); + +router.get("/:emoji", async (req, res) => { + const { message_id, channel_id } = req.params; + const emoji = getEmoji(req.params.emoji); + + const message = await MessageModel.findOne({ id: message_id, channel_id }).exec(); + if (!message) throw new HTTPError("Message not found", 404); + const reaction = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name); + if (!reaction) throw new HTTPError("Reaction not found", 404); + + const permissions = await getPermission(req.user_id, undefined, channel_id); + permissions.hasThrow("VIEW_CHANNEL"); + + const users = await UserModel.find({ id: { $in: reaction.user_ids } }, PublicUserProjection).exec(); + + res.json(toObject(users)); +}); + +router.put("/:emoji/:user_id", async (req, res) => { + const { message_id, channel_id, user_id } = req.params; + if (user_id !== "@me") throw new HTTPError("Invalid user"); + const emoji = getEmoji(req.params.emoji); + + const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true }).exec(); + if (!channel) throw new HTTPError("Channel not found", 404); + + const message = await MessageModel.findOne({ id: message_id, channel_id }).exec(); + if (!message) throw new HTTPError("Message not found", 404); + const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name); + + const permissions = await getPermission(req.user_id, undefined, channel_id); + permissions.hasThrow("READ_MESSAGE_HISTORY"); + if (!already_added) permissions.hasThrow("ADD_REACTIONS"); + + if (emoji.id) { + const external_emoji = await EmojiModel.findOne({ id: emoji.id }).exec(); + if (!external_emoji) throw new HTTPError("Emoji not found", 404); + if (!already_added) permissions.hasThrow("USE_EXTERNAL_EMOJIS"); + emoji.animated = external_emoji.animated; + emoji.name = external_emoji.name; + } + + if (already_added) { + if (already_added.user_ids.includes(req.user_id)) return res.sendStatus(204); // Do not throw an error ¯\_(ツ)_/¯ as discord also doesn't throw any error + already_added.count++; + } else message.reactions.push({ count: 1, emoji, user_ids: [req.user_id] }); + + await MessageModel.updateOne({ id: message_id, channel_id }, message).exec(); + + const member = channel.guild_id && (await MemberModel.findOne({ id: req.user_id }).exec()); + + await emitEvent({ + event: "MESSAGE_REACTION_ADD", + channel_id, + guild_id: channel.guild_id, + data: { + user_id: req.user_id, + channel_id, + message_id, + guild_id: channel.guild_id, + emoji, + member + } + } as MessageReactionAddEvent); + + res.sendStatus(204); +}); + +router.delete("/:emoji/:user_id", async (req, res) => { + var { message_id, channel_id, user_id } = req.params; + + const emoji = getEmoji(req.params.emoji); + + const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true }).exec(); + if (!channel) throw new HTTPError("Channel not found", 404); + + const message = await MessageModel.findOne({ id: message_id, channel_id }).exec(); + if (!message) throw new HTTPError("Message not found", 404); + + const permissions = await getPermission(req.user_id, undefined, channel_id); + + if (user_id === "@me") user_id = req.user_id; + else permissions.hasThrow("MANAGE_MESSAGES"); + + const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name); + if (!already_added || !already_added.user_ids.includes(user_id)) throw new HTTPError("Reaction not found", 404); + + already_added.count--; + + if (already_added.count <= 0) message.reactions.remove(already_added); + + await MessageModel.updateOne({ id: message_id, channel_id }, message).exec(); + + await emitEvent({ + event: "MESSAGE_REACTION_REMOVE", + channel_id, + guild_id: channel.guild_id, + data: { + user_id: req.user_id, + channel_id, + message_id, + guild_id: channel.guild_id, + emoji + } + } as MessageReactionRemoveEvent); + + res.sendStatus(204); +}); 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
index c70e7ac1..8a11475e 100644 --- a/src/routes/channels/#channel_id/messages/bulk-delete.ts +++ b/src/routes/channels/#channel_id/messages/bulk-delete.ts
@@ -13,7 +13,7 @@ export default router; // 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: [String] }), async (req, res) => { - const channel_id = req.params.channel_id; + const { channel_id } = req.params; 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); @@ -31,7 +31,7 @@ router.post("/", check({ messages: [String] }), async (req, res) => { await emitEvent({ event: "MESSAGE_DELETE_BULK", channel_id, - data: { ids: messages, channel_id, guild_id: channel.guild_id }, + data: { ids: messages, channel_id, guild_id: channel.guild_id } } as MessageDeleteBulkEvent); res.sendStatus(204); diff --git a/src/routes/channels/#channel_id/messages/index.ts b/src/routes/channels/#channel_id/messages/index.ts
index b186343e..7fdff809 100644 --- a/src/routes/channels/#channel_id/messages/index.ts +++ b/src/routes/channels/#channel_id/messages/index.ts
@@ -8,7 +8,7 @@ import { MessageDocument, MessageModel, Snowflake, - toObject, + toObject } from "@fosscord/server-util"; import { HTTPError } from "lambert-server"; import { MessageCreateSchema } from "../../../../schema/Message"; @@ -18,6 +18,7 @@ import multer from "multer"; import { emitEvent } from "../../../../util/Event"; import { Query } from "mongoose"; import { PublicMemberProjection } from "../../../../util/Member"; +import { sendMessage } from "../../../../util/Message"; const router: Router = Router(); export default router; @@ -48,7 +49,7 @@ router.get("/", async (req, res) => { try { instanceOf({ $around: String, $after: String, $before: String, $limit: new Length(Number, 1, 100) }, req.query, { path: "query", - req, + req }); } catch (error) { return res.status(400).json({ code: 50035, message: "Invalid Query", success: false, errors: error }); @@ -67,7 +68,7 @@ router.get("/", async (req, res) => { else if (around) query = MessageModel.find({ channel_id, - id: { $gt: (BigInt(around) - BigInt(halfLimit)).toString(), $lt: (BigInt(around) + BigInt(halfLimit)).toString() }, + id: { $gt: (BigInt(around) - BigInt(halfLimit)).toString(), $lt: (BigInt(around) + BigInt(halfLimit)).toString() } }); else { query = MessageModel.find({ channel_id }).sort({ id: -1 }); @@ -75,7 +76,18 @@ router.get("/", async (req, res) => { const messages = await query.limit(limit).exec(); - return res.json(toObject(messages)); + 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; + }); + + return x; + }) + ); }); // TODO: config max upload size @@ -89,52 +101,12 @@ const messageUpload = multer({ limits: { fieldSize: 1024 * 1024 * 1024 * 50 } }) // TODO: trim and replace message content and every embed field // Send message router.post("/", check(MessageCreateSchema), async (req, res) => { - const channel_id = req.params.channel_id; + const { channel_id } = req.params; 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); - // TODO: are tts messages allowed in dm channels? should permission be checked? - - const permissions = await getPermission(req.user_id, channel.guild_id, channel_id, { channel }); - permissions.hasThrow("SEND_MESSAGES"); - if (body.tts) permissions.hasThrow("SEND_TTS_MESSAGES"); - if (body.message_reference) { - permissions.hasThrow("READ_MESSAGE_HISTORY"); - 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 be checked if the referenced message exists? - } - const embeds = []; if (body.embed) embeds.push(body.embed); - - // TODO: check and put it all in the body - const message: Message = { - id: Snowflake.generate(), - channel_id, - guild_id: channel.guild_id, - author_id: req.user_id, - content: body.content, - timestamp: new Date(), - mention_channels_ids: [], - mention_role_ids: [], - mention_user_ids: [], - attachments: [], - embeds: [], - reactions: [], - type: 0, - tts: body.tts, - nonce: body.nonce, - pinned: false, - }; - - const data = toObject(await new MessageModel(message).populate({ path: "member", select: PublicMemberProjection }).save()); - - await emitEvent({ event: "MESSAGE_CREATE", channel_id, data, guild_id: channel.guild_id } as MessageCreateEvent); + const data = await sendMessage({ ...body, type: 0, pinned: false, author_id: req.user_id, embeds, channel_id }); return res.send(data); }); diff --git a/src/routes/channels/#channel_id/pins.ts b/src/routes/channels/#channel_id/pins.ts
index 4d8f53b1..ccb909b8 100644 --- a/src/routes/channels/#channel_id/pins.ts +++ b/src/routes/channels/#channel_id/pins.ts
@@ -1,10 +1,10 @@ -import { ChannelModel, getPermission, MessageModel, toObject } from "@fosscord/server-util"; +import { ChannelModel, ChannelPinsUpdateEvent, getPermission, MessageModel, MessageUpdateEvent, toObject } from "@fosscord/server-util"; import { Router, Request, Response } from "express"; import * as Config from "../../../util/Config"; import { HTTPError } from "lambert-server"; +import { emitEvent } from "../../../util/Event"; const router: Router = Router(); - // TODO: auto throw error if findOne doesn't find anything router.put("/:message_id", async (req: Request, res: Response) => { @@ -22,6 +22,59 @@ router.put("/:message_id", async (req: Request, res: Response) => { if (pinned_count >= maxPins) throw new HTTPError("Max pin count reached: " + maxPins); await MessageModel.updateOne({ id: message_id }, { pinned: true }).exec(); + const message = toObject(await MessageModel.findOne({ id: message_id }).exec()); + if (!message) throw new HTTPError("Message not found", 404); + + await emitEvent({ + event: "MESSAGE_UPDATE", + channel_id, + guild_id: channel.guild_id, + data: message + } as MessageUpdateEvent); + + await emitEvent({ + event: "CHANNEL_PINS_UPDATE", + channel_id, + guild_id: channel.guild_id, + data: { + channel_id, + guild_id: channel.guild_id, + last_pin_timestamp: undefined + } + } as ChannelPinsUpdateEvent); + + res.sendStatus(204); +}); + +router.delete("/:message_id", async (req, res) => { + const { channel_id, message_id } = req.params; + + const channel = await ChannelModel.findOne({ id: channel_id }).exec(); + if (!channel) throw new HTTPError("Channel not found", 404); + + const permission = await getPermission(req.user_id, channel.guild_id, channel_id); + permission.hasThrow("VIEW_CHANNEL"); + if (channel.guild_id) permission.hasThrow("MANAGE_MESSAGES"); + + const message = toObject(await MessageModel.findOneAndUpdate({ id: message_id }, { pinned: false }).exec()); + + await emitEvent({ + event: "MESSAGE_UPDATE", + channel_id, + guild_id: channel.guild_id, + data: message + } as MessageUpdateEvent); + + await emitEvent({ + event: "CHANNEL_PINS_UPDATE", + channel_id, + guild_id: channel.guild_id, + data: { + channel_id, + guild_id: channel.guild_id, + last_pin_timestamp: undefined + } + } as ChannelPinsUpdateEvent); res.sendStatus(204); });