[core] Reply to button

fix #848
This commit is contained in:
devlikepro committed 2025-03-27 14:36:30 +07:00
1 parent 2ec94dc13c
commit 3e4d66ca27
9 files changed
+530 -1

No files matched your search

+11
View File
@@ -18,6 +18,7 @@ import {
ChatRequest,
CheckNumberStatusQuery,
GetMessageQuery,
MessageButtonReply,
MessageContactVcardRequest,
MessageFileRequest,
MessageForwardRequest,
@@ -177,6 +178,16 @@ export class ChattingController {
return whatsapp.sendContactVCard(request);
}
@Post('/send/buttons/reply')
@ApiOperation({
summary: 'Reply on a button message',
})
@UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
async sendButtonsReply(@Body() request: MessageButtonReply) {
const whatsapp = await this.manager.getWorkingSession(request.session);
return whatsapp.sendButtonsReply(request);
}
@Get('/sendText')
@ApiOperation({ summary: 'Send a text message', deprecated: true })
async sendTextGet(@Query() query: MessageTextQuery) {
+5
View File
@@ -46,6 +46,7 @@ import {
ChatRequest,
CheckNumberStatusQuery,
EditMessageRequest,
MessageButtonReply,
MessageContactVcardRequest,
MessageFileRequest,
MessageForwardRequest,
@@ -431,6 +432,10 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
sendButtonsReply(request: MessageButtonReply) {
throw new NotImplementedByEngineError();
}
abstract reply(request: MessageReplyRequest);
abstract sendSeen(chat: SendSeenRequest);
+175
View File
@@ -3236,6 +3236,168 @@ export namespace messages {
return ProfilePictureResponse.deserialize(bytes);
}
}
export class ButtonReplyRequest extends pb_1.Message {
#one_of_decls: number[][] = [];
constructor(data?: any[] | {
session?: Session;
jid?: string;
selectedDisplayText?: string;
selectedButtonID?: string;
replyTo?: string;
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
if (!Array.isArray(data) && typeof data == "object") {
if ("session" in data && data.session != undefined) {
this.session = data.session;
}
if ("jid" in data && data.jid != undefined) {
this.jid = data.jid;
}
if ("selectedDisplayText" in data && data.selectedDisplayText != undefined) {
this.selectedDisplayText = data.selectedDisplayText;
}
if ("selectedButtonID" in data && data.selectedButtonID != undefined) {
this.selectedButtonID = data.selectedButtonID;
}
if ("replyTo" in data && data.replyTo != undefined) {
this.replyTo = data.replyTo;
}
}
}
get session() {
return pb_1.Message.getWrapperField(this, Session, 1) as Session;
}
set session(value: Session) {
pb_1.Message.setWrapperField(this, 1, value);
}
get has_session() {
return pb_1.Message.getField(this, 1) != null;
}
get jid() {
return pb_1.Message.getFieldWithDefault(this, 2, "") as string;
}
set jid(value: string) {
pb_1.Message.setField(this, 2, value);
}
get selectedDisplayText() {
return pb_1.Message.getFieldWithDefault(this, 3, "") as string;
}
set selectedDisplayText(value: string) {
pb_1.Message.setField(this, 3, value);
}
get selectedButtonID() {
return pb_1.Message.getFieldWithDefault(this, 4, "") as string;
}
set selectedButtonID(value: string) {
pb_1.Message.setField(this, 4, value);
}
get replyTo() {
return pb_1.Message.getFieldWithDefault(this, 5, "") as string;
}
set replyTo(value: string) {
pb_1.Message.setField(this, 5, value);
}
static fromObject(data: {
session?: ReturnType<typeof Session.prototype.toObject>;
jid?: string;
selectedDisplayText?: string;
selectedButtonID?: string;
replyTo?: string;
}): ButtonReplyRequest {
const message = new ButtonReplyRequest({});
if (data.session != null) {
message.session = Session.fromObject(data.session);
}
if (data.jid != null) {
message.jid = data.jid;
}
if (data.selectedDisplayText != null) {
message.selectedDisplayText = data.selectedDisplayText;
}
if (data.selectedButtonID != null) {
message.selectedButtonID = data.selectedButtonID;
}
if (data.replyTo != null) {
message.replyTo = data.replyTo;
}
return message;
}
toObject() {
const data: {
session?: ReturnType<typeof Session.prototype.toObject>;
jid?: string;
selectedDisplayText?: string;
selectedButtonID?: string;
replyTo?: string;
} = {};
if (this.session != null) {
data.session = this.session.toObject();
}
if (this.jid != null) {
data.jid = this.jid;
}
if (this.selectedDisplayText != null) {
data.selectedDisplayText = this.selectedDisplayText;
}
if (this.selectedButtonID != null) {
data.selectedButtonID = this.selectedButtonID;
}
if (this.replyTo != null) {
data.replyTo = this.replyTo;
}
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.has_session)
writer.writeMessage(1, this.session, () => this.session.serialize(writer));
if (this.jid.length)
writer.writeString(2, this.jid);
if (this.selectedDisplayText.length)
writer.writeString(3, this.selectedDisplayText);
if (this.selectedButtonID.length)
writer.writeString(4, this.selectedButtonID);
if (this.replyTo.length)
writer.writeString(5, this.replyTo);
if (!w)
return writer.getResultBuffer();
}
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ButtonReplyRequest {
const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ButtonReplyRequest();
while (reader.nextField()) {
if (reader.isEndGroup())
break;
switch (reader.getFieldNumber()) {
case 1:
reader.readMessage(message.session, () => message.session = Session.deserialize(reader));
break;
case 2:
message.jid = reader.readString();
break;
case 3:
message.selectedDisplayText = reader.readString();
break;
case 4:
message.selectedButtonID = reader.readString();
break;
case 5:
message.replyTo = reader.readString();
break;
default: reader.skipField();
}
}
return message;
}
serializeBinary(): Uint8Array {
return this.serialize();
}
static deserializeBinary(bytes: Uint8Array): ButtonReplyRequest {
return ButtonReplyRequest.deserialize(bytes);
}
}
export class PresenceRequest extends pb_1.Message {
#one_of_decls: number[][] = [];
constructor(data?: any[] | {
@@ -7327,6 +7489,15 @@ export namespace messages {
responseSerialize: (message: MessageResponse) => Buffer.from(message.serialize()),
responseDeserialize: (bytes: Buffer) => MessageResponse.deserialize(new Uint8Array(bytes))
},
SendButtonReply: {
path: "/messages.MessageService/SendButtonReply",
requestStream: false,
responseStream: false,
requestSerialize: (message: ButtonReplyRequest) => Buffer.from(message.serialize()),
requestDeserialize: (bytes: Buffer) => ButtonReplyRequest.deserialize(new Uint8Array(bytes)),
responseSerialize: (message: MessageResponse) => Buffer.from(message.serialize()),
responseDeserialize: (bytes: Buffer) => MessageResponse.deserialize(new Uint8Array(bytes))
},
GetSubscribedNewsletters: {
path: "/messages.MessageService/GetSubscribedNewsletters",
requestStream: false,
@@ -7488,6 +7659,7 @@ export namespace messages {
abstract MarkRead(call: grpc_1.ServerUnaryCall<MarkReadRequest, Empty>, callback: grpc_1.sendUnaryData<Empty>): void;
abstract EditMessage(call: grpc_1.ServerUnaryCall<EditMessageRequest, MessageResponse>, callback: grpc_1.sendUnaryData<MessageResponse>): void;
abstract RevokeMessage(call: grpc_1.ServerUnaryCall<RevokeMessageRequest, MessageResponse>, callback: grpc_1.sendUnaryData<MessageResponse>): void;
abstract SendButtonReply(call: grpc_1.ServerUnaryCall<ButtonReplyRequest, MessageResponse>, callback: grpc_1.sendUnaryData<MessageResponse>): void;
abstract GetSubscribedNewsletters(call: grpc_1.ServerUnaryCall<NewsletterListRequest, NewsletterList>, callback: grpc_1.sendUnaryData<NewsletterList>): void;
abstract GetNewsletterInfo(call: grpc_1.ServerUnaryCall<NewsletterInfoRequest, Newsletter>, callback: grpc_1.sendUnaryData<Newsletter>): void;
abstract GetNewsletterMessagesByInvite(call: grpc_1.ServerUnaryCall<GetNewsletterMessagesByInviteRequest, Json>, callback: grpc_1.sendUnaryData<Json>): void;
@@ -7606,6 +7778,9 @@ export namespace messages {
RevokeMessage: GrpcUnaryServiceInterface<RevokeMessageRequest, MessageResponse> = (message: RevokeMessageRequest, metadata: grpc_1.Metadata | grpc_1.CallOptions | grpc_1.requestCallback<MessageResponse>, options?: grpc_1.CallOptions | grpc_1.requestCallback<MessageResponse>, callback?: grpc_1.requestCallback<MessageResponse>): grpc_1.ClientUnaryCall => {
return super.RevokeMessage(message, metadata, options, callback);
};
SendButtonReply: GrpcUnaryServiceInterface<ButtonReplyRequest, MessageResponse> = (message: ButtonReplyRequest, metadata: grpc_1.Metadata | grpc_1.CallOptions | grpc_1.requestCallback<MessageResponse>, options?: grpc_1.CallOptions | grpc_1.requestCallback<MessageResponse>, callback?: grpc_1.requestCallback<MessageResponse>): grpc_1.ClientUnaryCall => {
return super.SendButtonReply(message, metadata, options, callback);
};
GetSubscribedNewsletters: GrpcUnaryServiceInterface<NewsletterListRequest, NewsletterList> = (message: NewsletterListRequest, metadata: grpc_1.Metadata | grpc_1.CallOptions | grpc_1.requestCallback<NewsletterList>, options?: grpc_1.CallOptions | grpc_1.requestCallback<NewsletterList>, callback?: grpc_1.requestCallback<NewsletterList>): grpc_1.ClientUnaryCall => {
return super.GetSubscribedNewsletters(message, metadata, options, callback);
};
@@ -4,6 +4,17 @@
var grpc = require('@grpc/grpc-js');
var gows_pb = require('./gows_pb.js');
function serialize_messages_ButtonReplyRequest(arg) {
if (!(arg instanceof gows_pb.ButtonReplyRequest)) {
throw new Error('Expected argument of type messages.ButtonReplyRequest');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_messages_ButtonReplyRequest(buffer_arg) {
return gows_pb.ButtonReplyRequest.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_messages_ChatPresenceRequest(arg) {
if (!(arg instanceof gows_pb.ChatPresenceRequest)) {
throw new Error('Expected argument of type messages.ChatPresenceRequest');
@@ -954,6 +965,17 @@ sendMessage: {
responseSerialize: serialize_messages_MessageResponse,
responseDeserialize: deserialize_messages_MessageResponse,
},
sendButtonReply: {
path: '/messages.MessageService/SendButtonReply',
requestStream: false,
responseStream: false,
requestType: gows_pb.ButtonReplyRequest,
responseType: gows_pb.MessageResponse,
requestSerialize: serialize_messages_ButtonReplyRequest,
requestDeserialize: deserialize_messages_ButtonReplyRequest,
responseSerialize: serialize_messages_MessageResponse,
responseDeserialize: deserialize_messages_MessageResponse,
},
//
// Newsletters
//
+293
View File
@@ -22,6 +22,7 @@ var global = (function() {
}.call(null));
goog.exportSymbol('proto.messages.AudioInfo', null, global);
goog.exportSymbol('proto.messages.ButtonReplyRequest', null, global);
goog.exportSymbol('proto.messages.ChatPresence', null, global);
goog.exportSymbol('proto.messages.ChatPresenceRequest', null, global);
goog.exportSymbol('proto.messages.CheckPhonesRequest', null, global);
@@ -765,6 +766,27 @@ if (goog.DEBUG && !COMPILED) {
*/
proto.messages.ProfilePictureResponse.displayName = 'proto.messages.ProfilePictureResponse';
}
/**
* 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.ButtonReplyRequest = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.messages.ButtonReplyRequest, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.messages.ButtonReplyRequest.displayName = 'proto.messages.ButtonReplyRequest';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
@@ -7543,6 +7565,277 @@ proto.messages.ProfilePictureResponse.prototype.setUrl = 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_<name>, 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.ButtonReplyRequest.prototype.toObject = function(opt_includeInstance) {
return proto.messages.ButtonReplyRequest.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.ButtonReplyRequest} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.messages.ButtonReplyRequest.toObject = function(includeInstance, msg) {
var f, obj = {
session: (f = msg.getSession()) && proto.messages.Session.toObject(includeInstance, f),
jid: jspb.Message.getFieldWithDefault(msg, 2, ""),
selecteddisplaytext: jspb.Message.getFieldWithDefault(msg, 3, ""),
selectedbuttonid: jspb.Message.getFieldWithDefault(msg, 4, ""),
replyto: jspb.Message.getFieldWithDefault(msg, 5, "")
};
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.ButtonReplyRequest}
*/
proto.messages.ButtonReplyRequest.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.messages.ButtonReplyRequest;
return proto.messages.ButtonReplyRequest.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* @param {!proto.messages.ButtonReplyRequest} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.messages.ButtonReplyRequest}
*/
proto.messages.ButtonReplyRequest.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = new proto.messages.Session;
reader.readMessage(value,proto.messages.Session.deserializeBinaryFromReader);
msg.setSession(value);
break;
case 2:
var value = /** @type {string} */ (reader.readString());
msg.setJid(value);
break;
case 3:
var value = /** @type {string} */ (reader.readString());
msg.setSelecteddisplaytext(value);
break;
case 4:
var value = /** @type {string} */ (reader.readString());
msg.setSelectedbuttonid(value);
break;
case 5:
var value = /** @type {string} */ (reader.readString());
msg.setReplyto(value);
break;
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.messages.ButtonReplyRequest.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.messages.ButtonReplyRequest.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.ButtonReplyRequest} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.messages.ButtonReplyRequest.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getSession();
if (f != null) {
writer.writeMessage(
1,
f,
proto.messages.Session.serializeBinaryToWriter
);
}
f = message.getJid();
if (f.length > 0) {
writer.writeString(
2,
f
);
}
f = message.getSelecteddisplaytext();
if (f.length > 0) {
writer.writeString(
3,
f
);
}
f = message.getSelectedbuttonid();
if (f.length > 0) {
writer.writeString(
4,
f
);
}
f = message.getReplyto();
if (f.length > 0) {
writer.writeString(
5,
f
);
}
};
/**
* optional Session session = 1;
* @return {?proto.messages.Session}
*/
proto.messages.ButtonReplyRequest.prototype.getSession = function() {
return /** @type{?proto.messages.Session} */ (
jspb.Message.getWrapperField(this, proto.messages.Session, 1));
};
/**
* @param {?proto.messages.Session|undefined} value
* @return {!proto.messages.ButtonReplyRequest} returns this
*/
proto.messages.ButtonReplyRequest.prototype.setSession = function(value) {
return jspb.Message.setWrapperField(this, 1, value);
};
/**
* Clears the message field making it undefined.
* @return {!proto.messages.ButtonReplyRequest} returns this
*/
proto.messages.ButtonReplyRequest.prototype.clearSession = function() {
return this.setSession(undefined);
};
/**
* Returns whether this field is set.
* @return {boolean}
*/
proto.messages.ButtonReplyRequest.prototype.hasSession = function() {
return jspb.Message.getField(this, 1) != null;
};
/**
* optional string jid = 2;
* @return {string}
*/
proto.messages.ButtonReplyRequest.prototype.getJid = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
};
/**
* @param {string} value
* @return {!proto.messages.ButtonReplyRequest} returns this
*/
proto.messages.ButtonReplyRequest.prototype.setJid = function(value) {
return jspb.Message.setProto3StringField(this, 2, value);
};
/**
* optional string selectedDisplayText = 3;
* @return {string}
*/
proto.messages.ButtonReplyRequest.prototype.getSelecteddisplaytext = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
};
/**
* @param {string} value
* @return {!proto.messages.ButtonReplyRequest} returns this
*/
proto.messages.ButtonReplyRequest.prototype.setSelecteddisplaytext = function(value) {
return jspb.Message.setProto3StringField(this, 3, value);
};
/**
* optional string selectedButtonID = 4;
* @return {string}
*/
proto.messages.ButtonReplyRequest.prototype.getSelectedbuttonid = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
};
/**
* @param {string} value
* @return {!proto.messages.ButtonReplyRequest} returns this
*/
proto.messages.ButtonReplyRequest.prototype.setSelectedbuttonid = function(value) {
return jspb.Message.setProto3StringField(this, 4, value);
};
/**
* optional string replyTo = 5;
* @return {string}
*/
proto.messages.ButtonReplyRequest.prototype.getReplyto = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, ""));
};
/**
* @param {string} value
* @return {!proto.messages.ButtonReplyRequest} returns this
*/
proto.messages.ButtonReplyRequest.prototype.setReplyto = function(value) {
return jspb.Message.setProto3StringField(this, 5, value);
};
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
@@ -63,6 +63,7 @@ import {
ChatRequest,
CheckNumberStatusQuery,
EditMessageRequest,
MessageButtonReply,
MessageFileRequest,
MessageForwardRequest,
MessageImageRequest,
+6 -1
View File
@@ -41,6 +41,7 @@ import {
ChatRequest,
CheckNumberStatusQuery,
EditMessageRequest,
MessageButtonReply,
MessageFileRequest,
MessageForwardRequest,
MessageImageRequest,
@@ -540,6 +541,10 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
throw new AvailableInPlusVersion();
}
sendButtonsReply(request: MessageButtonReply) {
throw new AvailableInPlusVersion();
}
async sendLocation(request: MessageLocationRequest) {
const location = new Location(request.latitude, request.longitude, {
name: request.title,
@@ -1443,7 +1448,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
let mentions = request.mentions;
mentions = mentions ? mentions.map(this.ensureSuffix) : undefined;
const quotedMessageId = request.reply_to;
const quotedMessageId = request.reply_to || request.replyTo;
return {
mentions: mentions,
+1
View File
@@ -16,6 +16,7 @@ export class SessionQuery {
*/
export class SessionBaseRequest {
@IsString()
session: string = WHATSAPP_DEFAULT_SESSION_NAME;
}
+16
View File
@@ -64,6 +64,7 @@ export class GetPresenceQuery extends ChatQuery {}
*/
export class ChatRequest extends SessionBaseRequest {
@ChatIdProperty()
@IsString()
chatId: string;
}
@@ -333,3 +334,18 @@ export class MessageDestination {
from: string;
fromMe: boolean;
}
export class MessageButtonReply extends ChatRequest {
@ReplyToProperty()
@IsString()
@IsNotEmpty()
replyTo?: string;
@IsString()
@IsNotEmpty()
selectedDisplayText: string;
@IsString()
@IsNotEmpty()
selectedButtonID: string;
}