From 99d9bf563fb5e157600824776b149ca03cbea47c Mon Sep 17 00:00:00 2001 From: Puyodead1 Date: Sun, 10 Dec 2023 17:02:27 -0500 Subject: [PATCH 01/41] Start implementing webhooks --- assets/schemas.json | Bin 18300690 -> 18445000 bytes src/api/middlewares/Authentication.ts | 4 +- .../routes/channels/#channel_id/webhooks.ts | 25 +- src/api/routes/guilds/#guild_id/webhooks.ts | 35 ++- .../webhooks/#webhook_id/#token/index.ts | 215 ++++++++++++++++++ src/api/routes/webhooks/#webhook_id/index.ts | 32 +++ src/util/schemas/MessageCreateSchema.ts | 4 +- src/util/schemas/WebhookCreateSchema.ts | 1 - src/util/schemas/WebhookExecuteSchema.ts | 46 ++++ src/util/schemas/index.ts | 1 + src/util/util/Constants.ts | 2 +- 11 files changed, 348 insertions(+), 17 deletions(-) create mode 100644 src/api/routes/webhooks/#webhook_id/#token/index.ts create mode 100644 src/api/routes/webhooks/#webhook_id/index.ts create mode 100644 src/util/schemas/WebhookExecuteSchema.ts diff --git a/assets/schemas.json b/assets/schemas.json index a0ab1697c99e4bad5533af3b5d5feaa87e003ecc..018ddacace716f96c6f3733a521e7a5daaa385ef 100644 GIT binary patch delta 6760 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 a#WB1*bvZHo?I$P3jXR1}%F. */ -import { checkToken, Rights } from "@spacebar/util"; import * as Sentry from "@sentry/node"; +import { checkToken, Rights } from "@spacebar/util"; import { NextFunction, Request, Response } from "express"; import { HTTPError } from "lambert-server"; @@ -32,7 +32,7 @@ export const NO_AUTHORIZATION_ROUTES = [ "/auth/forgot", "/auth/reset", // Routes with a seperate auth system - "/webhooks/", + /\/webhooks\/\d+\/\w+\/?/, // no token requires auth // Public information endpoints "/ping", "/gateway", diff --git a/src/api/routes/channels/#channel_id/webhooks.ts b/src/api/routes/channels/#channel_id/webhooks.ts index d54756a16..4c1ccbdf2 100644 --- a/src/api/routes/channels/#channel_id/webhooks.ts +++ b/src/api/routes/channels/#channel_id/webhooks.ts @@ -26,8 +26,8 @@ import { WebhookCreateSchema, WebhookType, handleFile, - trimSpecial, isTextChannel, + trimSpecial, } from "@spacebar/util"; import crypto from "crypto"; import { Request, Response, Router } from "express"; @@ -35,10 +35,12 @@ import { HTTPError } from "lambert-server"; const router: Router = Router(); -//TODO: implement webhooks router.get( "/", route({ + description: + "Returns a list of channel webhook objects. Requires the MANAGE_WEBHOOKS permission.", + permission: "MANAGE_WEBHOOKS", responses: { 200: { body: "APIWebhookArray", @@ -46,7 +48,18 @@ router.get( }, }), async (req: Request, res: Response) => { - res.json([]); + const { channel_id } = req.params; + const webhooks = await Webhook.find({ + where: { channel_id }, + relations: [ + "user", + "guild", + "source_guild", + "application" /*"source_channel"*/, + ], + }); + + return res.json(webhooks); }, ); @@ -89,15 +102,15 @@ router.post( if (avatar) avatar = await handleFile(`/avatars/${channel_id}`, avatar); - const hook = Webhook.create({ + const hook = await Webhook.create({ type: WebhookType.Incoming, name, avatar, guild_id: channel.guild_id, channel_id: channel.id, user_id: req.user_id, - token: crypto.randomBytes(24).toString("base64"), - }); + token: crypto.randomBytes(24).toString("base64url"), + }).save(); const user = await User.getPublicUser(req.user_id); diff --git a/src/api/routes/guilds/#guild_id/webhooks.ts b/src/api/routes/guilds/#guild_id/webhooks.ts index d58659a41..a2ef7d699 100644 --- a/src/api/routes/guilds/#guild_id/webhooks.ts +++ b/src/api/routes/guilds/#guild_id/webhooks.ts @@ -16,12 +16,37 @@ along with this program. If not, see . */ -import { Router, Response, Request } from "express"; import { route } from "@spacebar/api"; +import { Webhook } from "@spacebar/util"; +import { Request, Response, Router } from "express"; const router = Router(); -//TODO: implement webhooks -router.get("/", route({}), async (req: Request, res: Response) => { - res.json([]); -}); +router.get( + "/", + route({ + description: + "Returns a list of guild webhook objects. Requires the MANAGE_WEBHOOKS permission.", + permission: "MANAGE_WEBHOOKS", + responses: { + 200: { + body: "APIWebhookArray", + }, + }, + }), + async (req: Request, res: Response) => { + const { guild_id } = req.params; + const webhooks = await Webhook.find({ + where: { guild_id }, + relations: [ + "user", + "guild", + "source_guild", + "application" /*"source_channel"*/, + ], + }); + + return res.json(webhooks); + }, +); + export default router; diff --git a/src/api/routes/webhooks/#webhook_id/#token/index.ts b/src/api/routes/webhooks/#webhook_id/#token/index.ts new file mode 100644 index 000000000..b47502b44 --- /dev/null +++ b/src/api/routes/webhooks/#webhook_id/#token/index.ts @@ -0,0 +1,215 @@ +import { handleMessage, route } from "@spacebar/api"; +import { + Attachment, + Config, + DiscordApiErrors, + FieldErrors, + Message, + Webhook, + WebhookExecuteSchema, + uploadFile, +} from "@spacebar/util"; +import { Request, Response, Router } from "express"; +import { HTTPError } from "lambert-server"; +import multer from "multer"; +import { MoreThan } from "typeorm"; +const router = Router(); + +router.get( + "/", + route({ + description: "Returns a webhook object for the given id.", + responses: { + 200: { + body: "APIWebhook", + }, + 404: {}, + }, + }), + async (req: Request, res: Response) => { + const { webhook_id, token } = req.params; + const webhook = await Webhook.findOne({ + where: { + id: webhook_id, + }, + relations: ["channel", "guild", "application"], + }); + + if (!webhook) { + throw DiscordApiErrors.UNKNOWN_WEBHOOK; + } + + if (webhook.token !== token) { + throw DiscordApiErrors.INVALID_WEBHOOK_TOKEN_PROVIDED; + } + + return res.json(webhook); + }, +); + +// TODO: config max upload size +const messageUpload = multer({ + limits: { + fileSize: Config.get().limits.message.maxAttachmentSize, + fields: 10, + // files: 1 + }, + storage: multer.memoryStorage(), +}); // max upload 50 mb + +// https://discord.com/developers/docs/resources/webhook#execute-webhook +router.post( + "/", + messageUpload.any(), + (req, res, next) => { + if (req.body.payload_json) { + req.body = JSON.parse(req.body.payload_json); + } + + next(); + }, + route({ + requestBody: "WebhookExecuteSchema", + query: { + wait: { + type: "boolean", + required: false, + description: + "waits for server confirmation of message send before response, and returns the created message body", + }, + thread_id: { + type: "string", + required: false, + description: + "Send a message to the specified thread within a webhook's channel.", + }, + }, + responses: { + 204: {}, + 400: { + body: "APIErrorResponse", + }, + 404: {}, + }, + }), + async (req: Request, res: Response) => { + const { webhook_id, token } = req.params; + const body = req.body as WebhookExecuteSchema; + const attachments: Attachment[] = []; + + // ensure one of content, embeds, components, or file is present + if ( + !body.content && + !body.embeds && + !body.components && + !body.file && + !body.attachments + ) { + throw DiscordApiErrors.CANNOT_SEND_EMPTY_MESSAGE; + } + + // block username from containing certain words + // TODO: configurable additions + const blockedContains = ["discord", "clyde", "spacebar"]; + for (const word of blockedContains) { + if (body.username?.toLowerCase().includes(word)) { + return res.status(400).json({ + username: [`Username cannot contain "${word}"`], + }); + } + } + + // block username from being certain words + // TODO: configurable additions + const blockedEquals = ["everyone", "here"]; + for (const word of blockedEquals) { + if (body.username?.toLowerCase() === word) { + return res.status(400).json({ + username: [`Username cannot be "${word}"`], + }); + } + } + + const webhook = await Webhook.findOne({ + where: { + id: webhook_id, + }, + relations: ["channel", "guild", "application"], + }); + + if (!webhook) { + throw DiscordApiErrors.UNKNOWN_WEBHOOK; + } + + if (!webhook.channel.isWritable()) { + throw new HTTPError( + `Cannot send messages to channel of type ${webhook.channel.type}`, + 400, + ); + } + + if (webhook.token !== token) { + throw DiscordApiErrors.INVALID_WEBHOOK_TOKEN_PROVIDED; + } + + // TODO: creating messages by users checks if the user can bypass rate limits, we cant do that on webhooks, but maybe we could check the application if there is one? + const limits = Config.get().limits; + if (limits.absoluteRate.register.enabled) { + const count = await Message.count({ + where: { + channel_id: webhook.channel_id, + timestamp: MoreThan( + new Date( + Date.now() - limits.absoluteRate.sendMessage.window, + ), + ), + }, + }); + + if (count >= limits.absoluteRate.sendMessage.limit) + throw FieldErrors({ + channel_id: { + code: "TOO_MANY_MESSAGES", + message: req.t("common:toomany.MESSAGE"), + }, + }); + } + + const files = (req.files as Express.Multer.File[]) ?? []; + for (const currFile of files) { + try { + const file = await uploadFile( + `/attachments/${webhook.channel.id}`, + currFile, + ); + attachments.push( + Attachment.create({ ...file, proxy_url: file.url }), + ); + } catch (error) { + return res.status(400).json({ message: error?.toString() }); + } + } + + // TODO: set username and avatar based on body + + const embeds = body.embeds || []; + const message = await handleMessage({ + ...body, + type: 0, + pinned: false, + webhook_id: webhook.id, + application_id: webhook.application?.id, + embeds, + channel_id: webhook.channel_id, + attachments, + timestamp: new Date(), + }); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore dont care2 + message.edited_timestamp = null; + + webhook.channel.last_message_id = message.id; + }, +); + +export default router; diff --git a/src/api/routes/webhooks/#webhook_id/index.ts b/src/api/routes/webhooks/#webhook_id/index.ts new file mode 100644 index 000000000..cc8c03865 --- /dev/null +++ b/src/api/routes/webhooks/#webhook_id/index.ts @@ -0,0 +1,32 @@ +import { route } from "@spacebar/api"; +import { Webhook } from "@spacebar/util"; +import { Request, Response, Router } from "express"; +const router = Router(); + +router.get( + "/", + route({ + description: "Returns a webhook object for the given id.", + responses: { + 200: { + body: "APIWebhook", + }, + 404: {}, + }, + }), + async (req: Request, res: Response) => { + const { webhook_id } = req.params; + const webhook = await Webhook.findOneOrFail({ + where: { id: webhook_id }, + relations: [ + "user", + "guild", + "source_guild", + "application" /*"source_channel"*/, + ], + }); + return res.json(webhook); + }, +); + +export default router; diff --git a/src/util/schemas/MessageCreateSchema.ts b/src/util/schemas/MessageCreateSchema.ts index 57abf62f9..8093a10a3 100644 --- a/src/util/schemas/MessageCreateSchema.ts +++ b/src/util/schemas/MessageCreateSchema.ts @@ -18,7 +18,7 @@ import { Embed } from "@spacebar/util"; -type Attachment = { +export type MessageCreateAttachment = { id: string; filename: string; }; @@ -52,7 +52,7 @@ export interface MessageCreateSchema { TODO: we should create an interface for attachments TODO: OpenWAAO<-->attachment-style metadata conversion **/ - attachments?: Attachment[]; + attachments?: MessageCreateAttachment[]; sticker_ids?: string[]; // eslint-disable-next-line @typescript-eslint/no-explicit-any components?: any[]; diff --git a/src/util/schemas/WebhookCreateSchema.ts b/src/util/schemas/WebhookCreateSchema.ts index f92cb63e7..7bd0afa8d 100644 --- a/src/util/schemas/WebhookCreateSchema.ts +++ b/src/util/schemas/WebhookCreateSchema.ts @@ -16,7 +16,6 @@ along with this program. If not, see . */ -// TODO: webhooks export interface WebhookCreateSchema { /** * @maxLength 80 diff --git a/src/util/schemas/WebhookExecuteSchema.ts b/src/util/schemas/WebhookExecuteSchema.ts new file mode 100644 index 000000000..943cbe9ed --- /dev/null +++ b/src/util/schemas/WebhookExecuteSchema.ts @@ -0,0 +1,46 @@ +/* + 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 { Embed } from "../entities"; +import { MessageCreateAttachment } from "./MessageCreateSchema"; + +export interface WebhookExecuteSchema { + content?: string; + username?: string; + avatar_url?: string; + tts?: boolean; + embeds?: Embed[]; + allowed_mentions?: { + parse?: string[]; + roles?: string[]; + users?: string[]; + replied_user?: boolean; + }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + components?: any[]; + file?: { filename: string }; + payload_json?: string; + /** + TODO: we should create an interface for attachments + TODO: OpenWAAO<-->attachment-style metadata conversion + **/ + attachments?: MessageCreateAttachment[]; + flags?: number; + thread_name?: string; + applied_tags?: string[]; +} diff --git a/src/util/schemas/index.ts b/src/util/schemas/index.ts index 44a504cda..4812b5356 100644 --- a/src/util/schemas/index.ts +++ b/src/util/schemas/index.ts @@ -79,5 +79,6 @@ export * from "./VoiceStateUpdateSchema"; export * from "./VoiceVideoSchema"; export * from "./WebAuthnSchema"; export * from "./WebhookCreateSchema"; +export * from "./WebhookExecuteSchema"; export * from "./WidgetModifySchema"; export * from "./responses"; diff --git a/src/util/util/Constants.ts b/src/util/util/Constants.ts index e68bb0b7e..112b0cc42 100644 --- a/src/util/util/Constants.ts +++ b/src/util/util/Constants.ts @@ -576,7 +576,7 @@ export const DiscordApiErrors = { UNKNOWN_TOKEN: new ApiError("Unknown token", 10012), UNKNOWN_USER: new ApiError("Unknown user", 10013), UNKNOWN_EMOJI: new ApiError("Unknown emoji", 10014), - UNKNOWN_WEBHOOK: new ApiError("Unknown webhook", 10015), + UNKNOWN_WEBHOOK: new ApiError("Unknown webhook", 10015, 404), UNKNOWN_WEBHOOK_SERVICE: new ApiError("Unknown webhook service", 10016), UNKNOWN_CONNECTION: new ApiError("Unknown connection", 10017, 400), UNKNOWN_SESSION: new ApiError("Unknown session", 10020), From e7a98b6c46e3d4fdaa68bd8a04ffcf53a9dac184 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Thu, 18 Jul 2024 12:42:07 +0200 Subject: [PATCH 02/41] webhook fixes & username/avatar property for msg --- assets/openapi.json | Bin 571764 -> 584567 bytes assets/schemas.json | Bin 18445000 -> 18473638 bytes .../webhooks/#webhook_id/#token/index.ts | 25 +++- src/api/util/handlers/Message.ts | 119 ++++++++++++------ src/util/entities/Message.ts | 19 ++- src/util/entities/Webhook.ts | 8 +- .../1721298824927-webhookMessageProperties.ts | 15 +++ .../1721298824927-webhookMessageProperties.ts | 15 +++ .../1721298824927-webhookMessageProperties.ts | 15 +++ 9 files changed, 172 insertions(+), 44 deletions(-) create mode 100644 src/util/migration/mariadb/1721298824927-webhookMessageProperties.ts create mode 100644 src/util/migration/mysql/1721298824927-webhookMessageProperties.ts create mode 100644 src/util/migration/postgres/1721298824927-webhookMessageProperties.ts diff --git a/assets/openapi.json b/assets/openapi.json index 19686c591fa5d8c8f8ab92715324cd5a311d8f48..6860c21b7c9f20565abb06947b07217aaac64dd3 100644 GIT binary patch delta 2189 zcmcIlYfKbZ6waN;K3ovg>Y}i4!3UKfEG8nFLJ>sNR-};Hh}GR?cYqO?S!Q;n6|HF6 zpoTWsn{u=zx^0>?jUpScTpMHAT7O_vT>W9%^bs3lYpg=iHf@MDogF}oB-J1G&t&es zGw1ux`M!J3A9qv!x;i%)3lQFbOeR_J2#P3GlLH+R1Wp=XB{2}>9g%JLI10rRd(hO= z2fNHrT8+%)X!@JPp0Tv2;*%dt(3gi6K(K_1d48Q=_2EhZyIpl|f1S%GcyM8jOOk}T zjE7sLvMR#mb8I}swxVbfyJrcBg;uiVw1wacao}__MsoY;dg4r&1A$>Sf#6mH{8o#s zWa<$E!C4&n;+QppX@;xkSSxXIJanB$ncba!1u7Xd9YP*1oAxE4#@R5im@(2#%TRa? zdG5$e2;4*|kbjby2LqkV6nLwLNg$>1xg@h78Jww19>Hasdp0_w5ec511EE}wqgUQR z*|`&OL5uqnwVy=>GlUe@0C_oVAp`>`9xkqBlOxeUj^onwm+%!74VmlM1U)!h{D=9F zIxnGL62aSu1n9cOE`#Qe(4zJhqZuy!$bLuP8$$goJ#`QL9i>r-BX*;4k~A+8++F;^ z2qQcu4|O1BMgRt`YTO)>;A@nXS_fRNKxxo?l3fCWE14}3u`S>&LmQ#9S6l8HVVrtO zkp~Z_PB0Q_e!$Z~%GgZwK{xXyqDjY@0#0Ky8+P;~Sp%QmzLK%gwr-|^eOffCp#`ER z2ib9!5ewnAmz_(Fe4hxhiO5Xn&0=E^+58}ic$+FTHi=X;v1&9K8d(j&cbNAGVRkDJ8H3F^{w+Rv!J%GMNGihKSmxy4ujk7^$pV0^2VPn5> zJnAc*{H=$J0~vDZ-hDr4eCbaX3%K2pa2s+2wOvz51k z*K1hL`)agfKuLxL5Zh=&8&Mf?KQ-wJD|l?7@wquSS$6oRq3! z_t^Y`ul;<*cyY5^lyr_N={&?hpn@-^_98w3ja7WG99ig4F0Y!V%PzmKW^ty=>#Y;r zde-#LUhWctp>l*H_S*#EMSLL?FXeM-LXbvL7-Twl za&{%yK=mcokmV*T9i*s_tVu_I;ixEcK9Fbd|jlLX+6>STTiVXKs-1?PmzzmJ?j@&Da zihlskVfcFFuC`aAz6eZsBN38?*EUnA&9Z23-zL0VM!gn1!qZ~zD7s#GNGiz`uGaO~ za7}uP!ybsG?=EPZf%+Kl>5*8bvG(Ui3Oz2^$czZe6XwW@Y~934gcTJGb%WF2CAE;v0}jS9%(_0JTMKct~i#@xafTJdKvi?ZP*BJ9;LFv_{uIWd)N z2`n>?CM>J~FsL#U7PDw5;~MJIKof)GB=pUh*v=r1I&G{(rNn&JT#n(NV4`?p4gauX zwS@W@OQpMa*_)XEL7$R^&k{?^3>KdlH9aSqiP{vKLXJUJAlns6J;Fx?C9LFDDPCCb_oJ#lGsfZ(&Ll^a^D5V^i<3y87ha<)qM@CHJtUYW#Elx+y z>O;0JYcrcIdJOLrot#J~rLir9EmN6o`r4vNF8i$f0p5N%KR&;&*ZcOlo%)Ur;i2<) zg_*~>>67W+9*Ist^}tR`bQ^^qZz!CIZPd>BkxE=k97`}Ov)U+)l$v`QK)9ig#u_#~_z-{}N=r?U@=Q}RUM=n4Wy zxA5VZQZOzk#vOuUJW^14Nj%xT-V=7O_d4VHs(OUAHv4(A7Wc^$yejo_AhtA1VN0{G z?r972o-Wnerw z#u@3j{|khD0g5eSpQDP&!Zq}a14GaFZhKs~lyTN1!%q$weiJ>UuLJ8-CV@UB=!y>x zCaU~kQ00f<#)L(xF^7JoF{2mAL_Q1>`S2iYV$(e$Al)N!6qeH`M1ekGN;k6q=;6{B zTcX=K6?9vp!?B_ahQp=PMq;_@=2)n1jw?krJ(>4xI?Q_(UxL+Zm(757nPRplu4m|k zJ+p;GrkM#c%`7LZD1%|bo;hAvE~-KaQ5AEWkfTNV?x>*ePGTxnPbrxXN=edeWHD%) zzeYd|QEz}DYQZI>*?yR|z8zpomaR#KWos7txFBoOFTbf^5o{`0tj2bIX3rBLu~?RX z#gbD0I^M~yM{3yhXz3c{s!Nz{X!Bpb&l?vmS&@zP zRS;XTT91wIZ0B2WwsTDhYSd=(G-0LdIAfdrWIDH96=9UvZzeSjOQ5D9<+22sDVbtD z$Q0T_K^6WSvsY%}eHR)-Wk$HYeXP0b*?2>mzAhk(R(LJzNSHy0u6!Rm*rc~36M8#7n2ps7b=V1^ z4j;}#t)Me`k}>ro$e6loE^75+T$PqUl{O0(ALG&sK8EyyY}}cIo%jpuc0*y^o??_w z#s&B2A-E@}4mC@Rd*Hhl9{A>lcwi=`lNxnFcIN|2Z0lQy7;x=#lfG!;^4vtY(?BPMc}R}4#id^KCcAg z^FFsiO$J+x&1wU{L6J&l1g3Rx*S2!XrD+6)a(NC+k3b^1?zamvp-x zl5Sg)k=(%Lq}b*n6x%fJLk4MlUnC~uW13)m%q1VBa6hY9*9?kvKlr{5yNX_hRrHD* zk|k;8A0f^B(;_TadH-jqyx%ex%ROy%Qy{3X+ zpsCgqRD!YWA(R;Y@w2ZMl`mJQ@Cf%P^_(hH)3Mq)n}a zGuhPIbAyp?bJ#x;)x0Bj1)HB*cPGwN>+a0q?z0Nsusxog_O{2navGG^Y5bs}JN^Th CUkZ}| delta 6850 zcmbW5`BPI@6vyX50s$2RLQEh$zy(kvCL-d}3Py=mHpSKz1+k*msUo6)1r)bSt7S3h z)kI4yNG*a;D;`@VQNazWh$9j~iQ3|VHj3!j1+fqK2TuA!CYj89=KDS8o_pRoulUjv z!PT}l!PZkuNFaT+Ue>(-Y!$oTeX$gBtp}?|&{x-LLus(p5I6ej?FAcqhF zU5Q_m75GJ2zj4B1q+<&YI=1iw2cd~~Wj64x%yu(gBe+EX!7X;vL|6^V5w>Ju|9-Hr ze}5~qwLVNSX#glDIZVgX+vE>8|oHqp;}#v6zWpktHqY7#g(!USL!jt1&fiC zd!CST&&$gPO{`*v!YX!HI64tzNBD5q5$>IacU7P61NG^?%aL_pG83!P5nxq1(hKQ| zBRZQ$L1(kf8n1C#;0KolV_rMra>Z$61;`;QK;h+yCQ=i|LTZA)(F09*9Uli?#{*bI zg%Pf$ai8&AYYS{;a0;zva-TmD?(--4BNY0YCNYhj45qOk`QVAhn2(_`W=bczMrdi( z)B&U@HwcPygD2vJI1Eawl*93au6-Kl+NT#H3x#`Ta0HKtBq|U|W*o)~ZBxyJZK_Yg zr0A15oIVQ3)- zsrWPuooBKn<}+9lGw&AORrl`s(7k(sd@wr2@2eKV`>I9D(1l4xn-{}qbM#HLD_TI_ z_ajZNOQFd%CJx!bxD*R!#e!hgvb`)inAO|kVDIaU2^u$e`yLv01RSO-Hh_2iQbmg`ULTqNJ zYMpo#sYv<~Dw5W2z}M{SBkSPxk%YVWglcZA2hEK{8Cs*DJj{j}K=Y;I1me`S0i3#$ z&Z6adVOvt@y9o+?llADl5_xP2$YWC{qixdIsNZ=s_eBPevbT9I)YFhQfmZP5c+ucT z&wQx7JJ^*w9zWudy%{{Rzj8nq6zS5ZL6<)Lf@M|+X9aR!Y^g!#N}VipCV`Sn2$XD_ zfEFWDRNG;SYDW~lZN((*gqWmVv(YVua)d3}SwvuGQPxFtKt$B=HHaF%8HWz0Z`5VG zcLUqKM~C;6I7yJ_sn1&&Ta7*+ zYV`YVSm-bQ`F@ChPA$a*;)5U%Yfe~ZlGZu!?1(x<3+j+Vi~bK=n|K&%6ThpqFv?X0 z1+c0>Hy(WoeTSFWcmxs~zvrWG0F7L6Oh6*6iy*@KsHNCQMKO$2{4h^~E)`A1jL8?ocz1@vi(n|Mp6QC!=ygY0jWy6}yfhOp^p?Gk~&Z>`E5Tw7@fYpA33@AI-J~l!vinGVW89Bl%WaQD^%Rx0LAUM zBxsCei*7@kNjK>yaS5xu?G$l`~k^P+8t+?wC zvX*w;NbHp)TF)1t_53pw9mhWQojoyxnZXeDat4|rjVZ67F{NWJn&@j5ABtYXhoa7T zXp9VNcmu;4-i|^)(sr`%D%rm*xF#yu-=LEHGaNrSaJ#jDC>Q+;%0*q-=w?p11se)T zcO~*Y9C!Xht3R9_TFneZTgzcO7{si%XMEAIA7%OxQ3{cHdL diff --git a/src/api/routes/webhooks/#webhook_id/#token/index.ts b/src/api/routes/webhooks/#webhook_id/#token/index.ts index b47502b44..538ee181f 100644 --- a/src/api/routes/webhooks/#webhook_id/#token/index.ts +++ b/src/api/routes/webhooks/#webhook_id/#token/index.ts @@ -1,12 +1,14 @@ -import { handleMessage, route } from "@spacebar/api"; +import { handleMessage, postHandleMessage, route } from "@spacebar/api"; import { Attachment, Config, DiscordApiErrors, FieldErrors, Message, + MessageCreateEvent, Webhook, WebhookExecuteSchema, + emitEvent, uploadFile, } from "@spacebar/util"; import { Request, Response, Router } from "express"; @@ -93,7 +95,11 @@ router.post( }, }), async (req: Request, res: Response) => { + const { wait, thread_id } = req.query; + if (!wait) return res.status(204).send(); + const { webhook_id, token } = req.params; + const body = req.body as WebhookExecuteSchema; const attachments: Attachment[] = []; @@ -200,6 +206,7 @@ router.post( webhook_id: webhook.id, application_id: webhook.application?.id, embeds, + // TODO: Support thread_id/thread_name once threads are implemented channel_id: webhook.channel_id, attachments, timestamp: new Date(), @@ -209,6 +216,22 @@ router.post( message.edited_timestamp = null; webhook.channel.last_message_id = message.id; + + await Promise.all([ + message.save(), + emitEvent({ + event: "MESSAGE_CREATE", + channel_id: webhook.channel_id, + data: message, + } as MessageCreateEvent), + ]); + + // no await as it shouldnt block the message send function and silently catch error + postHandleMessage(message).catch((e) => + console.error("[Message] post-message handler failed", e), + ); + + return res.json(message); }, ); diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts index 6172a3d0c..186165066 100644 --- a/src/api/util/handlers/Message.ts +++ b/src/api/util/handlers/Message.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -41,11 +41,13 @@ import { Sticker, MessageCreateSchema, EmbedCache, + handleFile, } from "@spacebar/util"; import { HTTPError } from "lambert-server"; import { In } from "typeorm"; import { EmbedHandlers } from "@spacebar/api"; import * as Sentry from "@sentry/node"; +import fetch from "node-fetch"; const allow_empty = false; // TODO: check webhook, application, system author, stickers // TODO: embed gifs/videos/images @@ -92,44 +94,89 @@ export async function handleMessage(opts: MessageOptions): Promise { where: { id: opts.application_id }, }); } + + let permission: any; if (opts.webhook_id) { message.webhook = await Webhook.findOneOrFail({ where: { id: opts.webhook_id }, }); - } - const permission = await getPermission( - opts.author_id, - channel.guild_id, - opts.channel_id, - ); - permission.hasThrow("SEND_MESSAGES"); - if (permission.cache.member) { - message.member = permission.cache.member; - } + message.author = (await User.findOne({ + where: { id: opts.webhook_id }, + })) || undefined; - if (opts.tts) permission.hasThrow("SEND_TTS_MESSAGES"); - if (opts.message_reference) { - permission.hasThrow("READ_MESSAGE_HISTORY"); - // code below has to be redone when we add custom message routing - if (message.guild_id !== null) { - const guild = await Guild.findOneOrFail({ - where: { id: channel.guild_id }, + if (!message.author) { + message.author = User.create({ + id: opts.webhook_id, + username: message.webhook.name, + discriminator: "0000", + avatar: message.webhook.avatar, + public_flags: 0, + premium: false, + premium_type: 0, + bot: true, + created_at: new Date(), + verified: true, + rights: "0", + data: { + valid_tokens_since: new Date(), + }, }); - if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) { - if (opts.message_reference.guild_id !== channel.guild_id) - throw new HTTPError( - "You can only reference messages from this guild", - ); - if (opts.message_reference.channel_id !== opts.channel_id) - throw new HTTPError( - "You can only reference messages from this channel", - ); - } + + await message.author.save(); + } + + if (opts.username) { + message.username = opts.username; + message.author.username = message.username; + } + if (opts.avatar_url) { + const avatarData = await fetch(opts.avatar_url); + const base64 = await avatarData.buffer().then((x) => x.toString("base64")); + + const dataUri = "data:" + avatarData.headers.get("content-type") + ";base64," + base64; + + message.avatar = await handleFile( + `/avatars/${opts.webhook_id}`, + dataUri as string, + ); + console.log(message.avatar); + message.author.avatar = message.avatar; + } + } else { + permission = await getPermission( + opts.author_id, + channel.guild_id, + opts.channel_id, + ); + permission.hasThrow("SEND_MESSAGES"); + if (permission.cache.member) { + message.member = permission.cache.member; + } + + if (opts.tts) permission.hasThrow("SEND_TTS_MESSAGES"); + if (opts.message_reference) { + permission.hasThrow("READ_MESSAGE_HISTORY"); + // code below has to be redone when we add custom message routing + if (message.guild_id !== null) { + const guild = await Guild.findOneOrFail({ + where: { id: channel.guild_id }, + }); + if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) { + if (opts.message_reference.guild_id !== channel.guild_id) + throw new HTTPError( + "You can only reference messages from this guild", + ); + if (opts.message_reference.channel_id !== opts.channel_id) + throw new HTTPError( + "You can only reference messages from this channel", + ); + } + } + /** Q: should be checked if the referenced message exists? ANSWER: NO + otherwise backfilling won't work **/ + message.type = MessageType.REPLY; } - /** Q: should be checked if the referenced message exists? ANSWER: NO - otherwise backfilling won't work **/ - message.type = MessageType.REPLY; } // TODO: stickers/activity @@ -172,14 +219,14 @@ export async function handleMessage(opts: MessageOptions): Promise { const role = await Role.findOneOrFail({ where: { id: mention, guild_id: channel.guild_id }, }); - if (role.mentionable || permission.has("MANAGE_ROLES")) { + if (role.mentionable || (opts.webhook_id || permission.has("MANAGE_ROLES"))) { mention_role_ids.push(mention); } }, ), ); - if (permission.has("MENTION_EVERYONE")) { + if (opts.webhook_id || permission.has("MENTION_EVERYONE")) { mention_everyone = !!content.match(EVERYONE_MENTION) || !!content.match(HERE_MENTION); @@ -302,4 +349,6 @@ interface MessageOptions extends MessageCreateSchema { attachments?: Attachment[]; edited_timestamp?: Date; timestamp?: Date; + username?: string; + avatar_url?: string; } diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index b519099ab..86238e532 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -218,6 +218,12 @@ export class Message extends BaseClass { @Column({ type: "simple-json", nullable: true }) components?: MessageComponent[]; + @Column({ nullable: true }) + username?: string; + + @Column({ nullable: true }) + avatar?: string; + toJSON(): Message { return { ...this, @@ -234,7 +240,12 @@ export class Message extends BaseClass { reactions: this.reactions ?? undefined, sticker_items: this.sticker_items ?? undefined, message_reference: this.message_reference ?? undefined, - author: this.author?.toPublicUser() ?? undefined, + author: { + ...this.author?.toPublicUser() ?? undefined, + // Webhooks + username: this.username ?? this.author?.username, + avatar: this.avatar ?? this.author?.avatar, + }, activity: this.activity ?? undefined, application: this.application ?? undefined, components: this.components ?? undefined, diff --git a/src/util/entities/Webhook.ts b/src/util/entities/Webhook.ts index 91498a22c..b7fba53a1 100644 --- a/src/util/entities/Webhook.ts +++ b/src/util/entities/Webhook.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -35,7 +35,7 @@ export class Webhook extends BaseClass { type: WebhookType; @Column({ nullable: true }) - name?: string; + name: string; @Column({ nullable: true }) avatar?: string; diff --git a/src/util/migration/mariadb/1721298824927-webhookMessageProperties.ts b/src/util/migration/mariadb/1721298824927-webhookMessageProperties.ts new file mode 100644 index 000000000..ccbe689ad --- /dev/null +++ b/src/util/migration/mariadb/1721298824927-webhookMessageProperties.ts @@ -0,0 +1,15 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class WebhookMessageProperties1721298824927 implements MigrationInterface { + name = "WebhookMessageProperties1721298824927"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE `messages` ADD `username` text NULL"); + await queryRunner.query("ALTER TABLE `messages` ADD `avatar` text NULL"); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE `messages` DROP COLUMN `username`"); + await queryRunner.query("ALTER TABLE `messages` DROP COLUMN `avatar`"); + } +} diff --git a/src/util/migration/mysql/1721298824927-webhookMessageProperties.ts b/src/util/migration/mysql/1721298824927-webhookMessageProperties.ts new file mode 100644 index 000000000..ccbe689ad --- /dev/null +++ b/src/util/migration/mysql/1721298824927-webhookMessageProperties.ts @@ -0,0 +1,15 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class WebhookMessageProperties1721298824927 implements MigrationInterface { + name = "WebhookMessageProperties1721298824927"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE `messages` ADD `username` text NULL"); + await queryRunner.query("ALTER TABLE `messages` ADD `avatar` text NULL"); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE `messages` DROP COLUMN `username`"); + await queryRunner.query("ALTER TABLE `messages` DROP COLUMN `avatar`"); + } +} diff --git a/src/util/migration/postgres/1721298824927-webhookMessageProperties.ts b/src/util/migration/postgres/1721298824927-webhookMessageProperties.ts new file mode 100644 index 000000000..46c507d43 --- /dev/null +++ b/src/util/migration/postgres/1721298824927-webhookMessageProperties.ts @@ -0,0 +1,15 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class WebhookMessageProperties1721298824927 implements MigrationInterface { + name = "WebhookMessageProperties1721298824927"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE messages ADD username text NULL"); + await queryRunner.query("ALTER TABLE messages ADD avatar text NULL"); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE messages DROP COLUMN username"); + await queryRunner.query("ALTER TABLE messages DROP COLUMN avatar"); + } +} From 6be3714593d282b4ac9881005a358355f729aa0d Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Thu, 18 Jul 2024 12:52:00 +0200 Subject: [PATCH 03/41] Fix ESLint & prettier --- .../webhooks/#webhook_id/#token/index.ts | 2 +- src/api/routes/webhooks/#webhook_id/index.ts | 1 + src/api/util/handlers/Message.ts | 28 +++++++++++++------ src/util/entities/Message.ts | 2 +- .../1721298824927-webhookMessageProperties.ts | 16 ++++++++--- .../1721298824927-webhookMessageProperties.ts | 16 ++++++++--- .../1721298824927-webhookMessageProperties.ts | 4 ++- 7 files changed, 50 insertions(+), 19 deletions(-) diff --git a/src/api/routes/webhooks/#webhook_id/#token/index.ts b/src/api/routes/webhooks/#webhook_id/#token/index.ts index 538ee181f..49c47cca2 100644 --- a/src/api/routes/webhooks/#webhook_id/#token/index.ts +++ b/src/api/routes/webhooks/#webhook_id/#token/index.ts @@ -95,7 +95,7 @@ router.post( }, }), async (req: Request, res: Response) => { - const { wait, thread_id } = req.query; + const { wait } = req.query; if (!wait) return res.status(204).send(); const { webhook_id, token } = req.params; diff --git a/src/api/routes/webhooks/#webhook_id/index.ts b/src/api/routes/webhooks/#webhook_id/index.ts index cc8c03865..7d528dbfa 100644 --- a/src/api/routes/webhooks/#webhook_id/index.ts +++ b/src/api/routes/webhooks/#webhook_id/index.ts @@ -15,6 +15,7 @@ router.get( }, }), async (req: Request, res: Response) => { + // TODO: Permission check const { webhook_id } = req.params; const webhook = await Webhook.findOneOrFail({ where: { id: webhook_id }, diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts index 186165066..0a20fbc86 100644 --- a/src/api/util/handlers/Message.ts +++ b/src/api/util/handlers/Message.ts @@ -42,6 +42,7 @@ import { MessageCreateSchema, EmbedCache, handleFile, + Permissions, } from "@spacebar/util"; import { HTTPError } from "lambert-server"; import { In } from "typeorm"; @@ -95,15 +96,16 @@ export async function handleMessage(opts: MessageOptions): Promise { }); } - let permission: any; + let permission: undefined | Permissions; if (opts.webhook_id) { message.webhook = await Webhook.findOneOrFail({ where: { id: opts.webhook_id }, }); - message.author = (await User.findOne({ - where: { id: opts.webhook_id }, - })) || undefined; + message.author = + (await User.findOne({ + where: { id: opts.webhook_id }, + })) || undefined; if (!message.author) { message.author = User.create({ @@ -132,9 +134,15 @@ export async function handleMessage(opts: MessageOptions): Promise { } if (opts.avatar_url) { const avatarData = await fetch(opts.avatar_url); - const base64 = await avatarData.buffer().then((x) => x.toString("base64")); + const base64 = await avatarData + .buffer() + .then((x) => x.toString("base64")); - const dataUri = "data:" + avatarData.headers.get("content-type") + ";base64," + base64; + const dataUri = + "data:" + + avatarData.headers.get("content-type") + + ";base64," + + base64; message.avatar = await handleFile( `/avatars/${opts.webhook_id}`, @@ -219,14 +227,18 @@ export async function handleMessage(opts: MessageOptions): Promise { const role = await Role.findOneOrFail({ where: { id: mention, guild_id: channel.guild_id }, }); - if (role.mentionable || (opts.webhook_id || permission.has("MANAGE_ROLES"))) { + if ( + role.mentionable || + opts.webhook_id || + permission?.has("MANAGE_ROLES") + ) { mention_role_ids.push(mention); } }, ), ); - if (opts.webhook_id || permission.has("MENTION_EVERYONE")) { + if (opts.webhook_id || permission?.has("MENTION_EVERYONE")) { mention_everyone = !!content.match(EVERYONE_MENTION) || !!content.match(HERE_MENTION); diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index 86238e532..6f7120231 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts @@ -241,7 +241,7 @@ export class Message extends BaseClass { sticker_items: this.sticker_items ?? undefined, message_reference: this.message_reference ?? undefined, author: { - ...this.author?.toPublicUser() ?? undefined, + ...(this.author?.toPublicUser() ?? undefined), // Webhooks username: this.username ?? this.author?.username, avatar: this.avatar ?? this.author?.avatar, diff --git a/src/util/migration/mariadb/1721298824927-webhookMessageProperties.ts b/src/util/migration/mariadb/1721298824927-webhookMessageProperties.ts index ccbe689ad..775847e0b 100644 --- a/src/util/migration/mariadb/1721298824927-webhookMessageProperties.ts +++ b/src/util/migration/mariadb/1721298824927-webhookMessageProperties.ts @@ -1,15 +1,23 @@ import { MigrationInterface, QueryRunner } from "typeorm"; -export class WebhookMessageProperties1721298824927 implements MigrationInterface { +export class WebhookMessageProperties1721298824927 + implements MigrationInterface +{ name = "WebhookMessageProperties1721298824927"; public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query("ALTER TABLE `messages` ADD `username` text NULL"); - await queryRunner.query("ALTER TABLE `messages` ADD `avatar` text NULL"); + await queryRunner.query( + "ALTER TABLE `messages` ADD `username` text NULL", + ); + await queryRunner.query( + "ALTER TABLE `messages` ADD `avatar` text NULL", + ); } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query("ALTER TABLE `messages` DROP COLUMN `username`"); + await queryRunner.query( + "ALTER TABLE `messages` DROP COLUMN `username`", + ); await queryRunner.query("ALTER TABLE `messages` DROP COLUMN `avatar`"); } } diff --git a/src/util/migration/mysql/1721298824927-webhookMessageProperties.ts b/src/util/migration/mysql/1721298824927-webhookMessageProperties.ts index ccbe689ad..775847e0b 100644 --- a/src/util/migration/mysql/1721298824927-webhookMessageProperties.ts +++ b/src/util/migration/mysql/1721298824927-webhookMessageProperties.ts @@ -1,15 +1,23 @@ import { MigrationInterface, QueryRunner } from "typeorm"; -export class WebhookMessageProperties1721298824927 implements MigrationInterface { +export class WebhookMessageProperties1721298824927 + implements MigrationInterface +{ name = "WebhookMessageProperties1721298824927"; public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query("ALTER TABLE `messages` ADD `username` text NULL"); - await queryRunner.query("ALTER TABLE `messages` ADD `avatar` text NULL"); + await queryRunner.query( + "ALTER TABLE `messages` ADD `username` text NULL", + ); + await queryRunner.query( + "ALTER TABLE `messages` ADD `avatar` text NULL", + ); } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query("ALTER TABLE `messages` DROP COLUMN `username`"); + await queryRunner.query( + "ALTER TABLE `messages` DROP COLUMN `username`", + ); await queryRunner.query("ALTER TABLE `messages` DROP COLUMN `avatar`"); } } diff --git a/src/util/migration/postgres/1721298824927-webhookMessageProperties.ts b/src/util/migration/postgres/1721298824927-webhookMessageProperties.ts index 46c507d43..bd603f10f 100644 --- a/src/util/migration/postgres/1721298824927-webhookMessageProperties.ts +++ b/src/util/migration/postgres/1721298824927-webhookMessageProperties.ts @@ -1,6 +1,8 @@ import { MigrationInterface, QueryRunner } from "typeorm"; -export class WebhookMessageProperties1721298824927 implements MigrationInterface { +export class WebhookMessageProperties1721298824927 + implements MigrationInterface +{ name = "WebhookMessageProperties1721298824927"; public async up(queryRunner: QueryRunner): Promise { From 59c42c950b76ed99b3349ff64d98f979c75b2701 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Thu, 18 Jul 2024 13:31:04 +0200 Subject: [PATCH 04/41] try manual conflict resolving --- src/util/entities/Message.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index 6f7120231..4a1ed5b42 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts @@ -218,6 +218,9 @@ export class Message extends BaseClass { @Column({ type: "simple-json", nullable: true }) components?: MessageComponent[]; + @Column({ type: "simple-json", nullable: true }) + poll?: Poll; + @Column({ nullable: true }) username?: string; From 15a2e57b05223f93361fe01bb3bf42e90cea3483 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Thu, 18 Jul 2024 14:42:22 +0200 Subject: [PATCH 05/41] Prettier & fix merge util/handlers/Message --- src/api/util/handlers/Message.ts | 58 +++++++++++++++++--------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts index 9b2679282..461cddb45 100644 --- a/src/api/util/handlers/Message.ts +++ b/src/api/util/handlers/Message.ts @@ -43,16 +43,12 @@ import { //CHANNEL_MENTION, USER_MENTION, Webhook, - Attachment, - Config, - Sticker, - MessageCreateSchema, - EmbedCache, + handleFile, + Permissions, } from "@spacebar/util"; import { HTTPError } from "lambert-server"; import { In } from "typeorm"; -import { EmbedHandlers } from "@spacebar/api"; -import * as Sentry from "@sentry/node"; +import fetch from "node-fetch"; const allow_empty = false; // TODO: check webhook, application, system author, stickers // TODO: embed gifs/videos/images @@ -167,28 +163,36 @@ export async function handleMessage(opts: MessageOptions): Promise { message.member = permission.cache.member; } - if (opts.tts) permission.hasThrow("SEND_TTS_MESSAGES"); - if (opts.message_reference) { - permission.hasThrow("READ_MESSAGE_HISTORY"); - // code below has to be redone when we add custom message routing - if (message.guild_id !== null) { - const guild = await Guild.findOneOrFail({ - where: { id: channel.guild_id }, - }); - if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) { - if (opts.message_reference.guild_id !== channel.guild_id) - throw new HTTPError( - "You can only reference messages from this guild", - ); - if (opts.message_reference.channel_id !== opts.channel_id) - throw new HTTPError( - "You can only reference messages from this channel", - ); + if (opts.tts) permission.hasThrow("SEND_TTS_MESSAGES"); + if (opts.message_reference) { + permission.hasThrow("READ_MESSAGE_HISTORY"); + // code below has to be redone when we add custom message routing + if (message.guild_id !== null) { + const guild = await Guild.findOneOrFail({ + where: { id: channel.guild_id }, + }); + if (!opts.message_reference.guild_id) + opts.message_reference.guild_id = channel.guild_id; + if (!opts.message_reference.channel_id) + opts.message_reference.channel_id = opts.channel_id; + + if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) { + if (opts.message_reference.guild_id !== channel.guild_id) + throw new HTTPError( + "You can only reference messages from this guild", + ); + if (opts.message_reference.channel_id !== opts.channel_id) + throw new HTTPError( + "You can only reference messages from this channel", + ); + } + + message.message_reference = opts.message_reference; } + /** Q: should be checked if the referenced message exists? ANSWER: NO + otherwise backfilling won't work **/ + message.type = MessageType.REPLY; } - /** Q: should be checked if the referenced message exists? ANSWER: NO - otherwise backfilling won't work **/ - message.type = MessageType.REPLY; } // TODO: stickers/activity From 873107f90d44f907f5964b8c0a95261224a74759 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:16:50 +0200 Subject: [PATCH 06/41] Perms for GET webhook, url property --- assets/openapi.json | Bin 604064 -> 604136 bytes assets/schemas.json | Bin 22032984 -> 22041912 bytes .../webhooks/#webhook_id/#token/index.ts | 9 +++-- src/api/routes/webhooks/#webhook_id/index.ts | 34 +++++++++++++----- src/api/util/handlers/Message.ts | 1 - src/util/entities/Webhook.ts | 8 +++-- 6 files changed, 37 insertions(+), 15 deletions(-) diff --git a/assets/openapi.json b/assets/openapi.json index 9bcb54a53686b0b09ffc6b43f05d4ade613597f3..5cf313273185976af200242369f0c2d3c2ff14c9 100644 GIT binary patch delta 132 zcmZ4RUggDmm4+>hA7ZEP4`Yd){vnp}>U8-u7WU~APBM#4UoXYTG5wt>qr~)v<%~*` z9bTJF|G%4&XZrKqjP}zN%2*VqZ#c~8*KVA^2*gZ4%)H$=fhE#_TOlzoMWG}=J2g*l hav`(Gc7cB^a*XZ!C$a!BD-g2*G5hxY6FD9(1po;0G`#=- delta 154 zcmaFyUS+|1m4+>hA7ZCZ5MpwjY#`!3yAOW%ok_#HS z&>#mziWZsixdgP`*s)yOQEX?l1p%2U3`G=TwTs4~8nm5u4&Wcq-5;`667DD8@80|D z^WtCkuiCVo`P8Q95=q-$^VQ)Lsynd!3Y7~sE2wUFoP>US@AFx-dEmN?9={a0o(|<3 zl^~D!J8$7mr_F%VX;#uS6h?J+izTI3tUtOUT9bq#IVzU`l}ndyLSgh6*xGW+3>hdh zY&1wo#=7 z1o~2a-bP}z7k#1jqF)@+q?a@RyrhA_NVTSEwnxOBQfWCAqZSA1Z$EJ zrKq-W4b~yhU>&MQO6ps~#WY`SNTB}uC9%{kSLA9;lu4;McSR<*bCVW!ZVGEk#aUs$ z3HsKEmB_hgTp9)AQk~~6gc@53^f}R>&snw>lP`un>v#p8b;K0mOg8wB#1awLp^SwN zWn4OPG-c$^C^<`oDDprB7VzmV~=~0GBxZS(f!0ug% z>K(X(m~XEI^X+x2-y*r)a|3MmeC<&tPO`3SBh;0>j_Nm-9Pz*#ARgFcjV$9j|0G!F zpR7TaqN^kYx=J>CBe9siREX)*TW|xFEAdT-65kB$lI&8v1zd`^s*om2d*6i8-fe$H zW-@PaCgd&N(S%e>FMJEq3$qfDOY-ynKfv?;o%4`t8GhN2;g^HOZWial&Eh|H;o5*3 z>D>(@y?JjUO&V4CU{vi%L>9oi@NMue+$0);kxRbt;nIA zTT}$OMep_?O;&UlLq&H<4sN#P;#B(~PF1>W8*U2fD(9f9ylenxk{x&|MBMX%a(F&) zz!SUsnq;%o2FOw$G9aU5x}ZD2H(^?+k}W3>D#7T=Xv-}ujme+rQ$tvrMy{7kJuc;vl zr$$$qnxypS0(%$gUU5c4+ppQpp_O0HaWm?aa$5MVLcw-&KGdu`A)!V20nSt&>AQfM zPy6`L=CQL2seHpy4|;sIr-)wlbXvromCv5yU3!+?Ke0<-*ZxWkH zi!Ss}{ho;W|4HmR^<9HrA|@*8E@)>>{^}GbqQ`%6U=QvN45XbC&hw|ubVkM~C!cdP zHrv=!_ru2`XmjLk1s(FvcjQ=)D|*@a)jpjhG3ZHEiwhIj9{zzaBH?GE5zM0o0+*r~oT1luZv3F452e ziH25xOitMW=GHeC;np{oPGX!gjGdXFJsAFhDFwrN%iEy0{BmdpVwPuE(&)#KH2OCI z8QV7PC$LT1ej91B^pF6hhpu9=rlC)vY3Q@oUu%+MS8D8aja{u>|8XrMA@vq6t*ism z%5Hp#jI5ylb13NlLW@klRmeZVD&(dN(*(SQo*(*3$|W0aL9*dXB+D@t&JT5_BQF}K zBvZp~Luy!eGIDLUs_Fr&s$LDI6Ihh&m?-ATOn0En)HfF?`7r$7@G$)AK};Fh!X>Zl z2YKbd2AmpYn22i``46;=4A$cIkdv&GI}$krMm+JLVYg;t))5_TSY@+T`*Zr%~z})=-7{B&eW2=#$F+& zy0c=JsZWI-GUe2pL3>4<5T%<<#51@~{#)qeA9`gW+L;wJ>d7OBdh(qD8ERwP7&OLx z|7|)>t*6lpJ&lhyAl0&;{ui>Jjwd5A=lCb!9RD;4i9wwOpl(8gnW|VfPF?&*PdtO@ ziRTNDlIIeBf^!M~^T%P+l796+gJ1nGN*sHz`g%$3W@1k%;ye{BT+kvJm3fm3^Get$?j r5%z~9kWOU!>kqCZLjA#43F;ipTlkj2RYYhRTuruM{FYf}*zEg1s5&i2 delta 7267 zcmb7JYgAO%6JlEjcE(UwqmV_rh1}F zxe}I~nj!;=1yKPBxKW7LR9lTz!6wmI!n0CpQ^6u()FSrI(6!oMVfV+ZdE9UB^X=!^ z=S*|W=QiIwzGhRnUIm^9f6V3{D`7dYf-$W%d&*WG)NXT*HjdWm> z1q^JOV!df{^f4m~*Lx}Hf!3*TpjEOeb#f7LfkZniNVKzFxDkV9p7yU~)KM=5N4-r5 zYJmxn&Sn{OHp|me@T=UG+rn+RvR;c3IjH9@>FxSZM$oNb4_m=u4T=m^%N#*1o4yQX zEgjdr1>-uW4{>O#CTFl}av4DIE-|u`DwQ+4));Ik)+r^-_E#3ufDcMoZNc*8Q(TG@ zr2cGaU-nEx!UZ{rE#@}68LocqLS`_|!PC8XnHxvzE(ojwXq1@~}2>**{F{dG1KWA0?rC20=%xm+g8#*sk~Ag+u2y2S9FfU=)H0Z)74`LL(LPAyTnmK5l`R z84KZMMvw}HF424@p;e8+P}R67JZ*A+@jpnkLCz;Cw3h-fHcE}z{Q4`+8Kgl$8)aa%_s zZ0lI-gVXIC@Gdw9Bn`iZ(p^-v9*T;RacN8!_iup3{Trv^d~zc+6>fy4g`m=x?mMc5 zeMi%Map(oTI#|$~VZ^-vC2gf9!CmRBKwp8JKk{bLTqy}$0 zgVTNR(ED)k(2o-Is92^A{20;(elioKn~7*^Dhta6S=e@GlnAXU`~YeScihJ%BI)}n zkiMT~<8(g_-U&~G^Ad1wync8WTtEDa2RJ4kUAw`f>z6?~lp7x{%7;gb_HIU{mZ5E< zKe48Fjuyb3qxD>=Ly&q=d+L2?(0dNdC7`X*yLnouq$v*lP#fL_G zcL*ZBJ3J4u(~He(DDnslMIJ3e@n)iK36-5F1lb9`9F@nczU-nk>++V&G0j{dOxdS% zXZ10~JLr&}gCV`$^9_^xK%7XD@;8Af8G8CeDV#oW@(Yx|>9Da3hK+^@ly0{7#TjcV#8iL~b7}~YF=8Zk zKH^lYeXKK-T?-6!HxJA`XHHAmvLJ~m0{%?=#8!J(4%o3-fFTts3xQ$|DmN1q!gR%5- zXx8L)`kzU+<>eJ^P|T?2_i{Lo6RlnpGh{R>d{kzGb#R82KE8k>53-_?RTv zVCf&ER1$pyB+gDR@N0!7u=A*eiM;;?Q>+h0F`_SX-Q z^L_5RN07Vj&t0fAVt*}FxS3j52s2bZS;4rP4ol{#u{CcbO!K|c$=t_k@8nE;D0F>) zhWVE+%~Y5FUIg5$?tpvM-=?7QgVv69LhV@BN!-|SKdA@qC-r8a?4eNI2T=W&l{hB* z#~#D}vHmY{x;x?rpdU?&-@^w|Tyx188j^K^5Fkf-y;R!3^XTq2}4_>c&cO}wYXmk6E`KjMSh&3V&F zR%qK8Kt4e=Zr*f?6^1UnO@5BLYBFfEgNTBE!7U)d3vMCl#<4O<3EJ#n;()`CURgwh z(JLXu4`+qxcqLTZE+)9kAwDlNOpIFQ5;7AP1&^k1B6u_{B?UM>UeaYm@RBYk^H6w} zEt&7c%Eun7i#>Kv{1-8}K}_Rv?PV1cDx~hr)6&+F6142DQieOE)1kLh6toKmf%i?{5ql`!a5R9^axTP87*w~{2O1| BY*7FJ diff --git a/src/api/routes/webhooks/#webhook_id/#token/index.ts b/src/api/routes/webhooks/#webhook_id/#token/index.ts index 49c47cca2..6d1449ebf 100644 --- a/src/api/routes/webhooks/#webhook_id/#token/index.ts +++ b/src/api/routes/webhooks/#webhook_id/#token/index.ts @@ -20,7 +20,7 @@ const router = Router(); router.get( "/", route({ - description: "Returns a webhook object for the given id.", + description: "Returns a webhook object for the given id and token.", responses: { 200: { body: "APIWebhook", @@ -45,7 +45,12 @@ router.get( throw DiscordApiErrors.INVALID_WEBHOOK_TOKEN_PROVIDED; } - return res.json(webhook); + const instanceUrl = + Config.get().api.endpointPublic || "http://localhost:3001"; + return res.json({ + ...webhook, + url: instanceUrl + "/webhooks/" + webhook.id + "/" + webhook.token, + }); }, ); diff --git a/src/api/routes/webhooks/#webhook_id/index.ts b/src/api/routes/webhooks/#webhook_id/index.ts index 7d528dbfa..98faaac16 100644 --- a/src/api/routes/webhooks/#webhook_id/index.ts +++ b/src/api/routes/webhooks/#webhook_id/index.ts @@ -1,5 +1,10 @@ import { route } from "@spacebar/api"; -import { Webhook } from "@spacebar/util"; +import { + Config, + DiscordApiErrors, + getPermission, + Webhook, +} from "@spacebar/util"; import { Request, Response, Router } from "express"; const router = Router(); @@ -15,18 +20,29 @@ router.get( }, }), async (req: Request, res: Response) => { - // TODO: Permission check const { webhook_id } = req.params; const webhook = await Webhook.findOneOrFail({ where: { id: webhook_id }, - relations: [ - "user", - "guild", - "source_guild", - "application" /*"source_channel"*/, - ], + relations: ["channel", "guild", "application", "user"], + }); + + if (webhook.guild_id) { + const permission = await getPermission( + req.user_id, + webhook.guild_id, + ); + + if (!permission.has("MANAGE_WEBHOOKS")) + throw DiscordApiErrors.UNKNOWN_WEBHOOK; + } else if (webhook.user_id != req.user_id) + throw DiscordApiErrors.UNKNOWN_WEBHOOK; + + const instanceUrl = + Config.get().api.endpointPublic || "http://localhost:3001"; + return res.json({ + ...webhook, + url: instanceUrl + "/webhooks/" + webhook.id + "/" + webhook.token, }); - return res.json(webhook); }, ); diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts index 461cddb45..f037417a5 100644 --- a/src/api/util/handlers/Message.ts +++ b/src/api/util/handlers/Message.ts @@ -149,7 +149,6 @@ export async function handleMessage(opts: MessageOptions): Promise { `/avatars/${opts.webhook_id}`, dataUri as string, ); - console.log(message.avatar); message.author.avatar = message.avatar; } } else { diff --git a/src/util/entities/Webhook.ts b/src/util/entities/Webhook.ts index b7fba53a1..8b1585ad9 100644 --- a/src/util/entities/Webhook.ts +++ b/src/util/entities/Webhook.ts @@ -38,20 +38,20 @@ export class Webhook extends BaseClass { name: string; @Column({ nullable: true }) - avatar?: string; + avatar: string; @Column({ nullable: true }) token?: string; @Column({ nullable: true }) @RelationId((webhook: Webhook) => webhook.guild) - guild_id: string; + guild_id?: string; @JoinColumn({ name: "guild_id" }) @ManyToOne(() => Guild, { onDelete: "CASCADE", }) - guild: Guild; + guild?: Guild; @Column({ nullable: true }) @RelationId((webhook: Webhook) => webhook.channel) @@ -92,4 +92,6 @@ export class Webhook extends BaseClass { onDelete: "CASCADE", }) source_guild: Guild; + + url?: string; } From adc3474216af90df8ff54089eb6d2ad67a95b533 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:35:10 +0200 Subject: [PATCH 07/41] Consistent relations, url property, src channel --- assets/openapi.json | Bin 604136 -> 604475 bytes assets/schemas.json | Bin 22041912 -> 22078920 bytes .../routes/channels/#channel_id/webhooks.ts | 17 +++++++++++----- src/api/routes/guilds/#guild_id/webhooks.ts | 19 ++++++++++++------ .../webhooks/#webhook_id/#token/index.ts | 10 ++++++++- src/api/routes/webhooks/#webhook_id/index.ts | 11 ++++++++-- src/util/entities/Webhook.ts | 16 ++++++++++++--- 7 files changed, 56 insertions(+), 17 deletions(-) diff --git a/assets/openapi.json b/assets/openapi.json index 5cf313273185976af200242369f0c2d3c2ff14c9..be317720b104de104adb5083e0e2c4368e1b094d 100644 GIT binary patch delta 197 zcmaFyUS;hA7ZET#xk-`H=4_o0%2WpWhvkOA(rtR<8+63MtO+f#EHyGlM`52 zr`M%0Nl#z;f>C^WLkN)j?l7a@bp1wV_V$VdMj&PaV&?4?2`mQM(>q_YYbYt?7b%qF zDhA7ZEb&u4avY|{jP{cg zSXifjsALwK-oKogZMwrMM!)F^l`QP-4T+3E%ml>D+Zz&D474W~GK*{v*vBfv*sk5j T0>rF9%m&2l+qL^R1Xcq8H2o|I diff --git a/assets/schemas.json b/assets/schemas.json index cd3a018f0b34691e45472a154bb8f5e97b1bfe77..aecc76a210318ca1858f9baaa6ab1145bd4a0c5e 100644 GIT binary patch delta 12256 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 diff --git a/src/api/routes/channels/#channel_id/webhooks.ts b/src/api/routes/channels/#channel_id/webhooks.ts index 4c1ccbdf2..7533fcebb 100644 --- a/src/api/routes/channels/#channel_id/webhooks.ts +++ b/src/api/routes/channels/#channel_id/webhooks.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -53,13 +53,20 @@ router.get( where: { channel_id }, relations: [ "user", + "channel", + "source_channel", "guild", "source_guild", - "application" /*"source_channel"*/, + "application", ], }); - return res.json(webhooks); + const instanceUrl = + Config.get().api.endpointPublic || "http://localhost:3001"; + return res.json(webhooks.map(webhook => ({ + ...webhook, + url: instanceUrl + "/webhooks/" + webhook.id + "/" + webhook.token, + }))); }, ); diff --git a/src/api/routes/guilds/#guild_id/webhooks.ts b/src/api/routes/guilds/#guild_id/webhooks.ts index a2ef7d699..60c9818ab 100644 --- a/src/api/routes/guilds/#guild_id/webhooks.ts +++ b/src/api/routes/guilds/#guild_id/webhooks.ts @@ -1,23 +1,23 @@ /* 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 { route } from "@spacebar/api"; -import { Webhook } from "@spacebar/util"; +import { Config, Webhook } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router = Router(); @@ -39,13 +39,20 @@ router.get( where: { guild_id }, relations: [ "user", + "channel", + "source_channel", "guild", "source_guild", - "application" /*"source_channel"*/, + "application", ], }); - return res.json(webhooks); + const instanceUrl = + Config.get().api.endpointPublic || "http://localhost:3001"; + return res.json(webhooks.map(webhook => ({ + ...webhook, + url: instanceUrl + "/webhooks/" + webhook.id + "/" + webhook.token, + }))); }, ); diff --git a/src/api/routes/webhooks/#webhook_id/#token/index.ts b/src/api/routes/webhooks/#webhook_id/#token/index.ts index 6d1449ebf..8e0ad0dd3 100644 --- a/src/api/routes/webhooks/#webhook_id/#token/index.ts +++ b/src/api/routes/webhooks/#webhook_id/#token/index.ts @@ -34,7 +34,14 @@ router.get( where: { id: webhook_id, }, - relations: ["channel", "guild", "application"], + relations: [ + "user", + "channel", + "source_channel", + "guild", + "source_guild", + "application", + ], }); if (!webhook) { @@ -65,6 +72,7 @@ const messageUpload = multer({ }); // max upload 50 mb // https://discord.com/developers/docs/resources/webhook#execute-webhook +// TODO: GitHub/Slack compatible hooks router.post( "/", messageUpload.any(), diff --git a/src/api/routes/webhooks/#webhook_id/index.ts b/src/api/routes/webhooks/#webhook_id/index.ts index 98faaac16..c10bf2247 100644 --- a/src/api/routes/webhooks/#webhook_id/index.ts +++ b/src/api/routes/webhooks/#webhook_id/index.ts @@ -11,7 +11,7 @@ const router = Router(); router.get( "/", route({ - description: "Returns a webhook object for the given id.", + description: "Returns a webhook object for the given id. Requires the MANAGE_WEBHOOKS permission or to be the owner of the webhook.", responses: { 200: { body: "APIWebhook", @@ -23,7 +23,14 @@ router.get( const { webhook_id } = req.params; const webhook = await Webhook.findOneOrFail({ where: { id: webhook_id }, - relations: ["channel", "guild", "application", "user"], + relations: [ + "user", + "channel", + "source_channel", + "guild", + "source_guild", + "application", + ], }); if (webhook.guild_id) { diff --git a/src/util/entities/Webhook.ts b/src/util/entities/Webhook.ts index 8b1585ad9..9539d6e8e 100644 --- a/src/util/entities/Webhook.ts +++ b/src/util/entities/Webhook.ts @@ -85,13 +85,23 @@ export class Webhook extends BaseClass { @Column({ nullable: true }) @RelationId((webhook: Webhook) => webhook.guild) - source_guild_id: string; + source_guild_id?: string; @JoinColumn({ name: "source_guild_id" }) @ManyToOne(() => Guild, { onDelete: "CASCADE", }) - source_guild: Guild; + source_guild?: Guild; - url?: string; + @Column({ nullable: true }) + @RelationId((webhook: Webhook) => webhook.channel) + source_channel_id: string; + + @JoinColumn({ name: "source_channel_id" }) + @ManyToOne(() => Channel, { + onDelete: "CASCADE", + }) + source_channel: Channel; + + url: string; } From 4b1e9ba851a38cd36f6c5b362b51ecf39bad6ef2 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:36:00 +0200 Subject: [PATCH 08/41] Prettier --- src/api/routes/channels/#channel_id/webhooks.ts | 15 +++++++++++---- src/api/routes/guilds/#guild_id/webhooks.ts | 15 +++++++++++---- src/api/routes/webhooks/#webhook_id/index.ts | 3 ++- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/api/routes/channels/#channel_id/webhooks.ts b/src/api/routes/channels/#channel_id/webhooks.ts index 7533fcebb..2060760d5 100644 --- a/src/api/routes/channels/#channel_id/webhooks.ts +++ b/src/api/routes/channels/#channel_id/webhooks.ts @@ -63,10 +63,17 @@ router.get( const instanceUrl = Config.get().api.endpointPublic || "http://localhost:3001"; - return res.json(webhooks.map(webhook => ({ - ...webhook, - url: instanceUrl + "/webhooks/" + webhook.id + "/" + webhook.token, - }))); + return res.json( + webhooks.map((webhook) => ({ + ...webhook, + url: + instanceUrl + + "/webhooks/" + + webhook.id + + "/" + + webhook.token, + })), + ); }, ); diff --git a/src/api/routes/guilds/#guild_id/webhooks.ts b/src/api/routes/guilds/#guild_id/webhooks.ts index 60c9818ab..47e199475 100644 --- a/src/api/routes/guilds/#guild_id/webhooks.ts +++ b/src/api/routes/guilds/#guild_id/webhooks.ts @@ -49,10 +49,17 @@ router.get( const instanceUrl = Config.get().api.endpointPublic || "http://localhost:3001"; - return res.json(webhooks.map(webhook => ({ - ...webhook, - url: instanceUrl + "/webhooks/" + webhook.id + "/" + webhook.token, - }))); + return res.json( + webhooks.map((webhook) => ({ + ...webhook, + url: + instanceUrl + + "/webhooks/" + + webhook.id + + "/" + + webhook.token, + })), + ); }, ); diff --git a/src/api/routes/webhooks/#webhook_id/index.ts b/src/api/routes/webhooks/#webhook_id/index.ts index c10bf2247..59fdb76df 100644 --- a/src/api/routes/webhooks/#webhook_id/index.ts +++ b/src/api/routes/webhooks/#webhook_id/index.ts @@ -11,7 +11,8 @@ const router = Router(); router.get( "/", route({ - description: "Returns a webhook object for the given id. Requires the MANAGE_WEBHOOKS permission or to be the owner of the webhook.", + description: + "Returns a webhook object for the given id. Requires the MANAGE_WEBHOOKS permission or to be the owner of the webhook.", responses: { 200: { body: "APIWebhook", From 5a20924477eae54c7353c0aecf7a56f4e052e3b5 Mon Sep 17 00:00:00 2001 From: aintDatCap Date: Wed, 24 Jul 2024 19:06:12 +0200 Subject: [PATCH 09/41] Implemented proposal described in the issue #1122 --- src/api/routes/guilds/#guild_id/bulk-ban.ts | 5 ++++- .../types/subconfigurations/limits/GlobalRateLimits.ts | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/api/routes/guilds/#guild_id/bulk-ban.ts b/src/api/routes/guilds/#guild_id/bulk-ban.ts index f544103a5..10505282a 100644 --- a/src/api/routes/guilds/#guild_id/bulk-ban.ts +++ b/src/api/routes/guilds/#guild_id/bulk-ban.ts @@ -27,6 +27,7 @@ import { } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; +import { Config } from "@spacebar/util"; const router: Router = Router(); @@ -52,7 +53,9 @@ router.post( const userIds: Array = req.body.user_ids; if (!userIds) throw new HTTPError("The user_ids array is missing", 400); - if (userIds.length > 200) + + const bulkBanLimit = Config.get().limits.absoluteRate.bulkBan; + if (bulkBanLimit.enabled && userIds.length > bulkBanLimit.limit) throw new HTTPError( "The user_ids array must be between 1 and 200 in length", 400, diff --git a/src/util/config/types/subconfigurations/limits/GlobalRateLimits.ts b/src/util/config/types/subconfigurations/limits/GlobalRateLimits.ts index 835d71933..5a8a0d44b 100644 --- a/src/util/config/types/subconfigurations/limits/GlobalRateLimits.ts +++ b/src/util/config/types/subconfigurations/limits/GlobalRateLimits.ts @@ -27,6 +27,11 @@ export class GlobalRateLimits { window: 60 * 1000, enabled: true, }; + bulkBan: GlobalRateLimit = { + limit: 200, + window: 0, + enabled: true, + }; } export class GlobalRateLimit { From e4a14d659a8c27d222d06711e1ba23b43d3ad0e2 Mon Sep 17 00:00:00 2001 From: aintDatCap Date: Wed, 24 Jul 2024 19:22:40 +0200 Subject: [PATCH 10/41] Refactored max limit for bulk-bans from AbsoluteRateLimits to GuildLimits as suggested by @devtomatocake --- assets/schemas.json | Bin 19589397 -> 19613632 bytes src/api/routes/guilds/#guild_id/bulk-ban.ts | 3 +-- .../limits/GlobalRateLimits.ts | 5 ----- .../subconfigurations/limits/GuildLimits.ts | 1 + 4 files changed, 2 insertions(+), 7 deletions(-) diff --git a/assets/schemas.json b/assets/schemas.json index 00f86b3604f19db3f1eb55db5e1432de30d7d2a5..2deec550758dc241fc8983fcf7a427b88c248967 100644 GIT binary patch delta 9149 zcmbuDeN@zC7RS$n0}LQAAcH95fJnqhgc7TU2&f2A2BHNfj$)x$E$Aj_yT(fa-U{KT zqCC3Aj;ui{i{-9zM@47SR$H`~6+`W1Wld!?FPBIQJ?%>toNqC`GhqsB7 z5%l_L2p{bA^X=paY{>TAkUgq3+pmF>L|wM8GhA!%BeZy}!M}=Q0rvO!6Qgl|Pe3t8 zK<;dAa)vdLV~DZ1CNfZtV{~*#CA}yR(u;JMUNr77-%Hw&agcUIk7-AO#)ojdNK$cu z!>+-^cs%SHV$w6DV8^@=Vgl}%7g{=rBe1YJ6c#o=gbSO)nmL7q*>}Q-hjI3u@Gn>? z7~>3JjGKtYxQLH9!xWa3M-Y$TlJdwhRtoylk)S{QDC$o~J$ zC$gU536#`7110s(Vo7~w1k1to%b9Tfau!~{ywJ)M2!9T)hcCkG;n{mRvq)-EpI5>8 zRoTR1Jip5L2*<{tiacEG?kTr*t$0$F?kiT;mB-RfakYMew7R>GmODRl@|QV9d3u=` zKW(gBof7D^t$V6Q)-+AfN!#7+dReynq_5mGvLO7wk3KT0VRle$nX_B1HBBpbdIW1^ zyl+CV^nP6%By)b=Jz3hex=nR_3Y6W2d);MU#F&Y--81x1U^Ws`b7#YXy;q)mI+T-0%`EH@rNBm5j-p?+o1qFB8AT?t=W%;jNfM?=nCtL;;%2tG;Db!(pScV|8W6 z?+$jna+kC2$Qf4^wJdR&6DBvMJpM|p#-|X1^PPwh7@V();%v&Wv96FP!i{xne_@q3 zA>{XJuzj_bSckT+UX9|Y0I3jvz=V+XZ*v4pMiPrQcyD~sRa$iStRVxj~u zwUq8)37GGcg85Dvn(w@x5zHCysqt?>YJ54S##aP!7FO6@Q9)GV?urdztQ5T3H-J|= zpjZ2wdszYl-fzKx_uDw&UG*U=1$$-{*fTexJ+pcrYX$5wRYRAl2D?nk!>kl&s=JDsXO=r4GaDn>e2Z)1Mzr3}OWh0}d6*5{7Gg{2kIAcTJ zsywQe$7ZUurqvDJ;x$=1OErcLyOE}yueyjivgQ{N;CPgeCdB9lKa}JKj2%22*RKA7 zPQvyPf&Y7|Nx$Uyk)DntnKEsoi@z-Xa+-gw=M-OAuk#o!Km90Rlr%mNT9b*?o$IPy zykvEGvIgS%&BP&$>pwi5b#%elBY?4Oh_Oe1WNo!n2e?&v3~yCh4znEC+bv*kx1zoM zBir8$A8G#}H|+4PZx0>L-$-L z=2#gNx!HRGZuVZpo4sF@vPD{>zl7H4udp@x>vbFv$Y1w0@h{9@cL`Ynxowvqx2+p< z+y33c637sjAwztF8RE(ZEP=%2E0CCc6%&)M@dTo(u0d2)4@Onl+gJ`FY<7sSUB?L9 zjhgX{nGQb%eoNfMp8|V%DcEy*!Jcyq?K$5yvr_16{0=%B`>?a|Hm{Ae%z*XAf6#j4 z&Uns+z~I6OhM4b(AJ7of-~Sk=l1sPr!_qB3;?gYxdpLzK_4W$k4)f0s5I^Dk^LHCL zGB8#5Gw};f)!p0A5g01Rq-FPD(z5$FY1xAV92E#{dQcVGBrJq@i*qiR0`JNw4=qwNjJ*&QxVnRgj<%EG(O<#*5E6l1k&*@q$8#ur!{XAeM1bla9AC zWo|&Ipzx5e%nb+=6FEXkt1(Pacvx6kjp2fI`KIW0g$oJ>VbSfHD2@%^N20;Wz4%vZ CG^BI@ delta 3734 zcmZvfdpy-?9>-sg`<-*hxL-<2XGNx{JtK5QJ5;2kmM)9kQ??AFDW@oWw@nTm_1ol9 zskTX>Azz!}XjW=ts#Q%zwVItx#&+9PN-pzYUY+N8nLplNm(TOPJkR&JoYSZ{r+=`m zOg}AyRBM#_3pYRUdWSE4y%xJcp-h9KiG?z)O?Iq6s!PAx z@18B|5fh*fF#!e`6JU7Ekr4zc=mN<$q!=N|HjZKAjj8Ky8N<3;Cb;gF>2WUTfVCM6 zSc`DLT6~KWuEvPrYK%Evjj=e!#)I{zEhrOk{b|cw&ZFnKCGY#qBGdsw+@ROVlQIK) zoxFm_W)5`wP(G&HXy$`(rf%WQk5WfruSbY24A;-3%);S%?;x8GLuj}_O&`i^4Ah*n zQ1U?tbOVT-L-{pAq%>8%!c-V6ky1Xv!4luK>dNT;4eRoy%*C)SzdW@E6&||#Qv&dz z`@Bp}h)S9VQAzVLDrvzocALVllthaw<)XeCAut!m^1tA&wZwQx0BEsQ$DEn;1E6x3y} z!Mg0Vo~(y(DA7g)uiMsAev7Z$qUBC(%`%4=kU6YFnZtS&w-G1BuZKzT8*ozm#_4Q4 z2rl19`5gwA$F^__X3`f6CVg>e(idMimCaEa{Cg-3{sWc<%e>i8LTJZkZMd5&qin*v zxtn)!y`(CHb|heE#~*vxL}PBVUADk%m#sM4C9$9Df+RT+B*~wlBspn5n+@XXk|@a- zSNHhM zrkGj4<=!j`;N{*w@l}LN_d>Yz&loPvNEfeN}%*1DgPgJrD> zCpH;0iE}A=*d#u1fD`ET3Zaoo42{g!WcxUJH3PkxDF< zB732+b-$1DhWP?v=%vU;nWpWo3|VD0@>9qeAY;Eg+NWbT_Mf5|HbUAuEeGY6*JeV) zA(8FqU&7I=_9DUWgrhAT<)AbZMF>MD94&>xmG+h;)8CmAb77~*)>3%bKf@gMd;X1b z5%+u6eWrem=+*l!xsLKRzDus}2)8JUhSy#>ew|- zwA|JKmfIen<+g_@tO!~44=Il@tNvfe3ErBW;H~)>y*0l-#|aMtzlR5b|HcP_KU8o+ zM$QkAk@FwS$a%sE0$l|y@Vxzm(uJP4pVo42kFC4mvGtGm*t#djnQcaVY!Af8{ukq8 zpKIA3$(BLu)*hll>g%fpI$5O<{Rx{YI-lE zroYD2^fzxlX3LOuz5!WhAIdu4e#vDsc6|rNuKj52`kt@vg6ut9kPYAk+2DE3gC>IL z^FtVYe)wxnu;?4!XVFJg6tb5+*aA&rpg8KML8#Frs-u2dgqxaTQKlB5Mw_S>W$KW_ zY*@v;YdVA)U81^oO^@thPlF;{tWT&hAgXY&A?an0kRl+`kWgbpQ~{C3JE9V= zvL_j8EznJoTWn9LaUiPPVn@P!mS5~P_JKp#xkfx;^ad3kndI7!+>xqB)X76bxT(}L z5#-ip<{;6ULYayZEjKA!mO#HJXqPO1;z%roRE-orMf3EDgqlf2)jZvW=&DQk`OAT# zHOYlgGnuGblU&I(^?=20iWe7M2{luQ>cz#W#Ff)bh#OIukZD9^LL{U?t@*oY7|4GD D3FNyg diff --git a/src/api/routes/guilds/#guild_id/bulk-ban.ts b/src/api/routes/guilds/#guild_id/bulk-ban.ts index 10505282a..e5f7a0d8e 100644 --- a/src/api/routes/guilds/#guild_id/bulk-ban.ts +++ b/src/api/routes/guilds/#guild_id/bulk-ban.ts @@ -54,8 +54,7 @@ router.post( const userIds: Array = req.body.user_ids; if (!userIds) throw new HTTPError("The user_ids array is missing", 400); - const bulkBanLimit = Config.get().limits.absoluteRate.bulkBan; - if (bulkBanLimit.enabled && userIds.length > bulkBanLimit.limit) + if (userIds.length > Config.get().limits.guild.maxBulkBanUsers) throw new HTTPError( "The user_ids array must be between 1 and 200 in length", 400, diff --git a/src/util/config/types/subconfigurations/limits/GlobalRateLimits.ts b/src/util/config/types/subconfigurations/limits/GlobalRateLimits.ts index 5a8a0d44b..835d71933 100644 --- a/src/util/config/types/subconfigurations/limits/GlobalRateLimits.ts +++ b/src/util/config/types/subconfigurations/limits/GlobalRateLimits.ts @@ -27,11 +27,6 @@ export class GlobalRateLimits { window: 60 * 1000, enabled: true, }; - bulkBan: GlobalRateLimit = { - limit: 200, - window: 0, - enabled: true, - }; } export class GlobalRateLimit { diff --git a/src/util/config/types/subconfigurations/limits/GuildLimits.ts b/src/util/config/types/subconfigurations/limits/GuildLimits.ts index b64d94857..e77cf4242 100644 --- a/src/util/config/types/subconfigurations/limits/GuildLimits.ts +++ b/src/util/config/types/subconfigurations/limits/GuildLimits.ts @@ -21,5 +21,6 @@ export class GuildLimits { maxEmojis: number = 2000; maxMembers: number = 25000000; maxChannels: number = 65535; + maxBulkBanUsers: number = 200; maxChannelsInCategory: number = 65535; } From 4e0ac5274c041ac63ab3c38d71fb3f726fd27647 Mon Sep 17 00:00:00 2001 From: aintDatCap Date: Wed, 24 Jul 2024 20:07:08 +0200 Subject: [PATCH 11/41] Updated the openapi schema --- assets/openapi.json | Bin 582218 -> 584496 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/openapi.json b/assets/openapi.json index 2788cdb08bb2d9d7ee2a458cbee2a0f133cee203..6e719c2d9b07c292744063523b4a26cf93c3a34b 100644 GIT binary patch delta 548 zcmZvZO=uHQ6oq;B&D3co#z?dol3Mda7HZRKpe2YjF&kTyKo?pUB8eoZbPOX!T__l- z;6iMJPdo@B*}7^5iw_q<)CyLu?W(j1LZE_f#JX@J{*6V%f{U{`-{HdLdpQ%WEk*U9`gN zfT%WOBG(y!^^|WE*|&bwS1Bp22<_L~Gw00FOulq@3{NK6j@vsa&JqasQ3CgR$cLFY z$$vJ?!-D9FlN-qtGcXmWtzx{t13z|=E*_jUaHE@CSnMFb)O%?uz%%&*>?HFebc7-z zZN^Xz(J_?gSP}DI*l}*2E@96AZHIM*o4#=n3*vG#vUf}yIcgvs8`HJU1j2(!m@1#d>F>GltprKinfXK!A2d;7ZEj2pO6 z<>V$8+)8FjDxThD3GOA8ac&|JCg#nW^MC}2f`1Tb>ObOE4+XI*` zlub9d#HcxaeFP)t_W0vWzTyb}bRd7jbk1vxn$wb4__v*6TF$=Q`8m_m?E+_+-}8aU r4%W#NxVg5+Jz}054mKU+p~>rRE4Rm4vj8zG5VHX>`}R0%j!WwSlyGLA From 481cf38373037415594dd86695f94a3aeb17406e Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Sun, 28 Jul 2024 05:48:21 +0200 Subject: [PATCH 12/41] Fix sort order when using ?around --- .../routes/channels/#channel_id/messages/index.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts index 645c6db23..d4635b1d7 100644 --- a/src/api/routes/channels/#channel_id/messages/index.ts +++ b/src/api/routes/channels/#channel_id/messages/index.ts @@ -130,19 +130,25 @@ router.get( query.take = Math.floor(limit / 2); if (query.take != 0) { const [right, left] = await Promise.all([ - Message.find({ ...query, where: { id: LessThan(around) } }), Message.find({ ...query, - where: { id: MoreThanOrEqual(around) }, + where: { channel_id, id: LessThan(around) }, + }), + Message.find({ + ...query, + where: { channel_id, id: MoreThanOrEqual(around) }, + order: { timestamp: "ASC" }, }), ]); left.push(...right); - messages = left; + messages = left.sort( + (a, b) => a.timestamp.getTime() - b.timestamp.getTime(), + ); } else { query.take = 1; const message = await Message.findOne({ ...query, - where: { id: around }, + where: { channel_id, id: around }, }); messages = message ? [message] : []; } From b682f170b9e6156d7db66fc1527f451b734300db Mon Sep 17 00:00:00 2001 From: Cyber Date: Sun, 21 Jul 2024 20:00:14 +0200 Subject: [PATCH 13/41] fix: return an error on username change with no password --- src/api/routes/users/@me/index.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/api/routes/users/@me/index.ts b/src/api/routes/users/@me/index.ts index ad11a4281..95f572af8 100644 --- a/src/api/routes/users/@me/index.ts +++ b/src/api/routes/users/@me/index.ts @@ -120,7 +120,7 @@ router.patch( if (!body.password) throw FieldErrors({ password: { - message: req.t("auth:register.INVALID_PASSWORD"), + message: req.t("auth:login.INVALID_PASSWORD"), code: "INVALID_PASSWORD", }, }); @@ -162,6 +162,15 @@ router.patch( } } + if (!body.password) { + throw FieldErrors({ + password: { + message: req.t("auth:login.INVALID_PASSWORD"), + code: "INVALID_PASSWORD", + }, + }); + } + if (body.discriminator) { if ( await User.findOne({ From f047f93b77f04a574380851cda5f361e6efb2310 Mon Sep 17 00:00:00 2001 From: Cyber Date: Thu, 25 Jul 2024 19:41:56 +0200 Subject: [PATCH 14/41] fix: check for password only if `username` is present in body --- src/api/routes/users/@me/index.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/api/routes/users/@me/index.ts b/src/api/routes/users/@me/index.ts index 95f572af8..cddc3a087 100644 --- a/src/api/routes/users/@me/index.ts +++ b/src/api/routes/users/@me/index.ts @@ -160,15 +160,15 @@ router.patch( }, }); } - } - if (!body.password) { - throw FieldErrors({ - password: { - message: req.t("auth:login.INVALID_PASSWORD"), - code: "INVALID_PASSWORD", - }, - }); + if (!body.password) { + throw FieldErrors({ + password: { + message: req.t("auth:login.INVALID_PASSWORD"), + code: "INVALID_PASSWORD", + }, + }); + } } if (body.discriminator) { From a887608d30a728e677b5a8595c695fc55788f89c Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Fri, 19 Jul 2024 15:09:02 +0200 Subject: [PATCH 15/41] Fix msg component typings --- assets/openapi.json | Bin 582218 -> 601458 bytes assets/schemas.json | Bin 19589397 -> 23000385 bytes src/util/entities/Message.ts | 77 +++++++++++++++++- src/util/schemas/MessageCreateSchema.ts | 4 +- .../responses/GuildMessagesSearchResponse.ts | 4 +- 5 files changed, 77 insertions(+), 8 deletions(-) diff --git a/assets/openapi.json b/assets/openapi.json index 2788cdb08bb2d9d7ee2a458cbee2a0f133cee203..f3ad32a91baffeb6a23de34cb69f65748e111e90 100644 GIT binary patch delta 3936 zcmeHKeN0=|71zD@y=NN;wL=2_AjUY5w8;W?+91*<#Q9o7+7JREt(Fk{Y#!joy!Wup z8rEn9X%cF?CWM=Dv^8moCN0|^TTMW7x~d)YBWjnfg{o3M=rUNhHL0`&(qz)QRoi{f z7;s6Y{<%q$Z0Y*m^YJ_9o^$Tczk6%>tt&Bgar1#Jfoe9W8yxpo+1W6p>-t zLRLfna>N@C?)R)&U%$Et##C+<#D{P(c>Zo!LCre~!1n^GPg+6Q$dU3`_<3r*myXpf zrK3?Nw`0bY6Ac#f06}y|Etp?lYUkYt2x3OEK z18p`fVc=bi?7EN^Ff1;`UPFx(RavrVXJNVE4v5iEkXpKJNP#_OWT&2YjPTPJkQE+z z5a;uXDz>#VoAxYiAw{IdP(%vCtNbPKbT=}?1OqbIRk*}Q&FeT`k!zGE z=}EC0%x8oG@NY!fS$jKFsbsT$^)+DGh?n$U%7?0PbQtdW0&6Y6foC6b>x}~a0(NG# z$6|`SRcU2#B`SWD5B@gJ1fE^QJts*$CCe-bz7rr3- zaI8{R+gahKfpP1od(LAoR9{DxP(6-JAY4UxFls{&KztAzSQ1Q&=7+9;5knMizq)5hwD3(`74-)MdZJqMB*2std%%1W2^yR^bM&|)Zvhf`_$8DBo#&8KNSdZ%z@}pa&r7T@ zMyGKu+?d9B)Un$NUz|d@s0@_vaVx<8Bf+6p!{D)|>nLL`!+JQwkFsgU^a|h~r&xyk zZ(}3<`D-(ai!%#UPP;DJpg$88vGgpV@zpNy-AfF#KX?G*&B#b%9V#6dQj^2ee3f&6 z{~2U8hQ*-NqJ$g4`yn|%M}K4Fw7z7L&*#kH*d{fJ?cvyN69-X0618W`g8sGl>YbqMSZCF^oo=DaA=5>JN%4Q$Z4 zSxs+*&fg=c_XJPBU*QB7Z*)?Ba6NeLaK*G!U6E{&;eY`2uY~!k7pSelqP?yr_NXVD zJw5lyCQcou5}zv0de$UvZBk^+D5n}ygQVA3SdyEmOZ+yd4&>lwfBDNeH++1CJg(t} zsXM45t(3QGKzH!%(U{Uo2W~r)B{FliFm^j47 z{BRF(K);NagZcwv`t_%1%_4a2Ho68qN6|sO%@QBnLBCy{X5FnlD0`^?vJ3j3BN?=( zw^-vO>p&gimxJdqbTWDS{o}=Y_M^}<9jLZ#`voV!oSh_R4 zrhs`oNWvi*ycKMisa+(j?PtN$PceCR$iugs*bd{Zq%`qKCH~Tg8gvplOA!mTM=QzV z#^PkD_71j%kq6@SxCAyGCGOr)3!U<>`6|iW2csLY(~x|>B`6fpTI#B)ha(Cy(m!n~ z0CNtmKmnMn0AHC!O91i*Vg+L5AA(D^*#$Tl$s$9HqA#rEM9y3^V7(<-~!1>5v` z?Ok@cU|3;9d5%#gnbABvV|<8c9mHTZnn^{i=QJq4u<>uj4W%gI0j49 zBcb^YTRE z)K$C>>kJI;zks!=8BI#VR0R@X*8%h$P48I(!O1@u(nLc->{{`aK2YAwgUJo^glYe-bB JHU8`6{{SaEmS6w? delta 516 zcmZvYKWGzi6vz4fxx4(T=7DF)Fh zDk47h4gR}H!4ML-;h9Z}I5@OHaFJq@sdOm4Hlswn20921pXKp=pZDc${TTTAxxacX z04rgO?%w7hfmTfJ!Y?Jwf{%|cl0DB0Jy?CsHFqd%rl4(GaEE#z-&9bY9|v0+Q0-IJ1!W{sW`dPo zZO6#J^hQAKN1~o&{R@;MEy7P1}y@U6hIq9SM=spHHbAfM_ze3i< A6aWAK diff --git a/assets/schemas.json b/assets/schemas.json index 00f86b3604f19db3f1eb55db5e1432de30d7d2a5..621a84b4476278260c1ca5e13cbce85cd24c1ed0 100644 GIT binary patch delta 300606 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 MUng9nM#bLrKiH2WO8@`> delta 18496 zcmdU%`BPI@6vyYrgq`pLO2Q5WwYUw43!oHGl&Xj*f`}{;$yh`zNEsDDYh-C%Fm05J z6n%=Q2$oWp9;D)v78UD;OFLBUs3SV1C^A~D&{q4B*pC0fJ^k$^H8gH^i@k9;oT@eNn9-J53Mbmw6+D-^ z=Y)9aV=PBg-7^noh*>r#QlejR)!F#gourUW^t}U!^Cp(h%xdr|>pj={-MA)pr!+3U>okWI3VP z%0Lwj3z)H0zbA#G1MfQc)6+g*Uj$vWr%D1b9aA=S8b)A5mU7fy&)6_Jk z%K0JHJ&-3+wWqf)&2Gz@0DB$`OV*fv-s3gq4v_cMBTT}c2t>iP%sEmtP}b?wMp*07 zvy3&t5)Djl^^Iokh{!Q%+$bG$N0Kyh9>MTd+q+q!~l(J1pUKE%!fi!wD=LiJMiFmNwoN|qx6j+YlA zb|MH{m=qS-j=`5NkI(oD!$j-8E7}I^eWTJam?Of(AP@yq+}uzE(;5(MEN5q^yY)9y z?e&dT^74pCj;;XCiH4PNO4=7^C8f)x@peFq68aDjQ$J~Z4*S`L&dN)OgAbfEh&a6skmQXJDjyXs-CdkSE zNqF%8BN0Q50@E{+qcEiK61?z5G(-S80Gu-<`Oykuz`gIWj+Wzg5kZol^ANbspM~h2I`XpaIdDE zZKnkcsr(TG42Upw82VC-qAV}DS%YB>-#l=8J%)R=*m7V7-5=gT(f{!L{=H!sCYF-t zUT)=wJJ!#YkA-A2sn6Km5;bLR%ZDuP02n=Xys?m)7fJ{t>mRYg4)Go4&7Wh zu${dq^nCAMH3%@2>eu_&W0+2agb%I~vKB+67@ra1E25+%6M;A;nx+;rl7)mO8v#c* zZ%y_i;0RR6$rlJX?EF25jMr*8Qh|V`?U_B`i*J?kX4>O8hY7kg>&;H{e(bOLm46_79K$+wUYSv564>OM$&*RBSP_ z6-w4kC&d_O&W1U<@Ev{clW6wR2C2*EdIla&}i;-|-uMg)_vU}zdtBA8-? roY8S)4gzba;Ydoz<`)nQ4dTLBMIyn_k0$hKlgMG_{={h{R)zln5. */ -import { Embed, MessageComponent, PollAnswer, PollMedia } from "@spacebar/util"; +import { ActionRowComponent, Embed, PollAnswer, PollMedia } from "@spacebar/util"; type Attachment = { id: string; @@ -54,7 +54,7 @@ export interface MessageCreateSchema { **/ attachments?: Attachment[]; sticker_ids?: string[]; - components?: MessageComponent[]; + components?: ActionRowComponent[]; // TODO: Fix TypeScript errors in src\api\util\handlers\Message.ts once this is enabled poll?: PollCreationSchema; enforce_nonce?: boolean; // For Discord compatibility, it's the default behavior here diff --git a/src/util/schemas/responses/GuildMessagesSearchResponse.ts b/src/util/schemas/responses/GuildMessagesSearchResponse.ts index 6121983e0..ec41965b4 100644 --- a/src/util/schemas/responses/GuildMessagesSearchResponse.ts +++ b/src/util/schemas/responses/GuildMessagesSearchResponse.ts @@ -17,9 +17,9 @@ */ import { + ActionRowComponent, Attachment, Embed, - MessageComponent, MessageType, Poll, PublicUser, @@ -42,7 +42,7 @@ export interface GuildMessagesSearchMessage { timestamp: string; edited_timestamp: string | null; flags: number; - components: MessageComponent[]; + components: ActionRowComponent[]; poll: Poll; hit: true; } From c41b4f6364f9a7e448830dcd5058d696a20f8ee6 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Fri, 19 Jul 2024 15:13:27 +0200 Subject: [PATCH 16/41] prettier .-. --- src/util/entities/Message.ts | 14 ++++++++++++-- src/util/schemas/MessageCreateSchema.ts | 7 ++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index ea3a3cc76..9ba6ff7e7 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts @@ -253,7 +253,12 @@ export interface MessageComponent { export interface ActionRowComponent extends MessageComponent { type: MessageComponentType.ActionRow; - components: (ButtonComponent | StringSelectMenuComponent | SelectMenuComponent | TextInputComponent)[]; + components: ( + | ButtonComponent + | StringSelectMenuComponent + | SelectMenuComponent + | TextInputComponent + )[]; } export interface ButtonComponent extends MessageComponent { @@ -277,7 +282,12 @@ export enum ButtonStyle { } export interface SelectMenuComponent extends MessageComponent { - type: MessageComponentType.StringSelect | MessageComponentType.UserSelect | MessageComponentType.RoleSelect | MessageComponentType.MentionableSelect | MessageComponentType.ChannelSelect; + type: + | MessageComponentType.StringSelect + | MessageComponentType.UserSelect + | MessageComponentType.RoleSelect + | MessageComponentType.MentionableSelect + | MessageComponentType.ChannelSelect; custom_id: string; channel_types?: number[]; placeholder?: string; diff --git a/src/util/schemas/MessageCreateSchema.ts b/src/util/schemas/MessageCreateSchema.ts index 2d7e4179b..51263dcee 100644 --- a/src/util/schemas/MessageCreateSchema.ts +++ b/src/util/schemas/MessageCreateSchema.ts @@ -16,7 +16,12 @@ along with this program. If not, see . */ -import { ActionRowComponent, Embed, PollAnswer, PollMedia } from "@spacebar/util"; +import { + ActionRowComponent, + Embed, + PollAnswer, + PollMedia, +} from "@spacebar/util"; type Attachment = { id: string; From 81c44a0c77e136ef44df2fc775119a4fad26a1e8 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Sun, 28 Jul 2024 21:40:06 +0200 Subject: [PATCH 17/41] Fix ?before messages fetch param --- src/api/routes/channels/#channel_id/messages/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts index d4635b1d7..db46aff71 100644 --- a/src/api/routes/channels/#channel_id/messages/index.ts +++ b/src/api/routes/channels/#channel_id/messages/index.ts @@ -157,6 +157,7 @@ router.get( if (BigInt(after) > BigInt(Snowflake.generate())) return res.status(422); query.where.id = MoreThan(after); + query.order = { timestamp: "ASC" }; } else if (before) { if (BigInt(before) > BigInt(Snowflake.generate())) return res.status(422); From 9ed53e228a9f9b4d429c227697e2b8e59c108ccd Mon Sep 17 00:00:00 2001 From: Cyber Date: Tue, 30 Jul 2024 18:56:57 +0200 Subject: [PATCH 18/41] fix: reaction removing --- .../channels/#channel_id/messages/#message_id/reactions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts b/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts index c2222b0a3..aa871d037 100644 --- a/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts +++ b/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts @@ -262,7 +262,7 @@ router.put( ); router.delete( - "/:emoji/:user_id", + "/:emoji/:burst/:user_id", route({ responses: { 204: {}, From 2aa7f5bfe0e86867e7126d097f54a96b300a0833 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Fri, 9 Aug 2024 08:08:55 +0200 Subject: [PATCH 19/41] opcode 8 --- assets/schemas.json | Bin 23267068 -> 23428652 bytes src/gateway/opcodes/RequestGuildMembers.ts | 100 ++++++++++++++++-- src/util/interfaces/Event.ts | 10 +- src/util/schemas/RequestGuildMembersSchema.ts | 35 ++++++ src/util/schemas/index.ts | 7 +- 5 files changed, 138 insertions(+), 14 deletions(-) create mode 100644 src/util/schemas/RequestGuildMembersSchema.ts diff --git a/assets/schemas.json b/assets/schemas.json index 5226bbade87cac4040e598053c272c32a2445bb6..6e8f2f48dfe5166d80de9008f3e61b2fae25f76e 100644 GIT binary patch delta 1481 zcmW;BcU;tU0LJmx4-|xR9D3k!;Blx3DpN(A2qK^$3Jx5IgFMT^M1@LIgYzjeHBj=t zl@@AKrLwYYYifJ%rH0Wi)3VG#<@5B%`}4>1v~SvIYiZwVtA&LC1fl~vA_&3ggb;+n zhR*1MuIL6k!q6Q(;6OMc&=XEX!i6YAqZizWfd{eZjXvm$IP^n263`z5Fc67ILNZd2 ziZl$uU<^Szh9U#QFdQQ=5~DC0nHYmCWMeFHFb=uMLq5i10t!%wi73J(Ohz$EFa=XF z4bxGI8JLM#n2j>bK{@7P9_C{KDsTlB!izIAb(|RQrK{3S zu`6Lpccq8oP{Nf6rKjRlA{Cbsr9>;e6t@zic$8SBx6()HtHde&lz1gU>8}h>1}ce4 zl9H^XD5*-CGDsP$3{ldRp-P4_Oc}0>P(~`Fl+jA2GDgW#vX!w)jxtWkRq~X4WxO&$ zDNqWPiAs?&Ntvt^D<#SlWvVhwnXZ&7GnARiEM>M*rp!^wmAT41Wxle&I_UFPSgWco E18oYTwg3PC delta 1318 zcmW;Gd3Xo}9LDiy46~Wrm}53O%r%?2*=&Zf*_itn#wL}RRyY=s;h>AR8zqUQ7wh4 zw(6*^>Z!gOsG%AuOpVn<;cBX8YOWTFP)kKBO05*F7{w}1trf2}YO8i?uMSF3MY}ddrta#Yp6aFEN>Qr%C{5|=s|@v1e+^Kk25OM9l&!(aQLct)sPZ&S!!<%9 zHAN)kD22Mk#krU=L zcA7ZhPE)6u)7)v{L^v&-NGHl^w07d1Hcnfoozvdw;3PO5okSEt9k zot-XDSErlP-Ra@Eon1=}uoK!|CVrcLq3_&Om37ljUSPgPj~F*BRmr yb@H5H&TwahGtwF5jCRI2W1Vr%cqiYP;7oKTIR(ySXNptkObysk5n2?mVAg-y4-oJG diff --git a/src/gateway/opcodes/RequestGuildMembers.ts b/src/gateway/opcodes/RequestGuildMembers.ts index 304d4b390..68c7b7f4a 100644 --- a/src/gateway/opcodes/RequestGuildMembers.ts +++ b/src/gateway/opcodes/RequestGuildMembers.ts @@ -1,23 +1,111 @@ /* 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 { WebSocket } from "@spacebar/gateway"; +import { + getPermission, + GuildMembersChunkEvent, + Member, + Presence, + RequestGuildMembersSchema, +} from "@spacebar/util"; +import { + WebSocket, + Payload, + OPCODES, + Send, +} from "@spacebar/gateway"; +import { check } from "./instanceOf"; +import { FindManyOptions, In, Like } from "typeorm"; -export function onRequestGuildMembers(this: WebSocket) { - // return this.close(CLOSECODES.Unknown_error); +export async function onRequestGuildMembers(this: WebSocket, { d }: Payload) { + // TODO: check data + check.call(this, RequestGuildMembersSchema, d); + + const { guild_id, query, presences, nonce } = + d as RequestGuildMembersSchema; + let { limit, user_ids } = + d as RequestGuildMembersSchema; + + if ("query" in d && (!limit || Number.isNaN(limit))) throw new Error("\"query\" requires \"limit\" to be set"); + if ("query" in d && user_ids) throw new Error("\"query\" and \"user_ids\" are mutually exclusive"); + if (user_ids && !Array.isArray(user_ids)) user_ids = [user_ids]; + user_ids = user_ids as string[] | undefined; + + // TODO: Configurable limit? + if ((query || (user_ids && user_ids.length > 0)) && (!limit || limit > 100)) limit = 100; + + const permissions = await getPermission(this.user_id, guild_id); + permissions.hasThrow("VIEW_CHANNEL"); + + const whereQuery: any = {}; + if (query) { + whereQuery.user = { + username: Like(query + "%") + }; + } else if (user_ids && user_ids.length > 0) { + whereQuery.id = In(user_ids); + } + + const memberFind: FindManyOptions = { + where: { + ...whereQuery, + guild_id + }, + relations: ["user", "roles", ...(presences ? ["presence"] : [])], + }; + if (limit) memberFind.take = Math.abs(Number(limit || 100)); + const members = await Member.find(memberFind); + + const baseData = { + guild_id, + nonce, + } + + const chunks: GuildMembersChunkEvent["data"][] = []; + while (members.length > 0) { + const chunk = members.splice(0, 1000); + + const presenceList: Presence[] = []; + if (presences) { + for await (const member of chunk) { + presenceList.push(member.presence); + delete member.presence; + } + } + + chunks.push({ + ...baseData, + members: chunk.map(member => member.toPublicMember()), + presences: presences ? presenceList : undefined, + chunk_index: chunks.length, + chunk_count: Math.ceil(members.length / 1000), + }); + } + + if (user_ids && user_ids.length > 0) + chunks[0].not_found = user_ids.filter(id => !members.some(member => member.user.id == id)); + + chunks.forEach((chunk) => { + Send(this, { + op: OPCODES.Dispatch, + s: this.sequence++, + t: "GUILD_MEMBERS_CHUNK", + d: chunk, + }); + }); } diff --git a/src/util/interfaces/Event.ts b/src/util/interfaces/Event.ts index 98a64e94a..a31e22639 100644 --- a/src/util/interfaces/Event.ts +++ b/src/util/interfaces/Event.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -280,8 +280,8 @@ export interface GuildMembersChunkEvent extends Event { members: PublicMember[]; chunk_index: number; chunk_count: number; - not_found: string[]; - presences: Presence[]; + not_found?: string[]; + presences?: Presence[]; nonce?: string; }; } diff --git a/src/util/schemas/RequestGuildMembersSchema.ts b/src/util/schemas/RequestGuildMembersSchema.ts new file mode 100644 index 000000000..01ba4f2e9 --- /dev/null +++ b/src/util/schemas/RequestGuildMembersSchema.ts @@ -0,0 +1,35 @@ +/* + 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 . +*/ + +export interface RequestGuildMembersSchema { + guild_id: string; + query?: string; + limit?: number; + presences?: boolean; + user_ids?: string | string[]; + nonce?: string; +} + +export const RequestGuildMembersSchema = { + guild_id: String, + $query: String, + $limit: Number, + $presences: Boolean, + $user_ids: [] as string | string[], + $nonce: String, +}; diff --git a/src/util/schemas/index.ts b/src/util/schemas/index.ts index 4812b5356..62199dfb1 100644 --- a/src/util/schemas/index.ts +++ b/src/util/schemas/index.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -58,6 +58,7 @@ export * from "./PurgeSchema"; export * from "./RegisterSchema"; export * from "./RelationshipPostSchema"; export * from "./RelationshipPutSchema"; +export * from "./RequestGuildMembersSchema"; export * from "./RoleModifySchema"; export * from "./RolePositionUpdateSchema"; export * from "./SelectProtocolSchema"; From cc63571d89cc1053c4a6a78b639766700b38398d Mon Sep 17 00:00:00 2001 From: Cyber Date: Sat, 10 Aug 2024 20:25:52 +0200 Subject: [PATCH 20/41] fix: make this a new route --- .../messages/#message_id/reactions.ts | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts b/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts index aa871d037..4792c5343 100644 --- a/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts +++ b/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts @@ -261,6 +261,76 @@ router.put( }, ); +router.delete( + "/:emoji/:user_id", + route({ + responses: { + 204: {}, + 400: { + body: "APIErrorResponse", + }, + 404: {}, + 403: {}, + }, + }), + async (req: Request, res: Response) => { + let { user_id } = req.params; + const { message_id, channel_id } = req.params; + + const emoji = getEmoji(req.params.emoji); + + const channel = await Channel.findOneOrFail({ + where: { id: channel_id }, + }); + const message = await Message.findOneOrFail({ + where: { id: message_id, channel_id }, + }); + + if (user_id === "@me") user_id = req.user_id; + else { + const permissions = await getPermission( + req.user_id, + undefined, + channel_id, + ); + 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); + else + already_added.user_ids.splice( + already_added.user_ids.indexOf(user_id), + 1, + ); + + await message.save(); + + await emitEvent({ + event: "MESSAGE_REACTION_REMOVE", + channel_id, + data: { + user_id: req.user_id, + channel_id, + message_id, + guild_id: channel.guild_id, + emoji, + }, + } as MessageReactionRemoveEvent); + + res.sendStatus(204); + }, +); + router.delete( "/:emoji/:burst/:user_id", route({ From f8a21eff0cdaf8e5ce81e50385c5b05f4a4e7dd6 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Sat, 10 Aug 2024 20:59:05 +0200 Subject: [PATCH 21/41] Fix chunk_count & not_found --- src/gateway/opcodes/RequestGuildMembers.ts | 44 ++++++++++++---------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/gateway/opcodes/RequestGuildMembers.ts b/src/gateway/opcodes/RequestGuildMembers.ts index 68c7b7f4a..8934377d0 100644 --- a/src/gateway/opcodes/RequestGuildMembers.ts +++ b/src/gateway/opcodes/RequestGuildMembers.ts @@ -23,12 +23,7 @@ import { Presence, RequestGuildMembersSchema, } from "@spacebar/util"; -import { - WebSocket, - Payload, - OPCODES, - Send, -} from "@spacebar/gateway"; +import { WebSocket, Payload, OPCODES, Send } from "@spacebar/gateway"; import { check } from "./instanceOf"; import { FindManyOptions, In, Like } from "typeorm"; @@ -38,24 +33,26 @@ export async function onRequestGuildMembers(this: WebSocket, { d }: Payload) { const { guild_id, query, presences, nonce } = d as RequestGuildMembersSchema; - let { limit, user_ids } = - d as RequestGuildMembersSchema; + let { limit, user_ids } = d as RequestGuildMembersSchema; - if ("query" in d && (!limit || Number.isNaN(limit))) throw new Error("\"query\" requires \"limit\" to be set"); - if ("query" in d && user_ids) throw new Error("\"query\" and \"user_ids\" are mutually exclusive"); + if ("query" in d && (!limit || Number.isNaN(limit))) + throw new Error('"query" requires "limit" to be set'); + if ("query" in d && user_ids) + throw new Error('"query" and "user_ids" are mutually exclusive'); if (user_ids && !Array.isArray(user_ids)) user_ids = [user_ids]; user_ids = user_ids as string[] | undefined; // TODO: Configurable limit? - if ((query || (user_ids && user_ids.length > 0)) && (!limit || limit > 100)) limit = 100; + if ((query || (user_ids && user_ids.length > 0)) && (!limit || limit > 100)) + limit = 100; const permissions = await getPermission(this.user_id, guild_id); permissions.hasThrow("VIEW_CHANNEL"); - const whereQuery: any = {}; + const whereQuery: FindManyOptions["where"] = {}; if (query) { whereQuery.user = { - username: Like(query + "%") + username: Like(query + "%"), }; } else if (user_ids && user_ids.length > 0) { whereQuery.id = In(user_ids); @@ -64,9 +61,9 @@ export async function onRequestGuildMembers(this: WebSocket, { d }: Payload) { const memberFind: FindManyOptions = { where: { ...whereQuery, - guild_id + guild_id, }, - relations: ["user", "roles", ...(presences ? ["presence"] : [])], + relations: ["roles", ...(presences ? ["presence"] : [])], }; if (limit) memberFind.take = Math.abs(Number(limit || 100)); const members = await Member.find(memberFind); @@ -74,7 +71,15 @@ export async function onRequestGuildMembers(this: WebSocket, { d }: Payload) { const baseData = { guild_id, nonce, - } + }; + + const chunkCount = Math.ceil(members.length / 1000); + + let notFound: string[] = []; + if (user_ids && user_ids.length > 0) + notFound = user_ids.filter( + (id) => !members.some((member) => member.id == id), + ); const chunks: GuildMembersChunkEvent["data"][] = []; while (members.length > 0) { @@ -90,15 +95,14 @@ export async function onRequestGuildMembers(this: WebSocket, { d }: Payload) { chunks.push({ ...baseData, - members: chunk.map(member => member.toPublicMember()), + members: chunk.map((member) => member.toPublicMember()), presences: presences ? presenceList : undefined, chunk_index: chunks.length, - chunk_count: Math.ceil(members.length / 1000), + chunk_count: chunkCount, }); } - if (user_ids && user_ids.length > 0) - chunks[0].not_found = user_ids.filter(id => !members.some(member => member.user.id == id)); + if (notFound.length > 0) chunks[0].not_found = notFound; chunks.forEach((chunk) => { Send(this, { From 20b2843680ad1d72b561db5ae536c790015e90cd Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Sat, 10 Aug 2024 22:10:20 +0200 Subject: [PATCH 22/41] Make profile bio max length configurable --- src/api/routes/users/#id/profile.ts | 20 ++++++++++++++++--- src/api/routes/users/@me/index.ts | 18 ++++++++++++++--- .../subconfigurations/limits/UserLimits.ts | 7 ++++--- src/util/entities/User.ts | 8 ++++---- src/util/schemas/UserModifySchema.ts | 9 +++------ 5 files changed, 43 insertions(+), 19 deletions(-) diff --git a/src/api/routes/users/#id/profile.ts b/src/api/routes/users/#id/profile.ts index db0922d68..44271cad3 100644 --- a/src/api/routes/users/#id/profile.ts +++ b/src/api/routes/users/#id/profile.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -19,6 +19,8 @@ import { route } from "@spacebar/api"; import { Badge, + Config, + FieldErrors, Member, PrivateUserProjection, User, @@ -136,6 +138,18 @@ router.patch( select: [...PrivateUserProjection, "data"], }); + if (body.bio) { + const { maxBio } = Config.get().limits.user; + if (body.bio.length > maxBio) { + throw FieldErrors({ + bio: { + code: "BIO_INVALID", + message: `Bio must be less than ${maxBio} in length`, + }, + }); + } + } + user.assign(body); await user.save(); diff --git a/src/api/routes/users/@me/index.ts b/src/api/routes/users/@me/index.ts index cddc3a087..5caf0d119 100644 --- a/src/api/routes/users/@me/index.ts +++ b/src/api/routes/users/@me/index.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -189,6 +189,18 @@ router.patch( } } + if (body.bio) { + const { maxBio } = Config.get().limits.user; + if (body.bio.length > maxBio) { + throw FieldErrors({ + bio: { + code: "BIO_INVALID", + message: `Bio must be less than ${maxBio} in length`, + }, + }); + } + } + user.assign(body); user.validate(); await user.save(); diff --git a/src/util/config/types/subconfigurations/limits/UserLimits.ts b/src/util/config/types/subconfigurations/limits/UserLimits.ts index 8f9b1a97f..afe9afbe9 100644 --- a/src/util/config/types/subconfigurations/limits/UserLimits.ts +++ b/src/util/config/types/subconfigurations/limits/UserLimits.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -20,4 +20,5 @@ export class UserLimits { maxGuilds: number = 1048576; maxUsername: number = 32; maxFriends: number = 5000; + maxBio: number = 190; } diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts index c929039ef..b299bcfce 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -130,7 +130,7 @@ export class User extends BaseClass { bot: boolean = false; // if user is bot @Column() - bio: string = ""; // short description of the user (max 190 chars -> should be configurable) + bio: string = ""; // short description of the user @Column() system: boolean = false; // shouldn't be used, the api sends this field type true, if the generated message comes from a system generated author diff --git a/src/util/schemas/UserModifySchema.ts b/src/util/schemas/UserModifySchema.ts index e155b9af4..4be6ad43a 100644 --- a/src/util/schemas/UserModifySchema.ts +++ b/src/util/schemas/UserModifySchema.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -23,9 +23,6 @@ export interface UserModifySchema { */ username?: string; avatar?: string | null; - /** - * @maxLength 1024 - */ bio?: string; accent_color?: number; banner?: string | null; From 5900d4fdacb89e0f6312ae2b3f52267f142f1b65 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Sat, 10 Aug 2024 22:23:02 +0200 Subject: [PATCH 23/41] Update schema for bio max length config --- assets/schemas.json | Bin 23267068 -> 23291081 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/schemas.json b/assets/schemas.json index 5226bbade87cac4040e598053c272c32a2445bb6..70a65862aa56151cd2364f5639bf6515ee96b3eb 100644 GIT binary patch delta 8586 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 delta 3986 zcmbuCeOymz7QjEJ>Ahctywp^Rrb+elW2)%|4QfO`p;QLxWnw1UQn5;!D9M>6b2G_h zv-?xb*paq|;VdmTc1L+xru8y08L72vhKB4YOxZovR!{u7e|*m$=lyxkbMJlbxpUjh zI=2;=P?eQ?m2( zbR))?o~N5EoiU)-WmcJj8J6BRVN9v?zFC40^*?J!yb43&%_$__q9Ev%Si1%bv}>>= zyM`f6!2?!#%at#OV{q6|W*7yBS-lX-V$NABW;o@X9kEVGiJntNGNZ_Iigl{c2sR$J zW^AbO@asoJeVwZ<*16hIovZz*{yg>?w9(l!qsd0+Fg&cUDb^=AI5J}>!C~z5{yeO2 zE*{G`QFHMdiT(P~8bp;d^CqFnJTv<9uz%xKPsWQHufF}E=%1za z#w@K5WodoG1-+b8;ctMd8ehhbR5g>5XZ1B}r|C74&txQ@DMUX0g+e59B=ZjCR7~v* z860GWHLlZ`=~Uwyutu0Rt}F{+0%>L0yOB`?LD;FD!30sKdS@2rV8jWEPRd z6l` zoyj0w@!DX)h-1t$nRPVAEDJ<^R8AH~<$Od@IoUf!y<2-Wy0xz-xAqM=VzVttIoOhv zOD#zo4F!MM`+MV|=-IrH$s^C^P2sZ#IwoaFem=43JNR5**6d!~TdM!9D`SrtA|71c8&rjXP# zwyVTEX(zU0+KEpo?L^UHai02KJ5b-NnDo7No)+`JDBp=M%4K>{zU!Je2bFp^s?@(F zmAb^HKR>_JVoOVyJrrB|yZU}TKmC;jdyO$|)bAM`rH$HaAQ+L2Oes|sl*I{pxi#Z6 zBb=wVkJ(T2^gjDQFd`e7)zz%#Kkho=Y7;prd9;prDCqJM4jL99(Kr`qI0F8%Z2 zj>p|Q#8l9@dw={{^pA|G#K@Q`ij4WPQ+OPazcI{V+H&Pcj_4m4{1pZUAEm(HW1EGE z%8KuCBQ&Ww#t@m*{3%;}^{^2Y-xH+xR(Aa7+24~imT^eqt)}TC%&>C+xqKhTmLQD);IVG1Ho-G zZ!kA$+sy9-@K z{KTIzKk*6WC$={VdR>*H2N=sgE$}tR%BFUvgDRVz`Y#zcLpuLzXD~rc(lh2cX_9^k z6dO6%^a~zr>ZF5BT{gm`dK065>CP>x^Gg95=Kt)KN8u+Rr( z{VW66Dw+aL8h{jV(h!t@lSYsrR_nQWO0AQPfrSYut&>e5T#W5@kTR>xK*_99L7ZUf zy_ZV%&gQ_v0u*~^OPC`#Nc(nM0*fJ_?AtvQwuz<{7Q;YVVPOTz3X9>8Cz_n|hJ)ms zHv$yrypfPEnp!$Wg4EJ63Y3-(>%pedBpZ-QlU@g!5v1DsF`(4ekA++zt&|(&1T5YFB{#@<@DjQwFZox$ z35tKU3v>uiK<{{^HD2R@g)1m)yxibo%)qIU)}3(!7Ve;|JEMjTVnq+1@gRBlXh8As znJ~D|N97YhdQ?6Ult<-n0g6%GCsnF)^aQ2K(QDAoNLj^Rz~XICvWmSSR(QfBD{~)U z;R}kDx!>T~-N^L==|=7(P;TT-h7_?@Zu?~3llCdRC++^QNO;LeR>|*h!sMx(FnJoB z7oH_)WXm*QF&&hVEdlU(u4BMiBY-k^LZcK!+9UvBVe&$k~c@iSr?}o_{f&~N{r;D>)yk= zuRrq26t%TH1Pz83#oZkyKg_coEl*spKVs)D!_l(l2a}g&$+Yvog=C)q6? Date: Sun, 11 Aug 2024 06:40:42 +0200 Subject: [PATCH 24/41] Fix issues with requesting presences --- src/gateway/events/Close.ts | 10 +++++----- src/gateway/opcodes/Identify.ts | 12 ++++++------ src/gateway/opcodes/LazyRequest.ts | 8 ++++---- src/gateway/opcodes/PresenceUpdate.ts | 15 ++++++++++----- src/gateway/opcodes/RequestGuildMembers.ts | 17 +++++++++++++---- src/util/entities/Session.ts | 12 +++++++----- src/util/interfaces/Status.ts | 9 +++++---- .../mariadb/1723347738541-client_status.ts | 17 +++++++++++++++++ .../mysql/1723347738541-client_status.ts | 17 +++++++++++++++++ .../postgres/1723347738541-client_status.ts | 17 +++++++++++++++++ 10 files changed, 101 insertions(+), 33 deletions(-) create mode 100644 src/util/migration/mariadb/1723347738541-client_status.ts create mode 100644 src/util/migration/mysql/1723347738541-client_status.ts create mode 100644 src/util/migration/postgres/1723347738541-client_status.ts diff --git a/src/gateway/events/Close.ts b/src/gateway/events/Close.ts index 16f6b188e..311ed32a0 100644 --- a/src/gateway/events/Close.ts +++ b/src/gateway/events/Close.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -50,7 +50,7 @@ export async function Close(this: WebSocket, code: number, reason: Buffer) { } as SessionsReplace); const session = sessions.first() || { activities: [], - client_info: {}, + client_status: {}, status: "offline", }; @@ -68,7 +68,7 @@ export async function Close(this: WebSocket, code: number, reason: Buffer) { data: { user: userOrId, activities: session.activities, - client_status: session?.client_info, + client_status: session?.client_status, status: session.status, }, } as PresenceUpdateEvent); diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts index 41f9f83db..e30a1ee0c 100644 --- a/src/gateway/opcodes/Identify.ts +++ b/src/gateway/opcodes/Identify.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -122,8 +122,8 @@ export async function onIdentify(this: WebSocket, data: Payload) { session_id: this.session_id, status: identify.presence?.status || "online", client_info: { - client: identify.properties?.$device, - os: identify.properties?.os, + client: identify.properties?.device || identify.properties?.$device, + os: identify.properties?.os || identify.properties?.$os, version: 0, }, activities: identify.presence?.activities, // TODO: validation @@ -372,7 +372,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { data: { user: user.toPublicUser(), activities: session.activities, - client_status: session.client_info, + client_status: session.client_status, status: session.status, }, } as PresenceUpdateEvent), diff --git a/src/gateway/opcodes/LazyRequest.ts b/src/gateway/opcodes/LazyRequest.ts index 3c21b708c..27e9b00af 100644 --- a/src/gateway/opcodes/LazyRequest.ts +++ b/src/gateway/opcodes/LazyRequest.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -248,7 +248,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { d: { user: user, activities: session?.activities || [], - client_status: session?.client_info, + client_status: session?.client_status, status: session?.status || "offline", } as Presence, }); diff --git a/src/gateway/opcodes/PresenceUpdate.ts b/src/gateway/opcodes/PresenceUpdate.ts index 03736263c..f84da1205 100644 --- a/src/gateway/opcodes/PresenceUpdate.ts +++ b/src/gateway/opcodes/PresenceUpdate.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -35,14 +35,19 @@ export async function onPresenceUpdate(this: WebSocket, { d }: Payload) { { status: presence.status, activities: presence.activities }, ); + const session = await Session.findOneOrFail({ + select: ["client_status"], + where: { session_id: this.session_id }, + }); + await emitEvent({ event: "PRESENCE_UPDATE", user_id: this.user_id, data: { user: await User.getPublicUser(this.user_id), - activities: presence.activities, - client_status: {}, // TODO: status: presence.status, + activities: presence.activities, + client_status: session.client_status, }, } as PresenceUpdateEvent); } diff --git a/src/gateway/opcodes/RequestGuildMembers.ts b/src/gateway/opcodes/RequestGuildMembers.ts index 8934377d0..9a966752b 100644 --- a/src/gateway/opcodes/RequestGuildMembers.ts +++ b/src/gateway/opcodes/RequestGuildMembers.ts @@ -22,6 +22,7 @@ import { Member, Presence, RequestGuildMembersSchema, + Session, } from "@spacebar/util"; import { WebSocket, Payload, OPCODES, Send } from "@spacebar/gateway"; import { check } from "./instanceOf"; @@ -63,7 +64,7 @@ export async function onRequestGuildMembers(this: WebSocket, { d }: Payload) { ...whereQuery, guild_id, }, - relations: ["roles", ...(presences ? ["presence"] : [])], + relations: ["users", "roles"], }; if (limit) memberFind.take = Math.abs(Number(limit || 100)); const members = await Member.find(memberFind); @@ -83,13 +84,21 @@ export async function onRequestGuildMembers(this: WebSocket, { d }: Payload) { const chunks: GuildMembersChunkEvent["data"][] = []; while (members.length > 0) { - const chunk = members.splice(0, 1000); + const chunk: Member[] = members.splice(0, 1000); const presenceList: Presence[] = []; if (presences) { for await (const member of chunk) { - presenceList.push(member.presence); - delete member.presence; + const session = await Session.findOne({ + where: { user_id: member.id }, + }); + if (session) + presenceList.push({ + user: member.user.toPublicUser(), + status: session.status, + activities: session.activities, + client_status: session.client_status, + }); } } diff --git a/src/util/entities/Session.ts b/src/util/entities/Session.ts index 6c6f7caa6..15f8faa2f 100644 --- a/src/util/entities/Session.ts +++ b/src/util/entities/Session.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -19,7 +19,7 @@ import { User } from "./User"; import { BaseClass } from "./BaseClass"; import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { Status } from "../interfaces/Status"; +import { ClientStatus, Status } from "../interfaces/Status"; import { Activity } from "../interfaces/Activity"; //TODO we need to remove all sessions on server start because if the server crashes without closing websockets it won't delete them @@ -43,7 +43,6 @@ export class Session extends BaseClass { @Column({ type: "simple-json", nullable: true }) activities: Activity[]; - // TODO client_status @Column({ type: "simple-json", select: false }) client_info: { client: string; @@ -51,6 +50,9 @@ export class Session extends BaseClass { version: number; }; + @Column({ type: "simple-json" }) + client_status: ClientStatus; + @Column({ nullable: false, type: "varchar" }) status: Status; //TODO enum } diff --git a/src/util/interfaces/Status.ts b/src/util/interfaces/Status.ts index 407a813ec..0f2f4e136 100644 --- a/src/util/interfaces/Status.ts +++ b/src/util/interfaces/Status.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -21,5 +21,6 @@ export type Status = "idle" | "dnd" | "online" | "offline" | "invisible"; export interface ClientStatus { desktop?: string; // e.g. Windows/Linux/Mac mobile?: string; // e.g. iOS/Android - web?: string; // e.g. browser, bot account + web?: string; // e.g. browser, bot account, unknown + embedded?: string; // e.g. embedded } diff --git a/src/util/migration/mariadb/1723347738541-client_status.ts b/src/util/migration/mariadb/1723347738541-client_status.ts new file mode 100644 index 000000000..0e02c45e5 --- /dev/null +++ b/src/util/migration/mariadb/1723347738541-client_status.ts @@ -0,0 +1,17 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class client_status1723347738541 implements MigrationInterface { + name = "client_status1723347738541"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE `sessions` ADD `client_status` text NULL AFTER `client_info`", + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE `sessions` DROP COLUMN `client_status`", + ); + } +} diff --git a/src/util/migration/mysql/1723347738541-client_status.ts b/src/util/migration/mysql/1723347738541-client_status.ts new file mode 100644 index 000000000..0e02c45e5 --- /dev/null +++ b/src/util/migration/mysql/1723347738541-client_status.ts @@ -0,0 +1,17 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class client_status1723347738541 implements MigrationInterface { + name = "client_status1723347738541"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE `sessions` ADD `client_status` text NULL AFTER `client_info`", + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE `sessions` DROP COLUMN `client_status`", + ); + } +} diff --git a/src/util/migration/postgres/1723347738541-client_status.ts b/src/util/migration/postgres/1723347738541-client_status.ts new file mode 100644 index 000000000..35d9391ff --- /dev/null +++ b/src/util/migration/postgres/1723347738541-client_status.ts @@ -0,0 +1,17 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class client_status1723347738541 implements MigrationInterface { + name = "client_status1723347738541"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE sessions ADD client_status text NULL", + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE sessions DROP COLUMN client_status", + ); + } +} From df794695ffd208f5607829ea64b2430f1ba56b00 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Tue, 13 Aug 2024 21:43:19 +0200 Subject: [PATCH 25/41] Icon for discovery categories & fix "primary_only" --- src/api/routes/discovery.ts | 10 +++++----- src/util/entities/Categories.ts | 10 +++++++--- .../1723577874393-discoveryCategoryIcon.ts | 15 +++++++++++++++ .../mysql/1723577874393-discoveryCategoryIcon.ts | 15 +++++++++++++++ .../1723577874393-discoveryCategoryIcon.ts | 13 +++++++++++++ 5 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 src/util/migration/mariadb/1723577874393-discoveryCategoryIcon.ts create mode 100644 src/util/migration/mysql/1723577874393-discoveryCategoryIcon.ts create mode 100644 src/util/migration/postgres/1723577874393-discoveryCategoryIcon.ts diff --git a/src/api/routes/discovery.ts b/src/api/routes/discovery.ts index a045c1919..dd3cc15d9 100644 --- a/src/api/routes/discovery.ts +++ b/src/api/routes/discovery.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -39,8 +39,8 @@ router.get( const { primary_only } = req.query; const out = primary_only - ? await Categories.find() - : await Categories.find({ where: { is_primary: true } }); + ? await Categories.find({ where: { is_primary: true } }) + : await Categories.find(); res.send(out); }, diff --git a/src/util/entities/Categories.ts b/src/util/entities/Categories.ts index bba1bfa79..13d969dee 100644 --- a/src/util/entities/Categories.ts +++ b/src/util/entities/Categories.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -46,6 +46,10 @@ export class Categories extends BaseClassWithoutId { @Column({ type: "simple-json" }) localizations: string; + // Whether to show the category prominently (e.g. in a sidebar) instead of only secondary (e.g. in search results) @Column({ nullable: true }) is_primary: boolean; + + @Column({ nullable: true }) + icon?: string; } diff --git a/src/util/migration/mariadb/1723577874393-discoveryCategoryIcon.ts b/src/util/migration/mariadb/1723577874393-discoveryCategoryIcon.ts new file mode 100644 index 000000000..18bc0a774 --- /dev/null +++ b/src/util/migration/mariadb/1723577874393-discoveryCategoryIcon.ts @@ -0,0 +1,15 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class DiscoveryCategoryIcon1723577874393 implements MigrationInterface { + name = "DiscoveryCategoryIcon1723577874393"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE `categories` ADD `icon` text NULL", + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE `categories` DROP COLUMN `icon`"); + } +} diff --git a/src/util/migration/mysql/1723577874393-discoveryCategoryIcon.ts b/src/util/migration/mysql/1723577874393-discoveryCategoryIcon.ts new file mode 100644 index 000000000..18bc0a774 --- /dev/null +++ b/src/util/migration/mysql/1723577874393-discoveryCategoryIcon.ts @@ -0,0 +1,15 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class DiscoveryCategoryIcon1723577874393 implements MigrationInterface { + name = "DiscoveryCategoryIcon1723577874393"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE `categories` ADD `icon` text NULL", + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE `categories` DROP COLUMN `icon`"); + } +} diff --git a/src/util/migration/postgres/1723577874393-discoveryCategoryIcon.ts b/src/util/migration/postgres/1723577874393-discoveryCategoryIcon.ts new file mode 100644 index 000000000..29b4138a8 --- /dev/null +++ b/src/util/migration/postgres/1723577874393-discoveryCategoryIcon.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class DiscoveryCategoryIcon1723577874393 implements MigrationInterface { + name = "DiscoveryCategoryIcon1723577874393"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE categories ADD icon text NULL"); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE categories DROP COLUMN icon"); + } +} From e721e7c4ca21feb0e97d386805963f6e532ba6b0 Mon Sep 17 00:00:00 2001 From: Cyber Date: Wed, 14 Aug 2024 10:19:41 +0200 Subject: [PATCH 26/41] fix: add webhook_id to webhook's messages --- src/util/entities/Message.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index 15423bdbf..1dd89dc17 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts @@ -232,7 +232,7 @@ export class Message extends BaseClass { ...this, author_id: undefined, member_id: undefined, - webhook_id: undefined, + webhook_id: this.webhook_id ?? undefined, application_id: undefined, nonce: this.nonce ?? undefined, From 7a726e786ce7618bae0cce8e345533528315582d Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:22:14 +0200 Subject: [PATCH 27/41] Add migrations for webhook source_channel_id --- .../1723644478176-webhookSourceChannel.ts | 23 +++++++++++++++++++ .../1723644478176-webhookSourceChannel.ts | 23 +++++++++++++++++++ .../1723644478176-webhookSourceChannel.ts | 23 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 src/util/migration/mariadb/1723644478176-webhookSourceChannel.ts create mode 100644 src/util/migration/mysql/1723644478176-webhookSourceChannel.ts create mode 100644 src/util/migration/postgres/1723644478176-webhookSourceChannel.ts diff --git a/src/util/migration/mariadb/1723644478176-webhookSourceChannel.ts b/src/util/migration/mariadb/1723644478176-webhookSourceChannel.ts new file mode 100644 index 000000000..ac2b1799b --- /dev/null +++ b/src/util/migration/mariadb/1723644478176-webhookSourceChannel.ts @@ -0,0 +1,23 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class WebhookSourceChannel1723644478176 implements MigrationInterface { + name = "WebhookSourceChannel1723644478176"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE `webhooks` ADD COLUMN `source_channel_id` VARCHAR(255) NULL DEFAULT NULL AFTER `source_guild_id`", + ); + await queryRunner.query( + "ALTER TABLE `webhooks` ADD CONSTRAINT `FK_d64f38834fa676f6caa4786ddd6` FOREIGN KEY (`source_channel_id`) REFERENCES `channels` (`id`) ON UPDATE NO ACTION ON DELETE CASCADE", + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE `webhooks` DROP FOREIGN KEY `FK_d64f38834fa676f6caa4786ddd6`", + ); + await queryRunner.query( + "ALTER TABLE `webhooks` DROP COLUMN `source_channel_id`", + ); + } +} diff --git a/src/util/migration/mysql/1723644478176-webhookSourceChannel.ts b/src/util/migration/mysql/1723644478176-webhookSourceChannel.ts new file mode 100644 index 000000000..ac2b1799b --- /dev/null +++ b/src/util/migration/mysql/1723644478176-webhookSourceChannel.ts @@ -0,0 +1,23 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class WebhookSourceChannel1723644478176 implements MigrationInterface { + name = "WebhookSourceChannel1723644478176"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE `webhooks` ADD COLUMN `source_channel_id` VARCHAR(255) NULL DEFAULT NULL AFTER `source_guild_id`", + ); + await queryRunner.query( + "ALTER TABLE `webhooks` ADD CONSTRAINT `FK_d64f38834fa676f6caa4786ddd6` FOREIGN KEY (`source_channel_id`) REFERENCES `channels` (`id`) ON UPDATE NO ACTION ON DELETE CASCADE", + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE `webhooks` DROP FOREIGN KEY `FK_d64f38834fa676f6caa4786ddd6`", + ); + await queryRunner.query( + "ALTER TABLE `webhooks` DROP COLUMN `source_channel_id`", + ); + } +} diff --git a/src/util/migration/postgres/1723644478176-webhookSourceChannel.ts b/src/util/migration/postgres/1723644478176-webhookSourceChannel.ts new file mode 100644 index 000000000..babcf488c --- /dev/null +++ b/src/util/migration/postgres/1723644478176-webhookSourceChannel.ts @@ -0,0 +1,23 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class WebhookSourceChannel1723644478176 implements MigrationInterface { + name = "WebhookSourceChannel1723644478176"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE webhooks ADD COLUMN source_channel_id VARCHAR(255) NULL DEFAULT NULL AFTER source_guild_id", + ); + await queryRunner.query( + "ALTER TABLE webhooks ADD CONSTRAINT FK_d64f38834fa676f6caa4786ddd6 FOREIGN KEY (source_channel_id) REFERENCES channels (id) ON UPDATE NO ACTION ON DELETE CASCADE", + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE webhooks DROP CONSTRAINT FK_d64f38834fa676f6caa4786ddd6", + ); + await queryRunner.query( + "ALTER TABLE webhooks DROP COLUMN source_channel_id", + ); + } +} From 6196227ed876856523bd0c2f0ac56695622f6571 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:46:43 +0200 Subject: [PATCH 28/41] Fix typo in Request Guild Members relation --- assets/openapi.json | Bin 612539 -> 614194 bytes assets/schemas.json | Bin 23452665 -> 23503872 bytes src/gateway/opcodes/RequestGuildMembers.ts | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/openapi.json b/assets/openapi.json index 3ae04496935b81802b8fd617001e43e8d820017b..50d4fca1fb924f0737abce6130996d1f285551f9 100644 GIT binary patch delta 391 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 Date: Wed, 14 Aug 2024 14:55:20 +0000 Subject: [PATCH 29/41] Bump follow-redirects from 1.15.2 to 1.15.6 Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.6. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.6) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | Bin 300713 -> 300680 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/package-lock.json b/package-lock.json index 875aba1b5a898e35da28cc0a36d5a6278844ed5b..691d5f586d38cd97ab26a5e4d16f596a81ed4bce 100644 GIT binary patch delta 155 zcmZ3vR;XjG&<00oB_c9!<-ER5T?voNXTPT%Irs6U;n zh)Hwup>BoHus|PoW8-`S6Ys=8i_*wUqmnScq?DrY0>i-Y3O~QpMD4s>PbU-CFkd5! zGJ}lV07LUC^F((;?Q+L#zpAi86YtcBkV0c4Bg4v6)4~w1082x!^63|*F=|a;w~9$= Va>GoKc8+2uAZFgqQOwey3;@-jH}3!d From 43703be6574a1e5536bb6f74f7ec8d53f692adb3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:55:24 +0000 Subject: [PATCH 30/41] Bump axios from 1.4.0 to 1.7.4 Bumps [axios](https://github.com/axios/axios) from 1.4.0 to 1.7.4. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.4.0...v1.7.4) --- updated-dependencies: - dependency-name: axios dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | Bin 300713 -> 300680 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/package-lock.json b/package-lock.json index 875aba1b5a898e35da28cc0a36d5a6278844ed5b..da400d3f328ef6f40add7bc9b3232b367e845ca4 100644 GIT binary patch delta 279 zcmV+y0qFjztrCc>60l7KlV1dLgW&|X;RFG~jR7~8p-2HRlU=b612HZ)lX0mbRYY}b zZDB@IZdo=#dO<-YVL?Y^MMY9lS7SptaWgMeS5bFNa7a=( zT2f3)RAf;zVM0VqHZ(S#m*dPj+EuQdCbxZgz7|D=TqBaYmQ% dmH{f4&#D30oB_c9!kiSr||EPUoJ;WH{MoBj5C( ziA*Y?!6p&T;l8f9Rb}~RCLxyYX=Q;f{t@YsK`vhU<;B5K$>v^VDd`oarG~{R243aG zr2#&LNyfpExuwO4>Ds9&Ze~@PmA0Cujnv)N8D};sx`nVe#=Np)KCk9%SMrIn7g!v_< z6onTU28LJo`K2am=jD1jnYf1e8d;PXWaI`Inpc@8x*KYjJ7)V;g%z53r$&Sn8XFlI rR;HR3hIj>78hVvazc7taYx=rXOiGg*W{R|P6f*%a^LCD6mIh@2)R$!A From d8fc8ce9ee0b2f246a105deb7df62f161b277ddf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:59:15 +0000 Subject: [PATCH 31/41] Bump tar from 6.1.15 to 6.2.1 Bumps [tar](https://github.com/isaacs/node-tar) from 6.1.15 to 6.2.1. - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-tar/compare/v6.1.15...v6.2.1) --- updated-dependencies: - dependency-name: tar dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | Bin 300680 -> 300678 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/package-lock.json b/package-lock.json index da400d3f328ef6f40add7bc9b3232b367e845ca4..5a94c7463150befe90145c29363f7f16d6365216 100644 GIT binary patch delta 135 zcmeBJE7Z1DsG)_ig{g&k3(Hl*>8f9u47p1Zi}ZE%fuye4^b6~mltW#jOe+0@LduJh zLbCMDeWM}^jmow4quf$F&HSVE(=3DCJc6{%4bwftLZdSL0|Pv&!km2y%7XF&(kl~v m3(~?XDzf!W{Qb%jjokx-EB!pZgUZY%KYXRIeYFwG-DCh)MlYWL delta 140 zcmV;70CWF_trCc>5`csOgaU*Ev;@^L1Tii#HJ8CT1TPF>axX0}bYXHWHZC!fz?>pg zFM3OKIaPUXXKr{&R4+wAM_F}vVNF?QPf1E?P&hGRVmVr7Ye`ITG;dB!P)1laFmZWV uZFM(lQffJIWms=`ZAwULM{Ht9Fe_tiGIe<|S3`3#Fq7fxAh)S91l?oyXfV$J From 3ceb6af398ec3beecd34f49374f4e749f9bd28b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:59:19 +0000 Subject: [PATCH 32/41] Bump ws from 8.13.0 to 8.17.1 Bumps [ws](https://github.com/websockets/ws) from 8.13.0 to 8.17.1. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/8.13.0...8.17.1) --- updated-dependencies: - dependency-name: ws dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | Bin 300680 -> 300680 bytes package.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index da400d3f328ef6f40add7bc9b3232b367e845ca4..d1e8c5f845f4548918d0d4a21ba57a21f33e7abf 100644 GIT binary patch delta 157 zcmeBJE7Y-8Xv1YzW^+Bm=4-6&*H{^WmQnS4(g9_b3Eegu=k{pwgiuH{{(t`pjwcP`!KfKJSw!Ocb H2$KzMpYXk+!Fsvw236d09k$VX$_9Wp+uWkB?7=zqXI#^oN%j)wcI{v;5=& E00*Tt00000 diff --git a/package.json b/package.json index ac42c7674..ef2ac1766 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "typeorm": "^0.3.17", "typescript-json-schema": "^0.50.1", "wretch": "^2.6.0", - "ws": "^8.13.0" + "ws": "^8.17.1" }, "_moduleAliases": { "@spacebar/api": "dist/api", From b1a840c884e2dac7a0dd7f467d39e0ce3b0a5fcd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:59:19 +0000 Subject: [PATCH 33/41] Bump express from 4.18.2 to 4.19.2 Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2) --- updated-dependencies: - dependency-name: express dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- package-lock.json | Bin 300680 -> 301797 bytes package.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index da400d3f328ef6f40add7bc9b3232b367e845ca4..23e2d2e9646b0f243da1f52721853616d61be819 100644 GIT binary patch delta 570 zcmXw!&ui0Q0EYR#RHz3p1Bb%EZUaFwXp*jJdk}21m?mk{to_ooI|ymkw5DmBEJ=Ul z>S5{*3KhSjn>P=F7b){Lul@llcu>L1Jb4i|RICRN&*eQlFVFp#OW$5Ey*i77+`aSo z{&^hC&*Sj=Es(>jBIH4Cc5<+)r4c*m+CC!rN;wM6URJUT#e5YNt#UV@)Jo*!SxT=n zZh0%!nKs5uX=kuQhHYV2nFu6h8cszrb-CrNzc>O@{854Q%$w8r^LMa>*Z#mYP0P^c6dC=nOLJ9)vs|Ic216$6 zOh(FB6*h%1O7eBPW2K~iG@?e%mP;D}%C&3h-7UM^EQY3AZ0Q4^Esd>IL5^5|w2Q$n z$j>jT@Fv9kF_FN}lOTzo91~Y&Zx0e$HWIw4Yw?QBiJs8yG|-UC8U-E0c6wwXP*D_AMyN*7N>xv$_%2IHq_LTGf~@Cr(cQgm)){hj>wo_;e(-@-T_zT0z#{>WX delta 312 zcmV-80muI3wGxP}60rFQ0XT#D2#5Lz0k`@H0wzEKIF}(w0!0BhlfjE2RW&bAb4)gA zM^I#DQ7>#uO)*tvXK86cPaNdij&F_%e70#*Svx1dS_P6U^( z>;VRsoKXT60X36x959#Q>j4s%5gq~}R#{pzM^$-lFK;+!W=l$JZfsaLXE%6pG-r8F zHcwM^Xh%>>VPQveb4Nllac4wTQ!r;xXLM9{Z+TiwRCGB(Vm4$$G-zgHIb>EfVRCDB zV=qrbZE|;)fqeo*m*7(Z6}Qd*0~!Ih;8Ow)0hfN&10lD2=mF~immpmN1&6d)0*AC% K1GltT1TjL9zHO5L diff --git a/package.json b/package.json index ac42c7674..6698e9235 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "eslint": "^8.56.0", - "express": "^4.18.2", + "express": "^4.19.2", "husky": "^8.0.3", "prettier": "^2.8.8", "pretty-quick": "^3.1.3", From 37021b26fbfd2232e2b944c71898d0164937a7df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:59:21 +0000 Subject: [PATCH 34/41] Bump nodemailer from 6.9.4 to 6.9.14 Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 6.9.4 to 6.9.14. - [Release notes](https://github.com/nodemailer/nodemailer/releases) - [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodemailer/nodemailer/compare/v6.9.4...v6.9.14) --- updated-dependencies: - dependency-name: nodemailer dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | Bin 300680 -> 300683 bytes package.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index da400d3f328ef6f40add7bc9b3232b367e845ca4..87f54a57fac4db4535ce7afab71822040afb0742 100644 GIT binary patch delta 147 zcmeBJE7ZMKXu~!ZM#JW9EbTxVh?%x;V_{zE#ArCZ(SuQcdY3!1%=Dz)%!*+y`AG%( zsYx#0PGwb=hQ&!GLEah3Re3%k8AaX|-X)gi?w&?&jyaB%mD*|9hL&08#-UZl#ZD3V xIbkKa$q`wJ`9+y!g~drpNm(wD+FscuCi+Q%j?*7rW>jf+@L=BV;KAbI2LQ_hGYtR$ delta 151 zcmeBPE7Y-8Xu~#^=It!)+gTWam}&cV7Urc+)0;dP^{4l^Gs{d4TB#7~9FY}RRumW& zW}E`YpP~uUV5?+~WS(xbR;o_GPP?eZvnv~}2 zTvp+3SeD_KnUfkE9&Tw+XrAX|;Ad=>JALDNX0_?_xS15DOZhT$x958>Z_oE&k?{in D)q*o9 diff --git a/package.json b/package.json index ac42c7674..270a7db09 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "node-2fa": "^2.0.3", "node-fetch": "^2.6.12", "node-os-utils": "^1.3.7", - "nodemailer": "^6.9.4", + "nodemailer": "^6.9.14", "picocolors": "^1.0.0", "probe-image-size": "^7.2.3", "proxy-agent": "^6.3.0", From bff355bc8140928b0f53c4c0c926020fa44f33bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:59:26 +0000 Subject: [PATCH 35/41] Bump jose from 4.14.4 to 4.15.9 Bumps [jose](https://github.com/panva/jose) from 4.14.4 to 4.15.9. - [Release notes](https://github.com/panva/jose/releases) - [Changelog](https://github.com/panva/jose/blob/v4.15.9/CHANGELOG.md) - [Commits](https://github.com/panva/jose/compare/v4.14.4...v4.15.9) --- updated-dependencies: - dependency-name: jose dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | Bin 300680 -> 300680 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/package-lock.json b/package-lock.json index da400d3f328ef6f40add7bc9b3232b367e845ca4..c19a5170f58fdc420bc774b2eb184b4105ef4869 100644 GIT binary patch delta 132 zcmV-~0DJ$4trCc>5`csOv;xn`0X36>oiLWl0uKQ-mytaIAyzSVRZ&1M^|Vxa#}|?Ic0KiPeM;h mYE>^!LUV3IRz_xFaC1_6Hf}{hm!Z=FJ%_}~0=LA<1H{W5K`>+h delta 131 zcmV-}0DS+5trCc>5`csOv;xn`0W_B}LIE(A$^s7oG?P%NAysNPM`%oGOKnv_Xe(rM zMp$fEF=0wWVstf2O=)6vOJXZzH)=6TVNXg&HghsnYFIH)QEXQ$PfJ2Cb3`;@FEv_O lMl@1mZCPzhQ*LBWZ&fo)bT2cPpwj|Ahs4SPx5UZ=#LIeME=T|X From d710350f6800e82356e6ebd62939e1264201aaf5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:59:35 +0000 Subject: [PATCH 36/41] Bump fast-xml-parser and @aws-sdk/client-s3 Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) to 4.4.1 and updates ancestor dependency [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3). These dependencies need to be updated together. Updates `fast-xml-parser` from 4.2.5 to 4.4.1 - [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases) - [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v4.2.5...v4.4.1) Updates `@aws-sdk/client-s3` from 3.388.0 to 3.629.0 - [Release notes](https://github.com/aws/aws-sdk-js-v3/releases) - [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.629.0/clients/client-s3) --- updated-dependencies: - dependency-name: fast-xml-parser dependency-type: indirect - dependency-name: "@aws-sdk/client-s3" dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | Bin 300680 -> 305955 bytes package.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index da400d3f328ef6f40add7bc9b3232b367e845ca4..2e6f0ae44e42e770cf54d506333f02bf838afe7c 100644 GIT binary patch delta 19507 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 delta 17695 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 diff --git a/package.json b/package.json index ac42c7674..57f825720 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "typescript": "^4.9.5" }, "dependencies": { - "@aws-sdk/client-s3": "^3.385.0", + "@aws-sdk/client-s3": "^3.629.0", "@sentry/integrations": "^7.66.0", "@sentry/node": "^7.66.0", "ajv": "8.6.2", From 76ca70cc043fcf955603c2068003365808bb58a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 15:01:04 +0000 Subject: [PATCH 37/41] Bump braces from 3.0.2 to 3.0.3 Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3. - [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3) --- updated-dependencies: - dependency-name: braces dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | Bin 300680 -> 300680 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/package-lock.json b/package-lock.json index da400d3f328ef6f40add7bc9b3232b367e845ca4..328909453bb0ee35d60b29d6d7ee4b303bca243b 100644 GIT binary patch delta 279 zcmeBJE7Y-8s9_7^uI1BJk22{`?~i3sm9?RKHe5lk(nkX;V$V$UX{kFNkLUc zMG^VsE){t>DJDtTfldYn$;tYLQRSA7;d#ymnU+;0E?$`>nPnw`Wr?1a-ibby-bKmU b8BXB_mD3pum<-xaW-tLU^Y)V&Ec5`eS;x~G@0yaO+nk6i%;m+q$l50{n{1RzynICX6+Ofgb=O*2 Date: Wed, 14 Aug 2024 11:07:18 -0400 Subject: [PATCH 38/41] Update 1723644478176-webhookSourceChannel.ts --- .../migration/postgres/1723644478176-webhookSourceChannel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/migration/postgres/1723644478176-webhookSourceChannel.ts b/src/util/migration/postgres/1723644478176-webhookSourceChannel.ts index babcf488c..63ce961fb 100644 --- a/src/util/migration/postgres/1723644478176-webhookSourceChannel.ts +++ b/src/util/migration/postgres/1723644478176-webhookSourceChannel.ts @@ -5,7 +5,7 @@ export class WebhookSourceChannel1723644478176 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { await queryRunner.query( - "ALTER TABLE webhooks ADD COLUMN source_channel_id VARCHAR(255) NULL DEFAULT NULL AFTER source_guild_id", + "ALTER TABLE webhooks ADD COLUMN source_channel_id VARCHAR(255) NULL DEFAULT NULL", ); await queryRunner.query( "ALTER TABLE webhooks ADD CONSTRAINT FK_d64f38834fa676f6caa4786ddd6 FOREIGN KEY (source_channel_id) REFERENCES channels (id) ON UPDATE NO ACTION ON DELETE CASCADE", From fd8196b02f0da208f207ca630fd8cbf3fdc59c6c Mon Sep 17 00:00:00 2001 From: Puyodead1 Date: Wed, 14 Aug 2024 11:45:01 -0400 Subject: [PATCH 39/41] update express patch --- ...ress+4.18.2.patch => express+4.19.2.patch} | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) rename patches/{express+4.18.2.patch => express+4.19.2.patch} (78%) diff --git a/patches/express+4.18.2.patch b/patches/express+4.19.2.patch similarity index 78% rename from patches/express+4.18.2.patch rename to patches/express+4.19.2.patch index de52db601..c69be2079 100644 --- a/patches/express+4.18.2.patch +++ b/patches/express+4.19.2.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/express/lib/response.js b/node_modules/express/lib/response.js -index fede486..e3d868e 100644 +index dd7b3c8..a339896 100644 --- a/node_modules/express/lib/response.js +++ b/node_modules/express/lib/response.js @@ -27,7 +27,6 @@ var merge = require('utils-merge'); @@ -10,21 +10,15 @@ index fede486..e3d868e 100644 var cookie = require('cookie'); var send = require('send'); var extname = path.extname; -@@ -49,13 +48,6 @@ var res = Object.create(http.ServerResponse.prototype) +@@ -54,7 +53,6 @@ module.exports = res + * @private + */ - module.exports = res - --/** -- * Module variables. -- * @private -- */ -- -var charsetRegExp = /;\s*charset\s*=/; -- + var schemaAndHostRegExp = /^(?:[a-zA-Z][a-zA-Z0-9+.-]*:)?\/\/[^\\\/\?]+/; + /** - * Set status `code`. - * -@@ -164,17 +156,6 @@ res.send = function send(body) { +@@ -165,16 +163,6 @@ res.send = function send(body) { break; } @@ -38,11 +32,10 @@ index fede486..e3d868e 100644 - this.set('Content-Type', setCharset(type, 'utf-8')); - } - } -- + // determine if ETag should be generated var etagFn = app.get('etag fn') - var generateETag = !this.get('ETag') && typeof etagFn === 'function' -@@ -780,17 +761,6 @@ res.header = function header(field, val) { +@@ -781,17 +769,6 @@ res.header = function header(field, val) { ? val.map(String) : String(val); From 35dde2e6e9d47309d6728a7aad157ad22308283e Mon Sep 17 00:00:00 2001 From: "Emma [it/its]@Rory&" Date: Wed, 14 Aug 2024 20:10:09 +0200 Subject: [PATCH 40/41] Fix nix-update.sh --- flake.lock | Bin 1305 -> 1497 bytes hashes.json | 4 ++-- nix-update.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 844f26fa6bf673dc335b2aabc1e75e6c0d046c8c..29505fc365815eccadfee3b290d344f4e9d9dcd9 100644 GIT binary patch literal 1497 zcmbtTU2obj6n*Dcpga#@C-K*$J+zxfsbI8xR2y#&cEC%BY2vg%)&IUEkk%DMW7_b~yQ~6YO z11J~|2*B87K9(V%2ne52!Qp}d$U=`Zf?RGRo83m1dzZ?r^1wNni%!*2B|I+vISjs< z%ztQYAgqXwb~uUJZla~_;${B0o>v~-vqp33kN7?xKTMYE+h{igL3Fp_zs9E5jmE?A zRHw84x8L-7t49V-{RV&3R%iAMYj;9Mo1x zsB`&&@%>)c(Yz=om_p!E>e70YXRqZaj)~F!UY8fyO60p1Z6~lvC*{VrH}<#aYqhW!Q&u5+ zKM(dl=j$6tjLO(=tYw;gSCLoEK7un!@)+x^!HCPK@Rjc3nNE`?eA(AICDRYh4bm>w z)IyRtP$FKl;}lD1DMtyy2*FBf$HlZmEUHvDd7fYFN(WqYo@Bn~$0Dq68!hYJ|AW;K zd=1u$3u6C}23~K)6!UwUT<(F!ePLIjqZnKT< z7B|syIiR(FNb*jXPU(*0OO;JViNqE$pd$>WnahZ_G?5S!hnt*BA{`AL_^Flb`tFicl>`>KS{47a|&e~TyaXDYZ`wsl-@>pcv3G+$Vh4VpXYDD_^R zmPu(>lwr|%bzUSTs0BF{wV{q{Qo)fzltY9$(I#?)=@?vKtM_0?>}xE(lGPA=qxHDm XC2)50^sdW-2c6}964wq} $TMPFILE +jq '.npmDepsHash = "'$DEPS_HASH'"' hashes.json > $TMPFILE mv -- "$TMPFILE" hashes.json nom build .# || exit $? From 475aee372b9fbf7b043a417c1efc062b8dbb85c6 Mon Sep 17 00:00:00 2001 From: TomatoCake <60300461+DEVTomatoCake@users.noreply.github.com> Date: Thu, 15 Aug 2024 10:14:44 +0200 Subject: [PATCH 41/41] Send 204 status --- src/api/routes/applications/#id/bot/index.ts | 8 ++++---- .../routes/channels/#channel_id/messages/index.ts | 12 ++++++++++-- src/api/routes/read-states/ack-bulk.ts | 8 ++++---- src/api/routes/users/@me/notes.ts | 8 ++++---- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/api/routes/applications/#id/bot/index.ts b/src/api/routes/applications/#id/bot/index.ts index 3c431e3d9..af754106a 100644 --- a/src/api/routes/applications/#id/bot/index.ts +++ b/src/api/routes/applications/#id/bot/index.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -57,7 +57,7 @@ router.post( res.send({ token: await generateToken(user.id), - }).status(204); + }); }, ); diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts index db46aff71..521ab7a1b 100644 --- a/src/api/routes/channels/#channel_id/messages/index.ts +++ b/src/api/routes/channels/#channel_id/messages/index.ts @@ -155,12 +155,20 @@ router.get( } else { if (after) { if (BigInt(after) > BigInt(Snowflake.generate())) - return res.status(422); + throw new HTTPError( + "after parameter must not be greater than current time", + 422, + ); + query.where.id = MoreThan(after); query.order = { timestamp: "ASC" }; } else if (before) { if (BigInt(before) > BigInt(Snowflake.generate())) - return res.status(422); + throw new HTTPError( + "before parameter must not be greater than current time", + 422, + ); + query.where.id = LessThan(before); } diff --git a/src/api/routes/read-states/ack-bulk.ts b/src/api/routes/read-states/ack-bulk.ts index 3ee25d1ab..2f41937b1 100644 --- a/src/api/routes/read-states/ack-bulk.ts +++ b/src/api/routes/read-states/ack-bulk.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -60,7 +60,7 @@ router.post( }), ]); - return res.status(204); + return res.sendStatus(204); }, ); diff --git a/src/api/routes/users/@me/notes.ts b/src/api/routes/users/@me/notes.ts index 248e61f9f..365e73b42 100644 --- a/src/api/routes/users/@me/notes.ts +++ b/src/api/routes/users/@me/notes.ts @@ -1,17 +1,17 @@ /* 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 . */ @@ -107,7 +107,7 @@ router.put( user_id: owner.id, }); - return res.status(204); + return res.sendStatus(204); }, );