diff --git a/src/api/routes/channels/#channel_id/greet.ts b/src/api/routes/channels/#channel_id/greet.ts new file mode 100644 index 00000000..a0b25b99 --- /dev/null +++ b/src/api/routes/channels/#channel_id/greet.ts @@ -0,0 +1,103 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2025 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +import { route } from "@spacebar/api"; +import { Channel, emitEvent, GreetRequestSchema, Message, MessageCreateEvent, MessageType, Permissions, Sticker } from "@spacebar/util"; +import { Request, Response, Router } from "express"; +import { In } from "typeorm"; + +const router: Router = Router(); + +router.post( + "/", + route({ + requestBody: "GreetRequestSchema", + permission: "MANAGE_CHANNELS", + responses: { + 200: { + body: "Message", + }, + 404: {}, + 400: { + body: "APIErrorResponse", + }, + }, + }), + async (req: Request, res: Response) => { + const payload = req.body as GreetRequestSchema; + const { channel_id } = req.params; + + const channel = await Channel.findOneOrFail({ + where: { id: channel_id }, + }); + + const targetMessage = await Message.findOneOrFail({ + where: { + id: payload.message_reference?.message_id, + channel_id: payload.message_reference?.channel_id, + guild_id: payload.message_reference?.guild_id, + }, + }); + + if (!channel.isDm() && targetMessage.type != MessageType.GUILD_MEMBER_JOIN) + return res.status(400).json({ + code: 400, // TODO: what's the actual error code? + message: "Cannot send greet message referencing this message.", + }); + + if (!(await channel.getUserPermissions({ user_id: req.user_id })).has(Permissions.FLAGS.SEND_MESSAGES)) { + return res.status(403).json({ + code: 403, + message: "Missing Permissions: SEND_MESSAGES", + }); + } + + const specCompliant = true; // incase we want to allow clients to add more than one sticker to pick + if (specCompliant && payload.sticker_ids.length != 1) + return res.status(400).json({ + code: 400, + message: "Must include exactly one sticker.", + }); + + const stickers = await Sticker.find({ where: { id: In(payload.sticker_ids) } }); + + const randomSticker = stickers[Math.floor(Math.random() * stickers.length)]; + + const message = Message.create({ + channel_id: channel_id, + author_id: req.user_id, + type: MessageType.REPLY, + message_reference: { ...payload.message_reference, type: 0 }, + referenced_message: targetMessage, + sticker_items: randomSticker ? [{ id: randomSticker.id, name: randomSticker.name, format_type: randomSticker.format_type }] : [], + }); + + await Promise.all([ + message.save(), + emitEvent({ + event: "MESSAGE_CREATE", + data: message, + channel_id, + } as MessageCreateEvent), + ]); + + res.send(channel); + }, +); + +export default router; diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts index 40b27947..d24aff05 100644 --- a/src/api/routes/channels/#channel_id/messages/index.ts +++ b/src/api/routes/channels/#channel_id/messages/index.ts @@ -324,7 +324,7 @@ router.post( if (!req.rights.has(Rights.FLAGS.BYPASS_RATE_LIMITS)) { const limits = Config.get().limits; - if (limits.absoluteRate.register.enabled) { + if (limits.absoluteRate.sendMessage.enabled) { const count = await Message.count({ where: { channel_id, diff --git a/src/api/routes/users/#id/messages.ts b/src/api/routes/users/#id/messages.ts index c71b8764..aefa8329 100644 --- a/src/api/routes/users/#id/messages.ts +++ b/src/api/routes/users/#id/messages.ts @@ -51,4 +51,6 @@ router.get( }, ); +// TODO: POST to send a message to the user + export default router; diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index 74fda7e5..000102bf 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts @@ -210,6 +210,7 @@ export class Message extends BaseClass { message_id: string; channel_id?: string; guild_id?: string; + type?: number; // 0 = DEFAULT, 1 = FORWARD }; @JoinColumn({ name: "message_reference_id" }) diff --git a/src/util/schemas/GreetRequestSchema.ts b/src/util/schemas/GreetRequestSchema.ts new file mode 100644 index 00000000..62294ece --- /dev/null +++ b/src/util/schemas/GreetRequestSchema.ts @@ -0,0 +1,31 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2023 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +import { ConnectedAccountTokenData } from "../interfaces"; +import { AllowedMentions } from "@spacebar/util*"; + +export interface GreetRequestSchema { + sticker_ids: string[]; + allowed_mentions?: AllowedMentions; + message_reference?: { + message_id: string; + channel_id?: string; + guild_id?: string; + fail_if_not_exists?: boolean; + }; +} \ No newline at end of file diff --git a/src/util/schemas/index.ts b/src/util/schemas/index.ts index 68c38fb5..9bba9cb4 100644 --- a/src/util/schemas/index.ts +++ b/src/util/schemas/index.ts @@ -41,6 +41,7 @@ export * from "./EmojiCreateSchema"; export * from "./EmojiModifySchema"; export * from "./ForgotPasswordSchema"; export * from "./GatewayPayloadSchema"; +export * from "./GreetRequestSchema"; export * from "./GuildCreateSchema"; export * from "./GuildSubscriptionsBulkSchema"; export * from "./GuildTemplateCreateSchema";