From 37da9aabe3b5b63ceb3e2806bb71dd405b723989 Mon Sep 17 00:00:00 2001 From: devlikepro Date: Wed, 9 Apr 2025 15:31:57 +0700 Subject: [PATCH] [core] Add POST /api/send/link-review fix #880 fix #596 --- src/api/chatting.controller.ts | 20 ++ src/core/abc/session.abc.ts | 7 + src/core/engines/gows/grpc/gows.ts | 162 ++++++++++ src/core/engines/gows/grpc/gows_pb.js | 319 ++++++++++++++++++- src/core/engines/gows/session.gows.core.ts | 8 +- src/core/engines/noweb/session.noweb.core.ts | 7 + src/nestjs/validation/IsFileType.ts | 28 ++ src/structures/chatting.dto.ts | 81 ++++- src/structures/files.dto.ts | 31 ++ 9 files changed, 660 insertions(+), 3 deletions(-) create mode 100644 src/nestjs/validation/IsFileType.ts diff --git a/src/api/chatting.controller.ts b/src/api/chatting.controller.ts index 1505ebff..8b577807 100644 --- a/src/api/chatting.controller.ts +++ b/src/api/chatting.controller.ts @@ -23,6 +23,7 @@ import { MessageFileRequest, MessageForwardRequest, MessageImageRequest, + MessageLinkCustomPreviewRequest, MessageLinkPreviewRequest, MessageLocationRequest, MessagePollRequest, @@ -95,6 +96,25 @@ export class ChattingController { return whatsapp.sendVideo(request); } + @Post('/send/link-custom-preview') + @ApiOperation({ + summary: 'Send a text message with a CUSTOM link preview.', + description: + 'You can use regular /api/sendText if you wanna send auto-generated link preview.', + }) + @UsePipes(new WAHAValidationPipe()) + async sendLinkCustomPreview( + @Body() request: MessageLinkCustomPreviewRequest, + ): Promise { + const whatsapp = await this.manager.getWorkingSession(request.session); + if (!request.text.includes(request.preview.url)) { + throw new Error( + '"text" must include the URL provided in the "preview.url"', + ); + } + return whatsapp.sendLinkCustomPreview(request); + } + @Post('/sendButtons') @ApiOperation({ summary: 'Send buttons (interactive message)', diff --git a/src/core/abc/session.abc.ts b/src/core/abc/session.abc.ts index e37fb2e8..4e60b722 100644 --- a/src/core/abc/session.abc.ts +++ b/src/core/abc/session.abc.ts @@ -52,6 +52,7 @@ import { MessageFileRequest, MessageForwardRequest, MessageImageRequest, + MessageLinkCustomPreviewRequest, MessageLinkPreviewRequest, MessageLocationRequest, MessagePollRequest, @@ -421,6 +422,12 @@ export abstract class WhatsappSession { throw new NotImplementedByEngineError(); } + sendLinkCustomPreview( + request: MessageLinkCustomPreviewRequest, + ): Promise { + throw new NotImplementedByEngineError(); + } + abstract forwardMessage(request: MessageForwardRequest): Promise; abstract sendImage(request: MessageImageRequest); diff --git a/src/core/engines/gows/grpc/gows.ts b/src/core/engines/gows/grpc/gows.ts index e26f247a..fb3354cc 100644 --- a/src/core/engines/gows/grpc/gows.ts +++ b/src/core/engines/gows/grpc/gows.ts @@ -2535,6 +2535,142 @@ export namespace messages { return Media.deserialize(bytes); } } + export class LinkPreview extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | { + url?: string; + title?: string; + description?: string; + image?: Uint8Array; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { + if ("url" in data && data.url != undefined) { + this.url = data.url; + } + if ("title" in data && data.title != undefined) { + this.title = data.title; + } + if ("description" in data && data.description != undefined) { + this.description = data.description; + } + if ("image" in data && data.image != undefined) { + this.image = data.image; + } + } + } + get url() { + return pb_1.Message.getFieldWithDefault(this, 1, "") as string; + } + set url(value: string) { + pb_1.Message.setField(this, 1, value); + } + get title() { + return pb_1.Message.getFieldWithDefault(this, 2, "") as string; + } + set title(value: string) { + pb_1.Message.setField(this, 2, value); + } + get description() { + return pb_1.Message.getFieldWithDefault(this, 3, "") as string; + } + set description(value: string) { + pb_1.Message.setField(this, 3, value); + } + get image() { + return pb_1.Message.getFieldWithDefault(this, 4, new Uint8Array(0)) as Uint8Array; + } + set image(value: Uint8Array) { + pb_1.Message.setField(this, 4, value); + } + static fromObject(data: { + url?: string; + title?: string; + description?: string; + image?: Uint8Array; + }): LinkPreview { + const message = new LinkPreview({}); + if (data.url != null) { + message.url = data.url; + } + if (data.title != null) { + message.title = data.title; + } + if (data.description != null) { + message.description = data.description; + } + if (data.image != null) { + message.image = data.image; + } + return message; + } + toObject() { + const data: { + url?: string; + title?: string; + description?: string; + image?: Uint8Array; + } = {}; + if (this.url != null) { + data.url = this.url; + } + if (this.title != null) { + data.title = this.title; + } + if (this.description != null) { + data.description = this.description; + } + if (this.image != null) { + data.image = this.image; + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.url.length) + writer.writeString(1, this.url); + if (this.title.length) + writer.writeString(2, this.title); + if (this.description.length) + writer.writeString(3, this.description); + if (this.image.length) + writer.writeBytes(4, this.image); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): LinkPreview { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new LinkPreview(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + message.url = reader.readString(); + break; + case 2: + message.title = reader.readString(); + break; + case 3: + message.description = reader.readString(); + break; + case 4: + message.image = reader.readBytes(); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): LinkPreview { + return LinkPreview.deserialize(bytes); + } + } export class MessageRequest extends pb_1.Message { #one_of_decls: number[][] = []; constructor(data?: any[] | { @@ -2549,6 +2685,7 @@ export namespace messages { replyTo?: string; id?: string; participants?: string[]; + preview?: LinkPreview; }) { super(); pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [11], this.#one_of_decls); @@ -2586,6 +2723,9 @@ export namespace messages { if ("participants" in data && data.participants != undefined) { this.participants = data.participants; } + if ("preview" in data && data.preview != undefined) { + this.preview = data.preview; + } } } get session() { @@ -2666,6 +2806,15 @@ export namespace messages { set participants(value: string[]) { pb_1.Message.setField(this, 11, value); } + get preview() { + return pb_1.Message.getWrapperField(this, LinkPreview, 12) as LinkPreview; + } + set preview(value: LinkPreview) { + pb_1.Message.setWrapperField(this, 12, value); + } + get has_preview() { + return pb_1.Message.getField(this, 12) != null; + } static fromObject(data: { session?: ReturnType; jid?: string; @@ -2678,6 +2827,7 @@ export namespace messages { replyTo?: string; id?: string; participants?: string[]; + preview?: ReturnType; }): MessageRequest { const message = new MessageRequest({}); if (data.session != null) { @@ -2713,6 +2863,9 @@ export namespace messages { if (data.participants != null) { message.participants = data.participants; } + if (data.preview != null) { + message.preview = LinkPreview.fromObject(data.preview); + } return message; } toObject() { @@ -2728,6 +2881,7 @@ export namespace messages { replyTo?: string; id?: string; participants?: string[]; + preview?: ReturnType; } = {}; if (this.session != null) { data.session = this.session.toObject(); @@ -2762,6 +2916,9 @@ export namespace messages { if (this.participants != null) { data.participants = this.participants; } + if (this.preview != null) { + data.preview = this.preview.toObject(); + } return data; } serialize(): Uint8Array; @@ -2790,6 +2947,8 @@ export namespace messages { writer.writeString(10, this.id); if (this.participants.length) writer.writeRepeatedString(11, this.participants); + if (this.has_preview) + writer.writeMessage(12, this.preview, () => this.preview.serialize(writer)); if (!w) return writer.getResultBuffer(); } @@ -2832,6 +2991,9 @@ export namespace messages { case 11: pb_1.Message.addToRepeatedField(message, 11, reader.readString()); break; + case 12: + reader.readMessage(message.preview, () => message.preview = LinkPreview.deserialize(reader)); + break; default: reader.skipField(); } } diff --git a/src/core/engines/gows/grpc/gows_pb.js b/src/core/engines/gows/grpc/gows_pb.js index 9b6b514b..762d0676 100644 --- a/src/core/engines/gows/grpc/gows_pb.js +++ b/src/core/engines/gows/grpc/gows_pb.js @@ -45,6 +45,7 @@ goog.exportSymbol('proto.messages.JidRequest', null, global); goog.exportSymbol('proto.messages.JidStringRequest', null, global); goog.exportSymbol('proto.messages.Json', null, global); goog.exportSymbol('proto.messages.JsonList', null, global); +goog.exportSymbol('proto.messages.LinkPreview', null, global); goog.exportSymbol('proto.messages.LogLevel', null, global); goog.exportSymbol('proto.messages.MarkReadRequest', null, global); goog.exportSymbol('proto.messages.Media', null, global); @@ -662,6 +663,27 @@ if (goog.DEBUG && !COMPILED) { */ proto.messages.Media.displayName = 'proto.messages.Media'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.messages.LinkPreview = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.messages.LinkPreview, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.messages.LinkPreview.displayName = 'proto.messages.LinkPreview'; +} /** * Generated by JsPbCodeGenerator. * @param {Array=} opt_data Optional initial data array, typically from a @@ -6338,6 +6360,250 @@ proto.messages.Media.prototype.setFilename = function(value) { + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.messages.LinkPreview.prototype.toObject = function(opt_includeInstance) { + return proto.messages.LinkPreview.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.messages.LinkPreview} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.messages.LinkPreview.toObject = function(includeInstance, msg) { + var f, obj = { + url: jspb.Message.getFieldWithDefault(msg, 1, ""), + title: jspb.Message.getFieldWithDefault(msg, 2, ""), + description: jspb.Message.getFieldWithDefault(msg, 3, ""), + image: msg.getImage_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.messages.LinkPreview} + */ +proto.messages.LinkPreview.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.messages.LinkPreview; + return proto.messages.LinkPreview.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.messages.LinkPreview} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.messages.LinkPreview} + */ +proto.messages.LinkPreview.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setUrl(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setTitle(value); + break; + case 3: + var value = /** @type {string} */ (reader.readString()); + msg.setDescription(value); + break; + case 4: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setImage(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.messages.LinkPreview.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.messages.LinkPreview.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.messages.LinkPreview} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.messages.LinkPreview.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getUrl(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getTitle(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getDescription(); + if (f.length > 0) { + writer.writeString( + 3, + f + ); + } + f = message.getImage_asU8(); + if (f.length > 0) { + writer.writeBytes( + 4, + f + ); + } +}; + + +/** + * optional string url = 1; + * @return {string} + */ +proto.messages.LinkPreview.prototype.getUrl = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.messages.LinkPreview} returns this + */ +proto.messages.LinkPreview.prototype.setUrl = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional string title = 2; + * @return {string} + */ +proto.messages.LinkPreview.prototype.getTitle = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.messages.LinkPreview} returns this + */ +proto.messages.LinkPreview.prototype.setTitle = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional string description = 3; + * @return {string} + */ +proto.messages.LinkPreview.prototype.getDescription = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * @param {string} value + * @return {!proto.messages.LinkPreview} returns this + */ +proto.messages.LinkPreview.prototype.setDescription = function(value) { + return jspb.Message.setProto3StringField(this, 3, value); +}; + + +/** + * optional bytes image = 4; + * @return {!(string|Uint8Array)} + */ +proto.messages.LinkPreview.prototype.getImage = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** + * optional bytes image = 4; + * This is a type-conversion wrapper around `getImage()` + * @return {string} + */ +proto.messages.LinkPreview.prototype.getImage_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getImage())); +}; + + +/** + * optional bytes image = 4; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getImage()` + * @return {!Uint8Array} + */ +proto.messages.LinkPreview.prototype.getImage_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getImage())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.messages.LinkPreview} returns this + */ +proto.messages.LinkPreview.prototype.setImage = function(value) { + return jspb.Message.setProto3BytesField(this, 4, value); +}; + + + /** * List of repeated fields within this message type. * @private {!Array} @@ -6386,7 +6652,8 @@ proto.messages.MessageRequest.toObject = function(includeInstance, msg) { linkpreviewhighquality: jspb.Message.getBooleanFieldWithDefault(msg, 8, false), replyto: jspb.Message.getFieldWithDefault(msg, 9, ""), id: jspb.Message.getFieldWithDefault(msg, 10, ""), - participantsList: (f = jspb.Message.getRepeatedField(msg, 11)) == null ? undefined : f + participantsList: (f = jspb.Message.getRepeatedField(msg, 11)) == null ? undefined : f, + preview: (f = msg.getPreview()) && proto.messages.LinkPreview.toObject(includeInstance, f) }; if (includeInstance) { @@ -6471,6 +6738,11 @@ proto.messages.MessageRequest.deserializeBinaryFromReader = function(msg, reader var value = /** @type {string} */ (reader.readString()); msg.addParticipants(value); break; + case 12: + var value = new proto.messages.LinkPreview; + reader.readMessage(value,proto.messages.LinkPreview.deserializeBinaryFromReader); + msg.setPreview(value); + break; default: reader.skipField(); break; @@ -6581,6 +6853,14 @@ proto.messages.MessageRequest.serializeBinaryToWriter = function(message, writer f ); } + f = message.getPreview(); + if (f != null) { + writer.writeMessage( + 12, + f, + proto.messages.LinkPreview.serializeBinaryToWriter + ); + } }; @@ -6877,6 +7157,43 @@ proto.messages.MessageRequest.prototype.clearParticipantsList = function() { }; +/** + * optional LinkPreview preview = 12; + * @return {?proto.messages.LinkPreview} + */ +proto.messages.MessageRequest.prototype.getPreview = function() { + return /** @type{?proto.messages.LinkPreview} */ ( + jspb.Message.getWrapperField(this, proto.messages.LinkPreview, 12)); +}; + + +/** + * @param {?proto.messages.LinkPreview|undefined} value + * @return {!proto.messages.MessageRequest} returns this +*/ +proto.messages.MessageRequest.prototype.setPreview = function(value) { + return jspb.Message.setWrapperField(this, 12, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.messages.MessageRequest} returns this + */ +proto.messages.MessageRequest.prototype.clearPreview = function() { + return this.setPreview(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.messages.MessageRequest.prototype.hasPreview = function() { + return jspb.Message.getField(this, 12) != null; +}; + + diff --git a/src/core/engines/gows/session.gows.core.ts b/src/core/engines/gows/session.gows.core.ts index 9def6e95..278e200d 100644 --- a/src/core/engines/gows/session.gows.core.ts +++ b/src/core/engines/gows/session.gows.core.ts @@ -63,10 +63,10 @@ import { ChatRequest, CheckNumberStatusQuery, EditMessageRequest, - MessageButtonReply, MessageFileRequest, MessageForwardRequest, MessageImageRequest, + MessageLinkCustomPreviewRequest, MessageLocationRequest, MessageReactionRequest, MessageReplyRequest, @@ -692,6 +692,12 @@ export class WhatsappSessionGoWSCore extends WhatsappSession { throw new AvailableInPlusVersion(); } + sendLinkCustomPreview( + request: MessageLinkCustomPreviewRequest, + ): Promise { + throw new AvailableInPlusVersion(); + } + reply(request: MessageReplyRequest) { throw new Error('Method not implemented.'); } diff --git a/src/core/engines/noweb/session.noweb.core.ts b/src/core/engines/noweb/session.noweb.core.ts index d1c8615f..d4af778e 100644 --- a/src/core/engines/noweb/session.noweb.core.ts +++ b/src/core/engines/noweb/session.noweb.core.ts @@ -97,6 +97,7 @@ import { MessageFileRequest, MessageForwardRequest, MessageImageRequest, + MessageLinkCustomPreviewRequest, MessageLinkPreviewRequest, MessageLocationRequest, MessagePollRequest, @@ -824,6 +825,12 @@ export class WhatsappSessionNoWebCore extends WhatsappSession { throw new AvailableInPlusVersion(); } + sendLinkCustomPreview( + request: MessageLinkCustomPreviewRequest, + ): Promise { + throw new AvailableInPlusVersion(); + } + protected async uploadMedia( file: RemoteFile | BinaryFile, type, diff --git a/src/nestjs/validation/IsFileType.ts b/src/nestjs/validation/IsFileType.ts new file mode 100644 index 00000000..a52711a6 --- /dev/null +++ b/src/nestjs/validation/IsFileType.ts @@ -0,0 +1,28 @@ +import { + registerDecorator, + ValidationArguments, + ValidationOptions, +} from 'class-validator'; + +export function IsFileType(validationOptions?: ValidationOptions) { + return function (object: any, propertyName: string) { + registerDecorator({ + name: 'isFileType', + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + validator: { + validate(value: any, _args: ValidationArguments) { + return ( + value && + typeof value === 'object' && + (typeof value.data === 'string' || typeof value.url === 'string') + ); + }, + defaultMessage() { + return 'must contain either "data" or "url".'; + }, + }, + }); + }; +} diff --git a/src/structures/chatting.dto.ts b/src/structures/chatting.dto.ts index 1eb8f907..faae2a10 100644 --- a/src/structures/chatting.dto.ts +++ b/src/structures/chatting.dto.ts @@ -4,8 +4,17 @@ import { ApiProperty, getSchemaPath, } from '@nestjs/swagger'; +import { IsFileType } from '@waha/nestjs/validation/IsFileType'; import { GetChatMessagesQuery } from '@waha/structures/chats.dto'; -import { IsNotEmpty, IsString } from 'class-validator'; +import { plainToInstance, Transform, Type } from 'class-transformer'; +import { + IsBoolean, + IsNotEmpty, + IsOptional, + IsString, + IsUrl, + ValidateNested, +} from 'class-validator'; import { SessionBaseRequest, @@ -14,6 +23,9 @@ import { } from './base.dto'; import { BinaryFile, + FileContent, + FileType, + FileURL, RemoteFile, VideoBinaryFile, VideoRemoteFile, @@ -162,6 +174,73 @@ export class MessageTextRequest extends ChatRequest { linkPreviewHighQuality?: boolean = false; } +@ApiExtraModels(FileURL, FileContent) +export class LinkPreviewData { + @IsNotEmpty() + @IsUrl({ + protocols: ['http', 'https'], + require_protocol: true, + require_tld: false, + }) + url: string = 'https://github.com/'; + + @IsString() + title: string = 'Your Title'; + + @IsString() + description: string = 'Check this out, amazing!'; + + @IsOptional() + @ValidateNested() + @Transform( + ({ value }) => { + if (value?.url) { + return plainToInstance(FileURL, value); + } + if (value?.data) { + return plainToInstance(FileContent, value); + } + return value; + }, + { toClassOnly: true }, + ) + @IsFileType({ message: 'Image must contain either "data" or "url".' }) + @ApiProperty({ + oneOf: [ + { $ref: getSchemaPath(FileURL) }, + { $ref: getSchemaPath(FileContent) }, + ], + example: { + url: + process.env.WHATSAPP_SWAGGER_JPG_EXAMPLE_URL || + 'https://github.com/devlikeapro/waha/raw/core/examples/waha.jpg', + }, + }) + image?: FileType; +} + +export class MessageLinkCustomPreviewRequest extends ChatRequest { + @IsString() + @ApiProperty({ + description: + 'The text to send. MUST include the URL provided in preview.url', + }) + text: string = 'Check this out! https://github.com/'; + + @IsBoolean() + @IsOptional() + linkPreviewHighQuality?: boolean = true; + + @ValidateNested() + @Type(() => LinkPreviewData) + preview: LinkPreviewData; + + @ReplyToProperty() + @IsString() + @IsOptional() + reply_to?: string; +} + export class EditMessageRequest { text: string = 'Hello, world!'; diff --git a/src/structures/files.dto.ts b/src/structures/files.dto.ts index d3ae8344..27774b2e 100644 --- a/src/structures/files.dto.ts +++ b/src/structures/files.dto.ts @@ -1,4 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; +import { IsBase64, IsNotEmpty, IsString, IsUrl } from 'class-validator'; export class Base64File { mimetype: string; @@ -117,3 +118,33 @@ export class VideoRemoteFile { }) url: string; } + +export class FileURL { + @IsString() + @IsNotEmpty() + @IsUrl({ + protocols: ['http', 'https'], + require_protocol: true, + require_tld: false, + }) + @ApiProperty({ + example: + process.env.WHATSAPP_SWAGGER_JPG_EXAMPLE_URL || + 'https://github.com/devlikeapro/waha/raw/core/examples/waha.jpg', + }) + url?: string; +} + +export class FileContent { + @IsString() + @IsNotEmpty() + @IsBase64() + @ApiProperty({ + description: 'Base64-encoded data of the file', + example: + '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', + }) + data?: string; +} + +export type FileType = FileURL | FileContent;