diff --git a/assets/openapi.json b/assets/openapi.json index be317720..3ae04496 100644 Binary files a/assets/openapi.json and b/assets/openapi.json differ diff --git a/assets/schemas.json b/assets/schemas.json index aecc76a2..5226bbad 100644 Binary files a/assets/schemas.json and b/assets/schemas.json differ diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts index 645c6db2..db46aff7 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] : []; } @@ -151,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); diff --git a/src/api/routes/guilds/#guild_id/bulk-ban.ts b/src/api/routes/guilds/#guild_id/bulk-ban.ts index f544103a..e5f7a0d8 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,8 @@ 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) + + 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/api/routes/users/@me/index.ts b/src/api/routes/users/@me/index.ts index ad11a428..cddc3a08 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", }, }); @@ -160,6 +160,15 @@ router.patch( }, }); } + + if (!body.password) { + throw FieldErrors({ + password: { + message: req.t("auth:login.INVALID_PASSWORD"), + code: "INVALID_PASSWORD", + }, + }); + } } if (body.discriminator) { diff --git a/src/util/config/types/subconfigurations/limits/GuildLimits.ts b/src/util/config/types/subconfigurations/limits/GuildLimits.ts index b64d9485..e77cf424 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; } diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index e70d6821..15423bdb 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts @@ -216,7 +216,7 @@ export class Message extends BaseClass { }; @Column({ type: "simple-json", nullable: true }) - components?: MessageComponent[]; + components?: ActionRowComponent[]; @Column({ type: "simple-json", nullable: true }) poll?: Poll; @@ -259,21 +259,100 @@ export class Message extends BaseClass { } export interface MessageComponent { - type: number; - style?: number; + type: MessageComponentType; +} + +export interface ActionRowComponent extends MessageComponent { + type: MessageComponentType.ActionRow; + components: ( + | ButtonComponent + | StringSelectMenuComponent + | SelectMenuComponent + | TextInputComponent + )[]; +} + +export interface ButtonComponent extends MessageComponent { + type: MessageComponentType.Button; + style: ButtonStyle; label?: string; emoji?: PartialEmoji; custom_id?: string; sku_id?: string; url?: string; disabled?: boolean; - components: MessageComponent[]; +} + +export enum ButtonStyle { + Primary = 1, + Secondary = 2, + Success = 3, + Danger = 4, + Link = 5, + Premium = 6, +} + +export interface SelectMenuComponent extends MessageComponent { + type: + | MessageComponentType.StringSelect + | MessageComponentType.UserSelect + | MessageComponentType.RoleSelect + | MessageComponentType.MentionableSelect + | MessageComponentType.ChannelSelect; + custom_id: string; + channel_types?: number[]; + placeholder?: string; + default_values?: SelectMenuDefaultOption[]; // only for non-string selects + min_values?: number; + max_values?: number; + disabled?: boolean; +} + +export interface SelectMenuOption { + label: string; + value: string; + description?: string; + emoji?: PartialEmoji; + default?: boolean; +} + +export interface SelectMenuDefaultOption { + id: string; + type: "user" | "role" | "channel"; +} + +export interface StringSelectMenuComponent extends SelectMenuComponent { + type: MessageComponentType.StringSelect; + options: SelectMenuOption[]; +} + +export interface TextInputComponent extends MessageComponent { + type: MessageComponentType.TextInput; + custom_id: string; + style: TextInputStyle; + label: string; + min_length?: number; + max_length?: number; + required?: boolean; + value?: string; + placeholder?: string; +} + +export enum TextInputStyle { + Short = 1, + Paragraph = 2, } export enum MessageComponentType { Script = 0, // self command script ActionRow = 1, Button = 2, + StringSelect = 3, + TextInput = 4, + UserSelect = 5, + RoleSelect = 6, + MentionableSelect = 7, + ChannelSelect = 8, } export interface Embed { diff --git a/src/util/schemas/MessageCreateSchema.ts b/src/util/schemas/MessageCreateSchema.ts index d7e47556..15537ca8 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 { Embed, MessageComponent, PollAnswer, PollMedia } from "@spacebar/util"; +import { + ActionRowComponent, + Embed, + PollAnswer, + PollMedia, +} from "@spacebar/util"; export type MessageCreateAttachment = { id: string; @@ -54,7 +59,7 @@ export interface MessageCreateSchema { **/ attachments?: MessageCreateAttachment[]; 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 6121983e..ec41965b 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; }