mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
refactor(api): split webhook attachment schemas (#2268)
This commit is contained in:
1 parent
240b7e4388
commit
ef8d1225b5
3 files changed
+100
-52
No files matched your search
@@ -22220,7 +22220,7 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"content": {
|
"content": {
|
||||||
"application/json": {"schema": {"$ref": "#/components/schemas/WebhookMessageRequest"}},
|
"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"},
|
"items": {"$ref": "#/components/schemas/RichEmbedRequest"},
|
||||||
"description": "Array of embed objects to include in the message"
|
"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": {
|
"attachments": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {"$ref": "#/components/schemas/ClientUploadedAttachmentRequest"},
|
||||||
"oneOf": [
|
"description": "Array of attachments uploaded through the presigned upload endpoint"
|
||||||
{"$ref": "#/components/schemas/ClientUploadedAttachmentRequest"},
|
}
|
||||||
{
|
}
|
||||||
|
},
|
||||||
|
"WebhookMultipartMessageRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"content": {"anyOf": [{"$ref": "#/components/schemas/MessageContentRequest"}, {"type": "null"}]},
|
||||||
"oneOf": [
|
"embeds": {
|
||||||
{"$ref": "#/components/schemas/SnowflakeType"},
|
"type": "array",
|
||||||
{"$ref": "#/components/schemas/Int32Type"}
|
"items": {"$ref": "#/components/schemas/RichEmbedRequest"},
|
||||||
],
|
"description": "Array of embed objects to include in the message"
|
||||||
"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"
|
|
||||||
},
|
},
|
||||||
"message_reference": {
|
"message_reference": {
|
||||||
"anyOf": [{"$ref": "#/components/schemas/MessageReferenceRequest"}, {"type": "null"}],
|
"anyOf": [{"$ref": "#/components/schemas/MessageReferenceRequest"}, {"type": "null"}],
|
||||||
@@ -34790,6 +34790,34 @@
|
|||||||
"avatar_url": {
|
"avatar_url": {
|
||||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
"description": "Override the default avatar URL of the webhook for this message"
|
"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,
|
WebhookExecuteQueryRequest,
|
||||||
WebhookMessageEditRequest,
|
WebhookMessageEditRequest,
|
||||||
WebhookMessageRequest,
|
WebhookMessageRequest,
|
||||||
|
WebhookMultipartMessageRequest,
|
||||||
WebhookTokenUpdateRequest,
|
WebhookTokenUpdateRequest,
|
||||||
WebhookUpdateRequest,
|
WebhookUpdateRequest,
|
||||||
} from '@fluxer/schema/src/domains/webhook/WebhookRequestSchemas';
|
} from '@fluxer/schema/src/domains/webhook/WebhookRequestSchemas';
|
||||||
@@ -47,6 +48,14 @@ function validateWebhookMessagePayload(data: unknown): WebhookMessageRequest {
|
|||||||
return validationResult.data;
|
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> {
|
async function parseWebhookJsonMessageData(ctx: Context<HonoEnv>): Promise<WebhookExecuteMessageData> {
|
||||||
let data: unknown;
|
let data: unknown;
|
||||||
try {
|
try {
|
||||||
@@ -89,10 +98,11 @@ async function parseWebhookMultipartMessageData(
|
|||||||
if (!parsedPayload) {
|
if (!parsedPayload) {
|
||||||
throw InputValidationError.fromCode('message_data', ValidationErrorCodes.INVALID_MESSAGE_DATA);
|
throw InputValidationError.fromCode('message_data', ValidationErrorCodes.INVALID_MESSAGE_DATA);
|
||||||
}
|
}
|
||||||
const webhookData = validateWebhookMessagePayload(parsedPayload);
|
const webhookData = validateWebhookMultipartMessagePayload(parsedPayload);
|
||||||
return {
|
return {
|
||||||
...webhookData,
|
...webhookData,
|
||||||
...messageData,
|
...messageData,
|
||||||
|
attachments: messageData.attachments,
|
||||||
username: webhookData.username,
|
username: webhookData.username,
|
||||||
avatar_url: webhookData.avatar_url,
|
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.',
|
'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,
|
responseSchema: MessageResponseSchema,
|
||||||
requestSchema: WebhookMessageRequest,
|
requestSchema: WebhookMessageRequest,
|
||||||
requestFormSchema: WebhookMessageRequest,
|
requestFormSchema: WebhookMultipartMessageRequest,
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
tags: ['Webhooks'],
|
tags: ['Webhooks'],
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export const WebhookTokenUpdateRequest = z
|
|||||||
|
|
||||||
export type WebhookTokenUpdateRequest = z.infer<typeof WebhookTokenUpdateRequest>;
|
export type WebhookTokenUpdateRequest = z.infer<typeof WebhookTokenUpdateRequest>;
|
||||||
|
|
||||||
const WebhookAttachmentRequest = z.object({
|
const WebhookMultipartAttachmentRequest = z.object({
|
||||||
id: z
|
id: z
|
||||||
.union([SnowflakeType, coerceNumberFromString(Int32Type)])
|
.union([SnowflakeType, coerceNumberFromString(Int32Type)])
|
||||||
.optional()
|
.optional()
|
||||||
@@ -70,8 +70,6 @@ const WebhookAttachmentRequest = z.object({
|
|||||||
filename: createStringType(1, 1024).optional().describe('Name of the file (1-1024 characters)'),
|
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)'),
|
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'),
|
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'),
|
size: NonNegativeSafeIntegerType.optional().describe('Size of the file in bytes'),
|
||||||
url: URLType.optional().describe('URL of the attachment'),
|
url: URLType.optional().describe('URL of the attachment'),
|
||||||
proxy_url: URLType.optional().describe('Proxied URL of the attachment'),
|
proxy_url: URLType.optional().describe('Proxied URL of the attachment'),
|
||||||
@@ -88,14 +86,10 @@ const WebhookAttachmentRequest = z.object({
|
|||||||
).optional(),
|
).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const WebhookMessageRequest = z
|
const WebhookMessageRequestBase = z
|
||||||
.object({
|
.object({
|
||||||
content: MessageContentRequest.nullish(),
|
content: MessageContentRequest.nullish(),
|
||||||
embeds: z.array(RichEmbedRequest).optional().describe('Array of embed objects to include in the message'),
|
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(
|
message_reference: MessageReferenceRequest.nullish().describe(
|
||||||
'Reference to another message (for replies or forwards)',
|
'Reference to another message (for replies or forwards)',
|
||||||
),
|
),
|
||||||
@@ -115,8 +109,24 @@ export const WebhookMessageRequest = z
|
|||||||
})
|
})
|
||||||
.partial();
|
.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 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
|
export const WebhookMessageEditRequest = z
|
||||||
.object({
|
.object({
|
||||||
content: MessageContentRequest.nullish().describe(
|
content: MessageContentRequest.nullish().describe(
|
||||||
|
|||||||
Reference in new issue
Block a user