diff options
Diffstat (limited to 'src/api/routes/channels/#channel_id/messages/index.ts')
-rw-r--r-- | src/api/routes/channels/#channel_id/messages/index.ts | 277 |
1 files changed, 148 insertions, 129 deletions
diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts index 7f0c9fb5..a15b462d 100644 --- a/src/api/routes/channels/#channel_id/messages/index.ts +++ b/src/api/routes/channels/#channel_id/messages/index.ts @@ -16,165 +16,172 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { Router, Response, Request } from "express"; +import { handleMessage, postHandleMessage, route } from "@spacebar/api"; import { Attachment, Channel, - ChannelType, Config, DmChannelDTO, - emitEvent, FieldErrors, - getPermission, + Member, Message, MessageCreateEvent, - Snowflake, - uploadFile, - Member, MessageCreateSchema, + Reaction, ReadState, Rights, - Reaction, + Snowflake, User, + emitEvent, + getPermission, + isTextChannel, + uploadFile, } from "@spacebar/util"; +import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; -import { handleMessage, postHandleMessage, route } from "@spacebar/api"; import multer from "multer"; import { FindManyOptions, FindOperator, LessThan, MoreThan } from "typeorm"; import { URL } from "url"; const router: Router = Router(); -export default router; - -export function isTextChannel(type: ChannelType): boolean { - switch (type) { - case ChannelType.GUILD_STORE: - case ChannelType.GUILD_VOICE: - case ChannelType.GUILD_STAGE_VOICE: - case ChannelType.GUILD_CATEGORY: - case ChannelType.GUILD_FORUM: - case ChannelType.DIRECTORY: - throw new HTTPError("not a text channel", 400); - case ChannelType.DM: - case ChannelType.GROUP_DM: - case ChannelType.GUILD_NEWS: - case ChannelType.GUILD_NEWS_THREAD: - case ChannelType.GUILD_PUBLIC_THREAD: - case ChannelType.GUILD_PRIVATE_THREAD: - case ChannelType.GUILD_TEXT: - case ChannelType.ENCRYPTED: - case ChannelType.ENCRYPTED_THREAD: - return true; - default: - throw new HTTPError("unimplemented", 400); - } -} - // https://discord.com/developers/docs/resources/channel#create-message // get messages -router.get("/", route({}), async (req: Request, res: Response) => { - const channel_id = req.params.channel_id; - const channel = await Channel.findOneOrFail({ - where: { id: channel_id }, - }); - if (!channel) throw new HTTPError("Channel not found", 404); - - isTextChannel(channel.type); - const around = req.query.around ? `${req.query.around}` : undefined; - const before = req.query.before ? `${req.query.before}` : undefined; - const after = req.query.after ? `${req.query.after}` : undefined; - const limit = Number(req.query.limit) || 50; - if (limit < 1 || limit > 100) - throw new HTTPError("limit must be between 1 and 100", 422); - - const halfLimit = Math.floor(limit / 2); - - const permissions = await getPermission( - req.user_id, - channel.guild_id, - channel_id, - ); - permissions.hasThrow("VIEW_CHANNEL"); - if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]); - - const query: FindManyOptions<Message> & { - where: { id?: FindOperator<string> | FindOperator<string>[] }; - } = { - order: { timestamp: "DESC" }, - take: limit, - where: { channel_id }, - relations: [ - "author", - "webhook", - "application", - "mentions", - "mention_roles", - "mention_channels", - "sticker_items", - "attachments", - ], - }; - - if (after) { - if (BigInt(after) > BigInt(Snowflake.generate())) - return res.status(422); - query.where.id = MoreThan(after); - } else if (before) { - if (BigInt(before) < BigInt(req.params.channel_id)) - return res.status(422); - query.where.id = LessThan(before); - } else if (around) { - query.where.id = [ - MoreThan((BigInt(around) - BigInt(halfLimit)).toString()), - LessThan((BigInt(around) + BigInt(halfLimit)).toString()), - ]; - - return res.json([]); // TODO: fix around - } +router.get( + "/", + route({ + query: { + around: { + type: "string", + }, + before: { + type: "string", + }, + after: { + type: "string", + }, + limit: { + type: "number", + description: + "max number of messages to return (1-100). defaults to 50", + }, + }, + responses: { + 200: { + body: "APIMessageArray", + }, + 400: { + body: "APIErrorResponse", + }, + 403: {}, + 404: {}, + }, + }), + async (req: Request, res: Response) => { + const channel_id = req.params.channel_id; + const channel = await Channel.findOneOrFail({ + where: { id: channel_id }, + }); + if (!channel) throw new HTTPError("Channel not found", 404); + + isTextChannel(channel.type); + const around = req.query.around ? `${req.query.around}` : undefined; + const before = req.query.before ? `${req.query.before}` : undefined; + const after = req.query.after ? `${req.query.after}` : undefined; + const limit = Number(req.query.limit) || 50; + if (limit < 1 || limit > 100) + throw new HTTPError("limit must be between 1 and 100", 422); + + const permissions = await getPermission( + req.user_id, + channel.guild_id, + channel_id, + ); + permissions.hasThrow("VIEW_CHANNEL"); + if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]); + + const query: FindManyOptions<Message> & { + where: { id?: FindOperator<string> | FindOperator<string>[] }; + } = { + order: { timestamp: "DESC" }, + take: limit, + where: { channel_id }, + relations: [ + "author", + "webhook", + "application", + "mentions", + "mention_roles", + "mention_channels", + "sticker_items", + "attachments", + ], + }; + + let messages: Message[]; + if (after) { + if (BigInt(after) > BigInt(Snowflake.generate())) + return res.status(422); + query.where.id = MoreThan(after); + messages = await Message.find(query); + } else if (before) { + if (BigInt(before) < BigInt(req.params.channel_id)) + return res.status(422); + query.where.id = LessThan(before); + messages = await Message.find(query); + } else if (around) { + query.take = Math.floor(limit / 2); + query.where.id = LessThan(around); + const messages_before = await Message.find(query); + query.where.id = MoreThan(around); + const messages_after = await Message.find(query); + messages = messages_before.concat(messages_after); + } else { + throw new HTTPError("after, around or before must be present", 422); + } - const messages = await Message.find(query); - const endpoint = Config.get().cdn.endpointPublic; + const endpoint = Config.get().cdn.endpointPublic; - return res.json( - messages.map((x: Partial<Message>) => { - (x.reactions || []).forEach((y: Partial<Reaction>) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - if ((y.user_ids || []).includes(req.user_id)) y.me = true; - delete y.user_ids; - }); - if (!x.author) - x.author = User.create({ - id: "4", - discriminator: "0000", - username: "Spacebar Ghost", - public_flags: 0, + return res.json( + messages.map((x: Partial<Message>) => { + (x.reactions || []).forEach((y: Partial<Reaction>) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore + if ((y.user_ids || []).includes(req.user_id)) y.me = true; + delete y.user_ids; + }); + if (!x.author) + x.author = User.create({ + id: "4", + discriminator: "0000", + username: "Spacebar Ghost", + public_flags: 0, + }); + x.attachments?.forEach((y: Attachment) => { + // dynamically set attachment proxy_url in case the endpoint changed + const uri = y.proxy_url.startsWith("http") + ? y.proxy_url + : `https://example.org${y.proxy_url}`; + y.proxy_url = `${endpoint == null ? "" : endpoint}${ + new URL(uri).pathname + }`; }); - x.attachments?.forEach((y: Attachment) => { - // dynamically set attachment proxy_url in case the endpoint changed - const uri = y.proxy_url.startsWith("http") - ? y.proxy_url - : `https://example.org${y.proxy_url}`; - y.proxy_url = `${endpoint == null ? "" : endpoint}${ - new URL(uri).pathname - }`; - }); - /** + /** Some clients ( discord.js ) only check if a property exists within the response, which causes errors when, say, the `application` property is `null`. **/ - // for (var curr in x) { - // if (x[curr] === null) - // delete x[curr]; - // } + // for (var curr in x) { + // if (x[curr] === null) + // delete x[curr]; + // } - return x; - }), - ); -}); + return x; + }), + ); + }, +); // TODO: config max upload size const messageUpload = multer({ @@ -205,9 +212,19 @@ router.post( next(); }, route({ - body: "MessageCreateSchema", + requestBody: "MessageCreateSchema", permission: "SEND_MESSAGES", right: "SEND_MESSAGES", + responses: { + 200: { + body: "Message", + }, + 400: { + body: "APIErrorResponse", + }, + 403: {}, + 404: {}, + }, }), async (req: Request, res: Response) => { const { channel_id } = req.params; @@ -366,3 +383,5 @@ router.post( return res.json(message); }, ); + +export default router; |