diff --git a/assets/email_templates/new_login_location.html b/assets/email_templates/new_login_location.html index f1c5f8c5..b8c4a4fb 100644 --- a/assets/email_templates/new_login_location.html +++ b/assets/email_templates/new_login_location.html @@ -1,76 +1,87 @@ - + + + + + + + Verify {instanceName} Login from New Location - - - - - - Verify {instanceName} Login from New Location + + - .ExternalClass { - width: 100%; - } - - - - -
- Branding + Branding -
+
-

+

- Hey {userUsername}, -

-

- It looks like someone tried to log into your {instanceName} - account from a new location. If this is you, follow the link - below to authorize logging in from this location on your - account. If this isn't you, we suggest changing your - password as soon as possible. -

-

- IP Address: {ipAddress} -
- Location: {locationCity}, {locationRegion}, - {locationCountryName} -

-
-
+ Hey {userUsername}, +

+

+ It looks like someone tried to log into your {instanceName} + account from a new location. If this is you, follow the link + below to authorize logging in from this location on your + account. If this isn't you, we suggest changing your + password as soon as possible. +

+

+ IP Address: {ipAddress} +
+ Location: {locationCity}, {locationRegion}, + {locationCountryName} +

+
+ -
-
Verify Login +
+
+
-

- Alternatively, you can directly paste this link into - your browser: -

- {actionUrl} + " + > +

+ Alternatively, you can directly paste this link into + your browser: +

+ {actionUrl} +
-
- - + diff --git a/assets/email_templates/password_changed.html b/assets/email_templates/password_changed.html index d0426279..7d368a0a 100644 --- a/assets/email_templates/password_changed.html +++ b/assets/email_templates/password_changed.html @@ -1,4 +1,4 @@ - + @@ -22,7 +22,7 @@ -
+
Branding + + + + + + + Password Reset Request for {instanceName} - - - - - - Password Reset Request for {instanceName} + + - .ExternalClass { - width: 100%; - } - - - - -
- Branding + Branding -
+
-

+

- Hey {userUsername}, -

-

- Your {instanceName} password can be reset by clicking the - button below. If you did not request a new password, please - ignore this email. -

-
-
+ Hey {userUsername}, +

+

+ Your {instanceName} password can be reset by clicking the + button below. If you did not request a new password, please + ignore this email. +

+
+ -
-
-

- Alternatively, you can directly paste this link into - your browser: -

- {actionUrl} + " + >Reset Password +
+
+
+

+ Alternatively, you can directly paste this link into + your browser: +

+ {actionUrl} +
-
- - + diff --git a/assets/email_templates/phone_removed.html b/assets/email_templates/phone_removed.html index 7cc552e9..bcbc8f18 100644 --- a/assets/email_templates/phone_removed.html +++ b/assets/email_templates/phone_removed.html @@ -1,4 +1,4 @@ - + @@ -22,7 +22,7 @@ -
+
Branding + @@ -22,7 +22,7 @@ -
+
diff --git a/assets/openapi.json b/assets/openapi.json index f649c9b2..2788cdb0 100644 Binary files a/assets/openapi.json and b/assets/openapi.json differ diff --git a/assets/public/0c8138dcc0dfe2689cdd73f7952c2475.png b/assets/public/0c8138dcc0dfe2689cdd73f7952c2475.png deleted file mode 100644 index 3e8eeae9..00000000 Binary files a/assets/public/0c8138dcc0dfe2689cdd73f7952c2475.png and /dev/null differ diff --git a/assets/public/22341bdb500c7b63a93bbce957d1601e.png b/assets/public/22341bdb500c7b63a93bbce957d1601e.png new file mode 100644 index 00000000..c8bf93d0 Binary files /dev/null and b/assets/public/22341bdb500c7b63a93bbce957d1601e.png differ diff --git a/assets/public/4a8562cf00887030c416d3ec2d46385a.png b/assets/public/4a8562cf00887030c416d3ec2d46385a.png new file mode 100644 index 00000000..73426bcd Binary files /dev/null and b/assets/public/4a8562cf00887030c416d3ec2d46385a.png differ diff --git a/assets/public/5ac2728593bb455250d11b848a0c36c6.png b/assets/public/5ac2728593bb455250d11b848a0c36c6.png deleted file mode 100644 index 9f137906..00000000 Binary files a/assets/public/5ac2728593bb455250d11b848a0c36c6.png and /dev/null differ diff --git a/assets/public/7213ab6677377974697dfdfbaf5f6a6f.png b/assets/public/7213ab6677377974697dfdfbaf5f6a6f.png new file mode 100644 index 00000000..f1fa9f0b Binary files /dev/null and b/assets/public/7213ab6677377974697dfdfbaf5f6a6f.png differ diff --git a/assets/public/823a3de61c4dc2415cc4dbc38fca4299.png b/assets/public/823a3de61c4dc2415cc4dbc38fca4299.png deleted file mode 100644 index 9b92bd2f..00000000 Binary files a/assets/public/823a3de61c4dc2415cc4dbc38fca4299.png and /dev/null differ diff --git a/assets/public/9b0bb198936784c45c72833cc426cc55.png b/assets/public/9b0bb198936784c45c72833cc426cc55.png new file mode 100644 index 00000000..367051ce Binary files /dev/null and b/assets/public/9b0bb198936784c45c72833cc426cc55.png differ diff --git a/assets/public/9d6ddb4e4d899a533a8cc617011351c9.png b/assets/public/9d6ddb4e4d899a533a8cc617011351c9.png new file mode 100644 index 00000000..740cdd25 Binary files /dev/null and b/assets/public/9d6ddb4e4d899a533a8cc617011351c9.png differ diff --git a/assets/public/addd2f3268df46459e1d6012ad8e75bd.png b/assets/public/addd2f3268df46459e1d6012ad8e75bd.png deleted file mode 100644 index 62c599a7..00000000 Binary files a/assets/public/addd2f3268df46459e1d6012ad8e75bd.png and /dev/null differ diff --git a/assets/public/c4e0c8300fa491d94acfd2a1fb26cea8.png b/assets/public/c4e0c8300fa491d94acfd2a1fb26cea8.png deleted file mode 100644 index bd7afef2..00000000 Binary files a/assets/public/c4e0c8300fa491d94acfd2a1fb26cea8.png and /dev/null differ diff --git a/assets/public/d9977836b82058bf2f74eebd50edc095.png b/assets/public/d9977836b82058bf2f74eebd50edc095.png new file mode 100644 index 00000000..bb73cd99 Binary files /dev/null and b/assets/public/d9977836b82058bf2f74eebd50edc095.png differ diff --git a/assets/public/e56a89224be0b2b1f7c04eca975be468.png b/assets/public/e56a89224be0b2b1f7c04eca975be468.png deleted file mode 100644 index 67ee7bbd..00000000 Binary files a/assets/public/e56a89224be0b2b1f7c04eca975be468.png and /dev/null differ diff --git a/assets/public/verify.html b/assets/public/verify.html new file mode 100644 index 00000000..c70d7709 --- /dev/null +++ b/assets/public/verify.html @@ -0,0 +1,147 @@ + + + + + + + Spacebar Server + + + + + + + + + +
+ Spacebar Logo + +
+

