mirror of
https://github.com/fluxerapp/fluxer.git
synced 2026-09-02 21:04:06 +03:00
refactor(api): split webhook attachment schemas (#2268)
This commit is contained in:
@@ -22220,7 +22220,7 @@
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {"schema": {"$ref": "#/components/schemas/WebhookMessageRequest"}},
|
||||
"multipart/form-data": {"schema": {"$ref": "#/components/schemas/WebhookMessageRequest"}}
|
||||
"multipart/form-data": {"schema": {"$ref": "#/components/schemas/WebhookMultipartMessageRequest"}}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34715,51 +34715,51 @@
|
||||
"items": {"$ref": "#/components/schemas/RichEmbedRequest"},
|
||||
"description": "Array of embed objects to include in the message"
|
||||
},
|
||||
"message_reference": {
|
||||
"anyOf": [{"$ref": "#/components/schemas/MessageReferenceRequest"}, {"type": "null"}],
|
||||
"description": "Reference to another message (for replies or forwards)"
|
||||
},
|
||||
"allowed_mentions": {
|
||||
"anyOf": [{"$ref": "#/components/schemas/AllowedMentionsRequest"}, {"type": "null"}],
|
||||
"description": "Controls which mentions trigger notifications"
|
||||
},
|
||||
"flags": {"$ref": "#/components/schemas/MessageFlags"},
|
||||
"nonce": {"$ref": "#/components/schemas/MessageNonceRequest"},
|
||||
"favorite_meme_id": {
|
||||
"anyOf": [{"$ref": "#/components/schemas/SnowflakeType"}, {"type": "null"}],
|
||||
"description": "ID of a favorite meme to attach"
|
||||
},
|
||||
"sticker_ids": {
|
||||
"anyOf": [
|
||||
{"type": "array", "items": {"$ref": "#/components/schemas/SnowflakeType"}, "maxItems": 3},
|
||||
{"type": "null"}
|
||||
],
|
||||
"description": "Array of sticker IDs to include (max 3)"
|
||||
},
|
||||
"tts": {"type": "boolean", "description": "Whether this is a text-to-speech message"},
|
||||
"username": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Override the default username of the webhook for this message"
|
||||
},
|
||||
"avatar_url": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Override the default avatar URL of the webhook for this message"
|
||||
},
|
||||
"attachments": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{"$ref": "#/components/schemas/ClientUploadedAttachmentRequest"},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"oneOf": [
|
||||
{"$ref": "#/components/schemas/SnowflakeType"},
|
||||
{"$ref": "#/components/schemas/Int32Type"}
|
||||
],
|
||||
"description": "Attachment ID for referencing uploaded files"
|
||||
},
|
||||
"filename": {"type": "string", "description": "Name of the file (1-1024 characters)"},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Description for the attachment (max 4096 characters)"
|
||||
},
|
||||
"content_type": {"type": "string", "description": "MIME type of the file"},
|
||||
"upload_filename": {"not": {}},
|
||||
"file_size": {"not": {}},
|
||||
"size": {"$ref": "#/components/schemas/NonNegativeSafeIntegerType"},
|
||||
"url": {"type": "string", "description": "URL of the attachment"},
|
||||
"proxy_url": {"type": "string", "description": "Proxied URL of the attachment"},
|
||||
"height": {
|
||||
"type": "integer",
|
||||
"format": "int53",
|
||||
"description": "Height of the image/video in pixels"
|
||||
},
|
||||
"width": {
|
||||
"type": "integer",
|
||||
"format": "int53",
|
||||
"description": "Width of the image/video in pixels"
|
||||
},
|
||||
"ephemeral": {"type": "boolean", "description": "Whether this attachment is ephemeral"},
|
||||
"duration": {"type": "number", "description": "Duration of audio file in seconds"},
|
||||
"waveform": {"type": "string", "description": "Base64-encoded bytearray of audio waveform"},
|
||||
"flags": {"$ref": "#/components/schemas/MessageAttachmentFlags"}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Array of attachment objects"
|
||||
"items": {"$ref": "#/components/schemas/ClientUploadedAttachmentRequest"},
|
||||
"description": "Array of attachments uploaded through the presigned upload endpoint"
|
||||
}
|
||||
}
|
||||
},
|
||||
"WebhookMultipartMessageRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {"anyOf": [{"$ref": "#/components/schemas/MessageContentRequest"}, {"type": "null"}]},
|
||||
"embeds": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/components/schemas/RichEmbedRequest"},
|
||||
"description": "Array of embed objects to include in the message"
|
||||
},
|
||||
"message_reference": {
|
||||
"anyOf": [{"$ref": "#/components/schemas/MessageReferenceRequest"}, {"type": "null"}],
|
||||
@@ -34790,6 +34790,34 @@
|
||||
"avatar_url": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Override the default avatar URL of the webhook for this message"
|
||||
},
|
||||
"attachments": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"oneOf": [{"$ref": "#/components/schemas/SnowflakeType"}, {"$ref": "#/components/schemas/Int32Type"}],
|
||||
"description": "Attachment ID for referencing uploaded files"
|
||||
},
|
||||
"filename": {"type": "string", "description": "Name of the file (1-1024 characters)"},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Description for the attachment (max 4096 characters)"
|
||||
},
|
||||
"content_type": {"type": "string", "description": "MIME type of the file"},
|
||||
"size": {"$ref": "#/components/schemas/NonNegativeSafeIntegerType"},
|
||||
"url": {"type": "string", "description": "URL of the attachment"},
|
||||
"proxy_url": {"type": "string", "description": "Proxied URL of the attachment"},
|
||||
"height": {"type": "integer", "format": "int53", "description": "Height of the image/video in pixels"},
|
||||
"width": {"type": "integer", "format": "int53", "description": "Width of the image/video in pixels"},
|
||||
"ephemeral": {"type": "boolean", "description": "Whether this attachment is ephemeral"},
|
||||
"duration": {"type": "number", "description": "Duration of audio file in seconds"},
|
||||
"waveform": {"type": "string", "description": "Base64-encoded bytearray of audio waveform"},
|
||||
"flags": {"$ref": "#/components/schemas/MessageAttachmentFlags"}
|
||||
}
|
||||
},
|
||||
"description": "Array of multipart attachment metadata objects"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
WebhookExecuteQueryRequest,
|
||||
WebhookMessageEditRequest,
|
||||
WebhookMessageRequest,
|
||||
WebhookMultipartMessageRequest,
|
||||
WebhookTokenUpdateRequest,
|
||||
WebhookUpdateRequest,
|
||||
} from '@fluxer/schema/src/domains/webhook/WebhookRequestSchemas';
|
||||
@@ -47,6 +48,14 @@ function validateWebhookMessagePayload(data: unknown): WebhookMessageRequest {
|
||||
return validationResult.data;
|
||||
}
|
||||
|
||||
function validateWebhookMultipartMessagePayload(data: unknown): WebhookMultipartMessageRequest {
|
||||
const validationResult = WebhookMultipartMessageRequest.safeParse(normalizeMessageRequestPayload(data));
|
||||
if (!validationResult.success) {
|
||||
throw InputValidationError.fromCode('message_data', ValidationErrorCodes.INVALID_MESSAGE_DATA);
|
||||
}
|
||||
return validationResult.data;
|
||||
}
|
||||
|
||||
async function parseWebhookJsonMessageData(ctx: Context<HonoEnv>): Promise<WebhookExecuteMessageData> {
|
||||
let data: unknown;
|
||||
try {
|
||||
@@ -89,10 +98,11 @@ async function parseWebhookMultipartMessageData(
|
||||
if (!parsedPayload) {
|
||||
throw InputValidationError.fromCode('message_data', ValidationErrorCodes.INVALID_MESSAGE_DATA);
|
||||
}
|
||||
const webhookData = validateWebhookMessagePayload(parsedPayload);
|
||||
const webhookData = validateWebhookMultipartMessagePayload(parsedPayload);
|
||||
return {
|
||||
...webhookData,
|
||||
...messageData,
|
||||
attachments: messageData.attachments,
|
||||
username: webhookData.username,
|
||||
avatar_url: webhookData.avatar_url,
|
||||
};
|
||||
@@ -333,7 +343,7 @@ export function WebhookController(app: HonoApp) {
|
||||
'Executes the webhook by sending a message to its configured channel. If the wait query parameter is true, returns the created message object; otherwise returns a 204 status with no content.',
|
||||
responseSchema: MessageResponseSchema,
|
||||
requestSchema: WebhookMessageRequest,
|
||||
requestFormSchema: WebhookMessageRequest,
|
||||
requestFormSchema: WebhookMultipartMessageRequest,
|
||||
statusCode: 200,
|
||||
tags: ['Webhooks'],
|
||||
}),
|
||||
|
||||
@@ -62,7 +62,7 @@ export const WebhookTokenUpdateRequest = z
|
||||
|
||||
export type WebhookTokenUpdateRequest = z.infer<typeof WebhookTokenUpdateRequest>;
|
||||
|
||||
const WebhookAttachmentRequest = z.object({
|
||||
const WebhookMultipartAttachmentRequest = z.object({
|
||||
id: z
|
||||
.union([SnowflakeType, coerceNumberFromString(Int32Type)])
|
||||
.optional()
|
||||
@@ -70,8 +70,6 @@ const WebhookAttachmentRequest = z.object({
|
||||
filename: createStringType(1, 1024).optional().describe('Name of the file (1-1024 characters)'),
|
||||
description: createStringType(1, 4096).optional().describe('Description for the attachment (max 4096 characters)'),
|
||||
content_type: createStringType(1, 256).optional().describe('MIME type of the file'),
|
||||
upload_filename: z.never().optional(),
|
||||
file_size: z.never().optional(),
|
||||
size: NonNegativeSafeIntegerType.optional().describe('Size of the file in bytes'),
|
||||
url: URLType.optional().describe('URL of the attachment'),
|
||||
proxy_url: URLType.optional().describe('Proxied URL of the attachment'),
|
||||
@@ -88,14 +86,10 @@ const WebhookAttachmentRequest = z.object({
|
||||
).optional(),
|
||||
});
|
||||
|
||||
export const WebhookMessageRequest = z
|
||||
const WebhookMessageRequestBase = z
|
||||
.object({
|
||||
content: MessageContentRequest.nullish(),
|
||||
embeds: z.array(RichEmbedRequest).optional().describe('Array of embed objects to include in the message'),
|
||||
attachments: z
|
||||
.array(z.union([ClientUploadedAttachmentRequest, WebhookAttachmentRequest]))
|
||||
.optional()
|
||||
.describe('Array of attachment objects'),
|
||||
message_reference: MessageReferenceRequest.nullish().describe(
|
||||
'Reference to another message (for replies or forwards)',
|
||||
),
|
||||
@@ -115,8 +109,24 @@ export const WebhookMessageRequest = z
|
||||
})
|
||||
.partial();
|
||||
|
||||
export const WebhookMessageRequest = WebhookMessageRequestBase.extend({
|
||||
attachments: z
|
||||
.array(ClientUploadedAttachmentRequest)
|
||||
.optional()
|
||||
.describe('Array of attachments uploaded through the presigned upload endpoint'),
|
||||
});
|
||||
|
||||
export type WebhookMessageRequest = z.infer<typeof WebhookMessageRequest>;
|
||||
|
||||
export const WebhookMultipartMessageRequest = WebhookMessageRequestBase.extend({
|
||||
attachments: z
|
||||
.array(WebhookMultipartAttachmentRequest)
|
||||
.optional()
|
||||
.describe('Array of multipart attachment metadata objects'),
|
||||
});
|
||||
|
||||
export type WebhookMultipartMessageRequest = z.infer<typeof WebhookMultipartMessageRequest>;
|
||||
|
||||
export const WebhookMessageEditRequest = z
|
||||
.object({
|
||||
content: MessageContentRequest.nullish().describe(
|
||||
|
||||
Reference in New Issue
Block a user