Verifying your email

+

Please wait...

+
+
+ + + + diff --git a/assets/schemas.json b/assets/schemas.json index 7a57dfcf..00f86b36 100644 Binary files a/assets/schemas.json and b/assets/schemas.json differ diff --git a/src/api/Server.ts b/src/api/Server.ts index 0f5df490..bea75d7e 100644 --- a/src/api/Server.ts +++ b/src/api/Server.ts @@ -18,15 +18,15 @@ import { Config, - Email, - initDatabase, - initEvent, - JSONReplacer, - registerRoutes, - Sentry, - WebAuthn, ConnectionConfig, ConnectionLoader, + Email, + JSONReplacer, + Sentry, + WebAuthn, + initDatabase, + initEvent, + registerRoutes, } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { Server, ServerOptions } from "lambert-server"; @@ -143,6 +143,10 @@ export class SpacebarServer extends Server { res.sendFile(path.join(PUBLIC_ASSETS_FOLDER, "index.html")), ); + app.get("/verify", (req, res) => + res.sendFile(path.join(PUBLIC_ASSETS_FOLDER, "verify.html")), + ); + this.app.use(ErrorHandler); Sentry.errorHandler(this.app); diff --git a/src/api/routes/auth/verify/index.ts b/src/api/routes/auth/verify/index.ts index 49f74277..32c3f305 100644 --- a/src/api/routes/auth/verify/index.ts +++ b/src/api/routes/auth/verify/index.ts @@ -85,7 +85,7 @@ router.post( user = userTokenData.user; } catch { throw FieldErrors({ - password: { + token: { message: req.t("auth:password_reset.INVALID_TOKEN"), code: "INVALID_TOKEN", }, diff --git a/src/api/routes/channels/#channel_id/index.ts b/src/api/routes/channels/#channel_id/index.ts index 99f9a647..291b6472 100644 --- a/src/api/routes/channels/#channel_id/index.ts +++ b/src/api/routes/channels/#channel_id/index.ts @@ -50,7 +50,13 @@ router.get( const channel = await Channel.findOneOrFail({ where: { id: channel_id }, }); + if (!channel.guild_id) return res.send(channel); + channel.position = await Channel.calculatePosition( + channel_id, + channel.guild_id, + channel.guild, + ); return res.send(channel); }, ); diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts b/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts index 5ca645c0..6362941c 100644 --- a/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts +++ b/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.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 . */ @@ -56,6 +56,7 @@ router.post( edited_timestamp: null, flags: 1, components: [], + poll: {}, }).status(200); }, ); diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/index.ts b/src/api/routes/channels/#channel_id/messages/#message_id/index.ts index c4d2e1e8..211cf997 100644 --- a/src/api/routes/channels/#channel_id/messages/#message_id/index.ts +++ b/src/api/routes/channels/#channel_id/messages/#message_id/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 . */ @@ -91,11 +91,10 @@ router.patch( } } else rights.hasThrow("SELF_EDIT_MESSAGES"); + // @ts-expect-error Something is wrong with message_reference here, TS complains since "channel_id" is optional in MessageCreateSchema const new_message = await handleMessage({ ...message, // TODO: should message_reference be overridable? - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore message_reference: message.message_reference, ...body, author_id: message.author_id, diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts index a5bfcfd7..645c6db2 100644 --- a/src/api/routes/channels/#channel_id/messages/index.ts +++ b/src/api/routes/channels/#channel_id/messages/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 . */ @@ -183,9 +183,17 @@ router.get( const uri = y.proxy_url.startsWith("http") ? y.proxy_url : `https://example.org${y.proxy_url}`; - y.proxy_url = `${endpoint == null ? "" : endpoint}${ - new URL(uri).pathname - }`; + + let pathname = new URL(uri).pathname; + while ( + pathname.split("/")[0] != "attachments" && + pathname.length > 30 + ) { + pathname = pathname.split("/").slice(1).join("/"); + } + if (!endpoint?.endsWith("/")) pathname = "/" + pathname; + + y.proxy_url = `${endpoint == null ? "" : endpoint}${pathname}`; }); /** diff --git a/src/api/routes/channels/#channel_id/permissions.ts b/src/api/routes/channels/#channel_id/permissions.ts index d3edb0fa..fe289f23 100644 --- a/src/api/routes/channels/#channel_id/permissions.ts +++ b/src/api/routes/channels/#channel_id/permissions.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,6 +53,11 @@ router.put( where: { id: channel_id }, }); if (!channel.guild_id) throw new HTTPError("Channel not found", 404); + channel.position = await Channel.calculatePosition( + channel_id, + channel.guild_id, + channel.guild, + ); if (body.type === 0) { if (!(await Role.count({ where: { id: overwrite_id } }))) diff --git a/src/api/routes/connections/index.ts b/src/api/routes/connections/index.ts new file mode 100644 index 00000000..37c20a37 --- /dev/null +++ b/src/api/routes/connections/index.ts @@ -0,0 +1,45 @@ +/* + 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 { ConnectionConfig } from "@spacebar/util"; +import { Request, Response, Router } from "express"; +const router = Router(); + +router.get( + "/", + route({ + responses: { + 200: { + body: "APIConnectionsConfiguration", + }, + }, + }), + async (req: Request, res: Response) => { + const config = ConnectionConfig.get(); + + Object.keys(config).forEach((key) => { + delete config[key].clientId; + delete config[key].clientSecret; + }); + + res.json(config); + }, +); + +export default router; diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts index 68208fee..3488b64d 100644 --- a/src/api/routes/guilds/#guild_id/channels.ts +++ b/src/api/routes/guilds/#guild_id/channels.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,6 +41,15 @@ router.get( const { guild_id } = req.params; const channels = await Channel.find({ where: { guild_id } }); + for await (const channel of channels) { + channel.position = await Channel.calculatePosition( + channel.id, + guild_id, + channel.guild, + ); + } + channels.sort((a, b) => a.position - b.position); + res.json(channels); }, ); @@ -71,6 +80,11 @@ router.post( { ...body, guild_id }, req.user_id, ); + channel.position = await Channel.calculatePosition( + channel.id, + guild_id, + channel.guild, + ); res.status(201).json(channel); }, diff --git a/src/api/routes/guilds/#guild_id/emojis.ts b/src/api/routes/guilds/#guild_id/emojis.ts index ef28f989..f3f0fd8b 100644 --- a/src/api/routes/guilds/#guild_id/emojis.ts +++ b/src/api/routes/guilds/#guild_id/emojis.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 . */ @@ -125,6 +125,7 @@ router.post( const user = await User.findOneOrFail({ where: { id: req.user_id } }); body.image = (await handleFile(`/emojis/${id}`, body.image)) as string; + const mimeType = body.image.split(":")[1].split(";")[0]; const emoji = await Emoji.create({ id: id, guild_id: guild_id, @@ -132,7 +133,10 @@ router.post( require_colons: body.require_colons ?? undefined, // schema allows nulls, db does not user: user, managed: false, - animated: false, // TODO: Add support animated emojis + animated: + mimeType == "image/gif" || + mimeType == "image/apng" || + mimeType == "video/webm", available: true, roles: [], }).save(); diff --git a/src/api/routes/guilds/#guild_id/messages/search.ts b/src/api/routes/guilds/#guild_id/messages/search.ts index 637d1e43..94adf9c6 100644 --- a/src/api/routes/guilds/#guild_id/messages/search.ts +++ b/src/api/routes/guilds/#guild_id/messages/search.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 . */ @@ -162,6 +162,7 @@ router.get( edited_timestamp: x.edited_timestamp, flags: x.flags, components: x.components, + poll: x.poll, hit: true, }, ]); diff --git a/src/api/routes/users/#id/profile.ts b/src/api/routes/users/#id/profile.ts index eecec0f3..db0922d6 100644 --- a/src/api/routes/users/#id/profile.ts +++ b/src/api/routes/users/#id/profile.ts @@ -18,6 +18,7 @@ import { route } from "@spacebar/api"; import { + Badge, Member, PrivateUserProjection, User, @@ -98,6 +99,9 @@ router.get( bio: guild_member?.bio || "", guild_id, }; + + const badges = await Badge.find(); + res.json({ connected_accounts: user.connected_accounts.filter( (x) => x.visibility != 0, @@ -111,6 +115,7 @@ router.get( user_profile: userProfile, guild_member: guild_member?.toPublicMember(), guild_member_profile: guild_id && guildMemberProfile, + badges: badges.filter((x) => user.badge_ids?.includes(x.id)), }); }, ); diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts index 6172a3d0..c3658668 100644 --- a/src/api/util/handlers/Message.ts +++ b/src/api/util/handlers/Message.ts @@ -1,57 +1,57 @@ /* 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 * as Sentry from "@sentry/node"; +import { EmbedHandlers } from "@spacebar/api"; import { + Application, + Attachment, Channel, + Config, Embed, + EmbedCache, emitEvent, - Guild, - Message, - MessageCreateEvent, - MessageUpdateEvent, + EVERYONE_MENTION, getPermission, getRights, + Guild, + HERE_MENTION, + Message, + MessageCreateEvent, + MessageCreateSchema, + MessageType, + MessageUpdateEvent, + Role, + ROLE_MENTION, + Sticker, + User, //CHANNEL_MENTION, USER_MENTION, - ROLE_MENTION, - Role, - EVERYONE_MENTION, - HERE_MENTION, - MessageType, - User, - Application, Webhook, - Attachment, - Config, - Sticker, - MessageCreateSchema, - EmbedCache, } from "@spacebar/util"; import { HTTPError } from "lambert-server"; import { In } from "typeorm"; -import { EmbedHandlers } from "@spacebar/api"; -import * as Sentry from "@sentry/node"; const allow_empty = false; // TODO: check webhook, application, system author, stickers // TODO: embed gifs/videos/images const LINK_REGEX = - /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/g; + /?/g; export async function handleMessage(opts: MessageOptions): Promise { const channel = await Channel.findOneOrFail({ @@ -66,6 +66,7 @@ export async function handleMessage(opts: MessageOptions): Promise { : undefined; const message = Message.create({ ...opts, + poll: opts.poll, sticker_items: stickers, guild_id: channel.guild_id, channel_id: opts.channel_id, @@ -116,6 +117,12 @@ export async function handleMessage(opts: MessageOptions): Promise { 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( @@ -126,6 +133,8 @@ export async function handleMessage(opts: MessageOptions): Promise { "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 **/ @@ -138,7 +147,9 @@ export async function handleMessage(opts: MessageOptions): Promise { !opts.content && !opts.embeds?.length && !opts.attachments?.length && - !opts.sticker_ids?.length + !opts.sticker_ids?.length && + !opts.poll && + !opts.components?.length ) { throw new HTTPError("Empty messages are not allowed", 50006); } @@ -213,6 +224,9 @@ export async function postHandleMessage(message: Message) { const cachePromises = []; for (const link of links) { + // Don't embed links in <> + if (link.startsWith("<") && link.endsWith(">")) continue; + const url = new URL(link); const cached = await EmbedCache.findOne({ where: { url: link } }); diff --git a/src/cdn/routes/badge-icons.ts b/src/cdn/routes/badge-icons.ts new file mode 100644 index 00000000..04e96f2f --- /dev/null +++ b/src/cdn/routes/badge-icons.ts @@ -0,0 +1,40 @@ +/* + 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 { Router, Response, Request } from "express"; +import { storage } from "../util/Storage"; +import FileType from "file-type"; +import { HTTPError } from "lambert-server"; + +const router = Router(); + +router.get("/:badge_id", async (req: Request, res: Response) => { + const { badge_id } = req.params; + const path = `badge-icons/${badge_id}`; + + const file = await storage.get(path); + if (!file) throw new HTTPError("not found", 404); + const type = await FileType.fromBuffer(file); + + res.set("Content-Type", type?.mime); + res.set("Cache-Control", "public, max-age=31536000, must-revalidate"); + + return res.send(file); +}); + +export default router; diff --git a/src/cdn/routes/embed.ts b/src/cdn/routes/embed.ts index b99396eb..95ac720d 100644 --- a/src/cdn/routes/embed.ts +++ b/src/cdn/routes/embed.ts @@ -23,12 +23,12 @@ import { HTTPError } from "lambert-server"; import { join } from "path"; const defaultAvatarHashMap = new Map([ - ["0", "823a3de61c4dc2415cc4dbc38fca4299"], - ["1", "e56a89224be0b2b1f7c04eca975be468"], - ["2", "0c8138dcc0dfe2689cdd73f7952c2475"], - ["3", "5ac2728593bb455250d11b848a0c36c6"], - ["4", "addd2f3268df46459e1d6012ad8e75bd"], - ["5", "c4e0c8300fa491d94acfd2a1fb26cea8"], + ["0", "4a8562cf00887030c416d3ec2d46385a"], + ["1", "9b0bb198936784c45c72833cc426cc55"], + ["2", "22341bdb500c7b63a93bbce957d1601e"], + ["3", "d9977836b82058bf2f74eebd50edc095"], + ["4", "9d6ddb4e4d899a533a8cc617011351c9"], + ["5", "7213ab6677377974697dfdfbaf5f6a6f"], ]); const defaultGroupDMAvatarHashMap = new Map([ diff --git a/src/connections/BattleNet/index.ts b/src/connections/BattleNet/index.ts index 4fdfccb1..8f44944c 100644 --- a/src/connections/BattleNet/index.ts +++ b/src/connections/BattleNet/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 . */ @@ -47,13 +47,15 @@ export default class BattleNetConnection extends Connection { settings: BattleNetSettings = new BattleNetSettings(); init(): void { - const settings = - ConnectionLoader.getConnectionConfig( - this.id, - this.settings, - ); + this.settings = ConnectionLoader.getConnectionConfig( + this.id, + this.settings, + ); - if (settings.enabled && (!settings.clientId || !settings.clientSecret)) + if ( + this.settings.enabled && + (!this.settings.clientId || !this.settings.clientSecret) + ) throw new Error(`Invalid settings for connection ${this.id}`); } diff --git a/src/connections/Discord/index.ts b/src/connections/Discord/index.ts index 731086f1..e5508f48 100644 --- a/src/connections/Discord/index.ts +++ b/src/connections/Discord/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 . */ @@ -43,12 +43,15 @@ export default class DiscordConnection extends Connection { settings: DiscordSettings = new DiscordSettings(); init(): void { - const settings = ConnectionLoader.getConnectionConfig( + this.settings = ConnectionLoader.getConnectionConfig( this.id, this.settings, ); - if (settings.enabled && (!settings.clientId || !settings.clientSecret)) + if ( + this.settings.enabled && + (!this.settings.clientId || !this.settings.clientSecret) + ) throw new Error(`Invalid settings for connection ${this.id}`); } diff --git a/src/connections/EpicGames/index.ts b/src/connections/EpicGames/index.ts index e5b2d336..cedfcd0a 100644 --- a/src/connections/EpicGames/index.ts +++ b/src/connections/EpicGames/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 . */ @@ -53,13 +53,15 @@ export default class EpicGamesConnection extends Connection { settings: EpicGamesSettings = new EpicGamesSettings(); init(): void { - const settings = - ConnectionLoader.getConnectionConfig( - this.id, - this.settings, - ); + this.settings = ConnectionLoader.getConnectionConfig( + this.id, + this.settings, + ); - if (settings.enabled && (!settings.clientId || !settings.clientSecret)) + if ( + this.settings.enabled && + (!this.settings.clientId || !this.settings.clientSecret) + ) throw new Error(`Invalid settings for connection ${this.id}`); } diff --git a/src/connections/Facebook/index.ts b/src/connections/Facebook/index.ts index 2bf26f34..bcb90b4c 100644 --- a/src/connections/Facebook/index.ts +++ b/src/connections/Facebook/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 . */ @@ -52,12 +52,15 @@ export default class FacebookConnection extends Connection { settings: FacebookSettings = new FacebookSettings(); init(): void { - const settings = ConnectionLoader.getConnectionConfig( + this.settings = ConnectionLoader.getConnectionConfig( this.id, this.settings, ); - if (settings.enabled && (!settings.clientId || !settings.clientSecret)) + if ( + this.settings.enabled && + (!this.settings.clientId || !this.settings.clientSecret) + ) throw new Error(`Invalid settings for connection ${this.id}`); } diff --git a/src/connections/GitHub/index.ts b/src/connections/GitHub/index.ts index 25e5f89f..78bf510e 100644 --- a/src/connections/GitHub/index.ts +++ b/src/connections/GitHub/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 . */ @@ -42,12 +42,15 @@ export default class GitHubConnection extends Connection { settings: GitHubSettings = new GitHubSettings(); init(): void { - const settings = ConnectionLoader.getConnectionConfig( + this.settings = ConnectionLoader.getConnectionConfig( this.id, this.settings, ); - if (settings.enabled && (!settings.clientId || !settings.clientSecret)) + if ( + this.settings.enabled && + (!this.settings.clientId || !this.settings.clientSecret) + ) throw new Error(`Invalid settings for connection ${this.id}`); } diff --git a/src/connections/Reddit/index.ts b/src/connections/Reddit/index.ts index 149cce02..0db23731 100644 --- a/src/connections/Reddit/index.ts +++ b/src/connections/Reddit/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 . */ @@ -54,12 +54,15 @@ export default class RedditConnection extends Connection { settings: RedditSettings = new RedditSettings(); init(): void { - const settings = ConnectionLoader.getConnectionConfig( + this.settings = ConnectionLoader.getConnectionConfig( this.id, this.settings, ); - if (settings.enabled && (!settings.clientId || !settings.clientSecret)) + if ( + this.settings.enabled && + (!this.settings.clientId || !this.settings.clientSecret) + ) throw new Error(`Invalid settings for connection ${this.id}`); } diff --git a/src/connections/Spotify/index.ts b/src/connections/Spotify/index.ts index ece404d8..4eb12602 100644 --- a/src/connections/Spotify/index.ts +++ b/src/connections/Spotify/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 . */ @@ -63,12 +63,16 @@ export default class SpotifyConnection extends RefreshableConnection { * So to prevent spamming the spotify api we disable the ability to refresh. */ this.refreshEnabled = false; - const settings = ConnectionLoader.getConnectionConfig( + + this.settings = ConnectionLoader.getConnectionConfig( this.id, this.settings, ); - if (settings.enabled && (!settings.clientId || !settings.clientSecret)) + if ( + this.settings.enabled && + (!this.settings.clientId || !this.settings.clientSecret) + ) throw new Error(`Invalid settings for connection ${this.id}`); } diff --git a/src/connections/Twitch/index.ts b/src/connections/Twitch/index.ts index 9a6cea35..953669a1 100644 --- a/src/connections/Twitch/index.ts +++ b/src/connections/Twitch/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 . */ @@ -55,12 +55,15 @@ export default class TwitchConnection extends RefreshableConnection { settings: TwitchSettings = new TwitchSettings(); init(): void { - const settings = ConnectionLoader.getConnectionConfig( + this.settings = ConnectionLoader.getConnectionConfig( this.id, this.settings, ); - if (settings.enabled && (!settings.clientId || !settings.clientSecret)) + if ( + this.settings.enabled && + (!this.settings.clientId || !this.settings.clientSecret) + ) throw new Error(`Invalid settings for connection ${this.id}`); } diff --git a/src/connections/Twitter/index.ts b/src/connections/Twitter/index.ts index 62fd7da1..eba8ceb5 100644 --- a/src/connections/Twitter/index.ts +++ b/src/connections/Twitter/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 . */ @@ -55,12 +55,15 @@ export default class TwitterConnection extends RefreshableConnection { settings: TwitterSettings = new TwitterSettings(); init(): void { - const settings = ConnectionLoader.getConnectionConfig( + this.settings = ConnectionLoader.getConnectionConfig( this.id, this.settings, ); - if (settings.enabled && (!settings.clientId || !settings.clientSecret)) + if ( + this.settings.enabled && + (!this.settings.clientId || !this.settings.clientSecret) + ) throw new Error(`Invalid settings for connection ${this.id}`); } diff --git a/src/connections/Xbox/index.ts b/src/connections/Xbox/index.ts index 935ff7ab..84066def 100644 --- a/src/connections/Xbox/index.ts +++ b/src/connections/Xbox/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 . */ @@ -62,12 +62,15 @@ export default class XboxConnection extends Connection { settings: XboxSettings = new XboxSettings(); init(): void { - const settings = ConnectionLoader.getConnectionConfig( + this.settings = ConnectionLoader.getConnectionConfig( this.id, this.settings, ); - if (settings.enabled && (!settings.clientId || !settings.clientSecret)) + if ( + this.settings.enabled && + (!this.settings.clientId || !this.settings.clientSecret) + ) throw new Error(`Invalid settings for connection ${this.id}`); } diff --git a/src/connections/Youtube/index.ts b/src/connections/Youtube/index.ts index 844803cf..38edbb0d 100644 --- a/src/connections/Youtube/index.ts +++ b/src/connections/Youtube/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 . */ @@ -62,12 +62,15 @@ export default class YoutubeConnection extends Connection { settings: YoutubeSettings = new YoutubeSettings(); init(): void { - const settings = ConnectionLoader.getConnectionConfig( + this.settings = ConnectionLoader.getConnectionConfig( this.id, this.settings, ); - if (settings.enabled && (!settings.clientId || !settings.clientSecret)) + if ( + this.settings.enabled && + (!this.settings.clientId || !this.settings.clientSecret) + ) throw new Error(`Invalid settings for connection ${this.id}`); } diff --git a/src/util/config/types/EmailConfiguration.ts b/src/util/config/types/EmailConfiguration.ts index ae9d53ba..62305d10 100644 --- a/src/util/config/types/EmailConfiguration.ts +++ b/src/util/config/types/EmailConfiguration.ts @@ -25,6 +25,7 @@ import { SendGridConfiguration } from "./subconfigurations/email/SendGrid"; export class EmailConfiguration { provider: string | null = null; + senderAddress: string | null = null; smtp: SMTPConfiguration = new SMTPConfiguration(); mailgun: MailGunConfiguration = new MailGunConfiguration(); mailjet: MailJetConfiguration = new MailJetConfiguration(); diff --git a/src/util/connections/Connection.ts b/src/util/connections/Connection.ts index 5bdebd47..638dde2c 100644 --- a/src/util/connections/Connection.ts +++ b/src/util/connections/Connection.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 . */ @@ -43,7 +43,7 @@ export abstract class Connection { */ getRedirectUri() { const endpointPublic = - Config.get().api.endpointPublic ?? "http://localhost:3001"; + Config.get().general.frontPage ?? "http://localhost:3001"; return `${endpointPublic}/connections/${this.id}/callback`; } diff --git a/src/util/dtos/UserDTO.ts b/src/util/dtos/UserDTO.ts index a24c8d96..17e4435f 100644 --- a/src/util/dtos/UserDTO.ts +++ b/src/util/dtos/UserDTO.ts @@ -24,6 +24,7 @@ export class MinimalPublicUserDTO { id: string; public_flags: number; username: string; + badge_ids?: string[] | null; constructor(user: User) { this.avatar = user.avatar; @@ -31,5 +32,6 @@ export class MinimalPublicUserDTO { this.id = user.id; this.public_flags = user.public_flags; this.username = user.username; + this.badge_ids = user.badge_ids; } } diff --git a/src/util/entities/Badge.ts b/src/util/entities/Badge.ts new file mode 100644 index 00000000..9535e207 --- /dev/null +++ b/src/util/entities/Badge.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 . +*/ + +import { Column, Entity } from "typeorm"; +import { BaseClassWithoutId } from "./BaseClass"; + +@Entity("badges") +export class Badge extends BaseClassWithoutId { + @Column({ primary: true }) + id: string; + + @Column() + description: string; + + @Column() + icon: string; + + @Column({ nullable: true }) + link?: string; +} diff --git a/src/util/entities/Member.ts b/src/util/entities/Member.ts index 65942816..3ef778ac 100644 --- a/src/util/entities/Member.ts +++ b/src/util/entities/Member.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 . */ @@ -31,7 +31,7 @@ import { PrimaryGeneratedColumn, RelationId, } from "typeorm"; -import { Ban, PublicGuildRelations } from "."; +import { Ban, Channel, PublicGuildRelations } from "."; import { ReadyGuildDTO } from "../dtos"; import { GuildCreateEvent, @@ -330,6 +330,13 @@ export class Member extends BaseClassWithoutId { relationLoadStrategy: "query", }); + for await (const channel of guild.channels) { + channel.position = await Channel.calculatePosition( + channel.id, + guild_id, + ); + } + const memberCount = await Member.count({ where: { guild_id } }); const memberPreview = ( diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index 4a9cff4a..d28c8c29 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,9 @@ export class Message extends BaseClass { @Column({ type: "simple-json", nullable: true }) components?: MessageComponent[]; + @Column({ type: "simple-json", nullable: true }) + poll?: Poll; + toJSON(): Message { return { ...this, @@ -238,6 +241,7 @@ export class Message extends BaseClass { activity: this.activity ?? undefined, application: this.application ?? undefined, components: this.components ?? undefined, + poll: this.poll ?? undefined, content: this.content ?? "", }; } @@ -249,6 +253,7 @@ export interface MessageComponent { label?: string; emoji?: PartialEmoji; custom_id?: string; + sku_id?: string; url?: string; disabled?: boolean; components: MessageComponent[]; @@ -327,3 +332,32 @@ export interface AllowedMentions { users?: string[]; replied_user?: boolean; } + +export interface Poll { + question: PollMedia; + answers: PollAnswer[]; + expiry: Date; + allow_multiselect: boolean; + results?: PollResult; +} + +export interface PollMedia { + text?: string; + emoji?: PartialEmoji; +} + +export interface PollAnswer { + answer_id?: string; + poll_media: PollMedia; +} + +export interface PollResult { + is_finalized: boolean; + answer_counts: PollAnswerCount[]; +} + +export interface PollAnswerCount { + id: string; + count: number; + me_voted: boolean; +} diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts index c6582b00..c929039e 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts @@ -49,6 +49,7 @@ export enum PublicUserEnum { premium_type, theme_colors, pronouns, + badge_ids, } export type PublicUserKeys = keyof typeof PublicUserEnum; @@ -231,6 +232,9 @@ export class User extends BaseClass { @OneToMany(() => SecurityKey, (key: SecurityKey) => key.user) security_keys: SecurityKey[]; + @Column({ type: "simple-array", nullable: true }) + badge_ids?: string[]; + // TODO: I don't like this method? validate() { if (this.discriminator) { diff --git a/src/util/entities/index.ts b/src/util/entities/index.ts index aa943dca..b2356aa7 100644 --- a/src/util/entities/index.ts +++ b/src/util/entities/index.ts @@ -20,6 +20,7 @@ export * from "./Application"; export * from "./Attachment"; export * from "./AuditLog"; export * from "./BackupCodes"; +export * from "./Badge"; export * from "./Ban"; export * from "./BaseClass"; export * from "./Categories"; diff --git a/src/util/migration/mariadb/1719776735000-newUserSettings.ts b/src/util/migration/mariadb/1719776735000-newUserSettings.ts new file mode 100644 index 00000000..f7c37ca9 --- /dev/null +++ b/src/util/migration/mariadb/1719776735000-newUserSettings.ts @@ -0,0 +1,23 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class NewUserSettings1719776735000 implements MigrationInterface { + name = "NewUserSettings1719776735000"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE `user_settings` ADD friend_discovery_flags integer NULL DEFAULT 0;", + ); + await queryRunner.query( + "ALTER TABLE `user_settings` ADD view_nsfw_guilds tinyint NULL DEFAULT 1;", + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE `user_settings` DROP COLUMN friend_discovery_flags;", + ); + await queryRunner.query( + "ALTER TABLE `user_settings` DROP COLUMN view_nsfw_guilds;", + ); + } +} diff --git a/src/util/migration/mariadb/1720157926878-messagePollObject.ts b/src/util/migration/mariadb/1720157926878-messagePollObject.ts new file mode 100644 index 00000000..c0866426 --- /dev/null +++ b/src/util/migration/mariadb/1720157926878-messagePollObject.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class MessagePollObject1720157926878 implements MigrationInterface { + name = "MessagePollObject1720157926878"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE `messages` ADD `poll` text NULL"); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE `messages` DROP COLUMN `poll`"); + } +} diff --git a/src/util/migration/mariadb/1720628601997-badges.ts b/src/util/migration/mariadb/1720628601997-badges.ts new file mode 100644 index 00000000..af298e42 --- /dev/null +++ b/src/util/migration/mariadb/1720628601997-badges.ts @@ -0,0 +1,21 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class Badges1720628601997 implements MigrationInterface { + name = "Badges1720628601997"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE \`badges\` (\`id\` varchar(255) NOT NULL, \`description\` varchar(255) NOT NULL, \`icon\` varchar(255) NOT NULL, \`link\` varchar(255) NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`, + ); + await queryRunner.query( + `ALTER TABLE \`users\` ADD \`badge_ids\` text NULL`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE \`users\` DROP COLUMN \`badge_ids\``, + ); + await queryRunner.query(`DROP TABLE \`badges\``); + } +} diff --git a/src/util/migration/mysql/1719776735000-newUserSettings.ts b/src/util/migration/mysql/1719776735000-newUserSettings.ts new file mode 100644 index 00000000..f7c37ca9 --- /dev/null +++ b/src/util/migration/mysql/1719776735000-newUserSettings.ts @@ -0,0 +1,23 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class NewUserSettings1719776735000 implements MigrationInterface { + name = "NewUserSettings1719776735000"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE `user_settings` ADD friend_discovery_flags integer NULL DEFAULT 0;", + ); + await queryRunner.query( + "ALTER TABLE `user_settings` ADD view_nsfw_guilds tinyint NULL DEFAULT 1;", + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE `user_settings` DROP COLUMN friend_discovery_flags;", + ); + await queryRunner.query( + "ALTER TABLE `user_settings` DROP COLUMN view_nsfw_guilds;", + ); + } +} diff --git a/src/util/migration/mysql/1720157926878-messagePollObject.ts b/src/util/migration/mysql/1720157926878-messagePollObject.ts new file mode 100644 index 00000000..c0866426 --- /dev/null +++ b/src/util/migration/mysql/1720157926878-messagePollObject.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class MessagePollObject1720157926878 implements MigrationInterface { + name = "MessagePollObject1720157926878"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE `messages` ADD `poll` text NULL"); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE `messages` DROP COLUMN `poll`"); + } +} diff --git a/src/util/migration/mysql/1720628601997-badges.ts b/src/util/migration/mysql/1720628601997-badges.ts new file mode 100644 index 00000000..af298e42 --- /dev/null +++ b/src/util/migration/mysql/1720628601997-badges.ts @@ -0,0 +1,21 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class Badges1720628601997 implements MigrationInterface { + name = "Badges1720628601997"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE \`badges\` (\`id\` varchar(255) NOT NULL, \`description\` varchar(255) NOT NULL, \`icon\` varchar(255) NOT NULL, \`link\` varchar(255) NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`, + ); + await queryRunner.query( + `ALTER TABLE \`users\` ADD \`badge_ids\` text NULL`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE \`users\` DROP COLUMN \`badge_ids\``, + ); + await queryRunner.query(`DROP TABLE \`badges\``); + } +} diff --git a/src/util/migration/postgres/1719776735000-newUserSettings.ts b/src/util/migration/postgres/1719776735000-newUserSettings.ts new file mode 100644 index 00000000..dbee0b0d --- /dev/null +++ b/src/util/migration/postgres/1719776735000-newUserSettings.ts @@ -0,0 +1,23 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class NewUserSettings1719776735000 implements MigrationInterface { + name = "NewUserSettings1719776735000"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE user_settings ADD COLUMN friend_discovery_flags integer DEFAULT 0;", + ); + await queryRunner.query( + "ALTER TABLE user_settings ADD COLUMN view_nsfw_guilds boolean DEFAULT true;", + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE user_settings DROP COLUMN friend_discovery_flags;", + ); + await queryRunner.query( + "ALTER TABLE user_settings DROP COLUMN view_nsfw_guilds;", + ); + } +} diff --git a/src/util/migration/postgres/1720157926878-messagePollObject.ts b/src/util/migration/postgres/1720157926878-messagePollObject.ts new file mode 100644 index 00000000..7c3c95a0 --- /dev/null +++ b/src/util/migration/postgres/1720157926878-messagePollObject.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class MessagePollObject1720157926878 implements MigrationInterface { + name = "MessagePollObject1720157926878"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE messages ADD poll text NULL"); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE messages DROP COLUMN poll"); + } +} diff --git a/src/util/migration/postgres/1720628601997-badges.ts b/src/util/migration/postgres/1720628601997-badges.ts new file mode 100644 index 00000000..f7b9958b --- /dev/null +++ b/src/util/migration/postgres/1720628601997-badges.ts @@ -0,0 +1,16 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class Badges1720628601997 implements MigrationInterface { + name = "Badges1720628601997"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "badges" ("id" character varying NOT NULL, "description" character varying NOT NULL, "icon" character varying NOT NULL, "link" character varying, CONSTRAINT "PK_8a651318b8de577e8e217676466" PRIMARY KEY ("id"))`, + ); + await queryRunner.query(`ALTER TABLE "users" ADD "badge_ids" text`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "badge_ids"`); + } +} diff --git a/src/util/schemas/MessageCreateSchema.ts b/src/util/schemas/MessageCreateSchema.ts index 57abf62f..014f6c87 100644 --- a/src/util/schemas/MessageCreateSchema.ts +++ b/src/util/schemas/MessageCreateSchema.ts @@ -1,22 +1,22 @@ /* 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 "@spacebar/util"; +import { Embed, MessageComponent, PollAnswer, PollMedia } from "@spacebar/util"; type Attachment = { id: string; @@ -42,7 +42,7 @@ export interface MessageCreateSchema { }; message_reference?: { message_id: string; - channel_id: string; + channel_id?: string; guild_id?: string; fail_if_not_exists?: boolean; }; @@ -54,6 +54,21 @@ export interface MessageCreateSchema { **/ attachments?: Attachment[]; sticker_ids?: string[]; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - components?: any[]; + components?: MessageComponent[]; + // 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 + applied_tags?: string[]; // Not implemented yet, for webhooks in forums + thread_name?: string; // Not implemented yet, for webhooks + avatar_url?: string; // Not implemented yet, for webhooks +} + +// TypeScript complains once this is used above +// eslint-disable-next-line @typescript-eslint/no-unused-vars +interface PollCreationSchema { + question: PollMedia; + answers: PollAnswer[]; + duration?: number; + allow_multiselect?: boolean; + layout_type?: number; } diff --git a/src/util/schemas/responses/GuildMessagesSearchResponse.ts b/src/util/schemas/responses/GuildMessagesSearchResponse.ts index b42aafd3..6121983e 100644 --- a/src/util/schemas/responses/GuildMessagesSearchResponse.ts +++ b/src/util/schemas/responses/GuildMessagesSearchResponse.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,9 @@ import { Attachment, Embed, + MessageComponent, MessageType, + Poll, PublicUser, Role, } from "../../entities"; @@ -40,7 +42,8 @@ export interface GuildMessagesSearchMessage { timestamp: string; edited_timestamp: string | null; flags: number; - components: unknown[]; + components: MessageComponent[]; + poll: Poll; hit: true; } diff --git a/src/util/schemas/responses/TypedResponses.ts b/src/util/schemas/responses/TypedResponses.ts index fa169c25..8214ff7b 100644 --- a/src/util/schemas/responses/TypedResponses.ts +++ b/src/util/schemas/responses/TypedResponses.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 . */ @@ -104,3 +104,10 @@ export type APIGuildVoiceRegion = GuildVoiceRegion[]; export type APILimitsConfiguration = LimitsConfiguration; export type APIStickerPackArray = StickerPack[]; + +export type APIConnectionsConfiguration = Record< + string, + { + enabled: boolean; + } +>; diff --git a/src/util/schemas/responses/UserProfileResponse.ts b/src/util/schemas/responses/UserProfileResponse.ts index 26e7e3bc..7b63542e 100644 --- a/src/util/schemas/responses/UserProfileResponse.ts +++ b/src/util/schemas/responses/UserProfileResponse.ts @@ -17,6 +17,7 @@ */ import { + Badge, Member, PublicConnectedAccount, PublicMember, @@ -52,4 +53,5 @@ export interface UserProfileResponse { user_profile: UserProfile; guild_member?: PublicMember; guild_member_profile?: PublicMemberProfile; + badges: Badge[]; } diff --git a/src/util/util/email/index.ts b/src/util/util/email/index.ts index 619cc5c3..e3382794 100644 --- a/src/util/util/email/index.ts +++ b/src/util/util/email/index.ts @@ -141,8 +141,9 @@ export const Email: { */ generateLink: async function (type, id, email) { const token = (await generateToken(id, email)) as string; + // puyodead1: this is set to api endpoint because the verification page is on the server since no clients have one, and not all 3rd party clients will have one const instanceUrl = - Config.get().general.frontPage || "http://localhost:3001"; + Config.get().api.endpointPublic || "http://localhost:3001"; const link = `${instanceUrl}/${type}#token=${token}`; return link; }, @@ -187,7 +188,9 @@ export const Email: { const message = { from: - Config.get().general.correspondenceEmail || "noreply@localhost", + Config.get().email.senderAddress || + Config.get().general.correspondenceEmail || + "noreply@localhost", to: email, subject, html, diff --git a/src/util/util/email/transports/SMTP.ts b/src/util/util/email/transports/SMTP.ts index 5b43a870..e3031943 100644 --- a/src/util/util/email/transports/SMTP.ts +++ b/src/util/util/email/transports/SMTP.ts @@ -27,9 +27,12 @@ export default async function () { if (!host || !port || secure === null || !username || !password) return console.error("[Email] SMTP has not been configured correctly."); - if (!Config.get().general.correspondenceEmail) + if ( + !Config.get().email.senderAddress && + !Config.get().general.correspondenceEmail + ) return console.error( - "[Email] Correspondence email has not been configured! This is used as the sender email address.", + '[Email] You have to configure either "email_senderAddress" or "general_correspondenceEmail" for emails to work. The configured value is used as the sender address.', ); // construct the transporter