Compare commits

..
19 Commits
Author SHA1 Message Date
devlikepro ef18e311b1 [core] 2025.3.2
Release / WEBJS - chrome - amd64 - chrome (push) Waiting to run
Release / WEBJS - chromium - amd64 - latest (push) Waiting to run
Release / WEBJS - chromium - linux/arm64 - arm (push) Waiting to run
Release / GOWS - none - amd64 - gows (push) Waiting to run
Release / GOWS - none - linux/arm64 - gows-arm (push) Waiting to run
Release / NOWEB - none - amd64 - noweb (push) Waiting to run
Release / NOWEB - none - linux/arm64 - noweb-arm (push) Waiting to run
2025-03-27 14:36:38 +07:00
devlikepro 68de84973a [core] GOWS - extract replyTo
fix #876
2025-03-27 14:36:38 +07:00
devlikepro ef6d4f9998 [core] Add _data (original message) to replyTo field 2025-03-27 14:36:37 +07:00
devlikepro 9f5413963b [core] NOWEB - log errors on profile picture change
fix #873
2025-03-27 14:36:36 +07:00
devlikepro ec7c0a5cb7 [core] check session name in waitUntilStatus 2025-03-27 14:36:36 +07:00
devlikepro f82bd9eadf [core] no browser for non webjs based engines 2025-03-27 14:36:35 +07:00
devlikepro 843c064ebe [core] Up GOWS 2025-03-27 14:36:35 +07:00
devlikepro 8a52e1d1d0 [core] Up Dashboard 2025-03-27 14:36:34 +07:00
devlikepro 8ec220dc9a [core] Status to 50K and more contacts
fix #858
2025-03-27 14:36:33 +07:00
devlikepro 9c4b9f4433 [core] NOWEB - fix scan qr issue 2025-03-27 14:36:33 +07:00
devlikepro 6059a29660 [core] fix message reactions empty for channels preview 2025-03-27 14:36:32 +07:00
devlikepro e65e6764fa [core] NOWEB - presence optimizations
Apply 1 hour ttl for presence information
2025-03-27 14:36:32 +07:00
devlikepro 1e77cb234a [core] NOWEB - use {} for presence 2025-03-27 14:36:31 +07:00
devlikepro 4a357087d1 [core] NOWEB - do not restart after SCAN_QR_CODE expires 2025-03-27 14:36:31 +07:00
devlikepro 6a3823cdff [core] NOWEB - fix the case when STARTING session fails the container
fix #821
fix #860
fix #869
2025-03-27 14:36:30 +07:00
devlikepro c2372ce96e [core] GOWS - fix get contact issue
fix #866
2025-03-27 14:36:30 +07:00
devlikepro 0f5e79b163 [core] Enable all features in Global Webhook
fix #855
2025-03-27 14:36:30 +07:00
devlikepro fa4026c83f [core] Allow only http/https for webhooks 2025-03-27 14:36:30 +07:00
devlikepro 3e4d66ca27 [core] Reply to button
fix #848
2025-03-27 14:36:30 +07:00
26 changed files with 1554 additions and 90 deletions

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) {
+14 -1
View File
@@ -1,9 +1,10 @@
import { Body, Controller, Post } from '@nestjs/common';
import { Body, Controller, Get, Post } from '@nestjs/common';
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
import {
SessionApiParam,
WorkingSessionParam,
} from '@waha/nestjs/params/SessionApiParam';
import { NewMessageIDResponse } from '@waha/structures/chatting.dto';
import { SessionManager } from '../core/abc/manager.abc';
import { WhatsappSession } from '../core/abc/session.abc';
@@ -70,6 +71,18 @@ class StatusController {
) {
return session.deleteStatus(status);
}
@Get('new-message-id')
@SessionApiParam
@ApiOperation({
summary: 'Generate message ID you can use to batch contacts',
})
async getNewMessageId(
@WorkingSessionParam session: WhatsappSession,
): Promise<NewMessageIDResponse> {
const id = await session.generateNewMessageId();
return { id: id };
}
}
export { StatusController };
+18 -19
View File
@@ -1,13 +1,19 @@
import { Injectable } from '@nestjs/common';
import { Injectable, Logger, OnApplicationBootstrap } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { WAHAEvents } from '@waha/structures/enums.dto';
import { GlobalWebhookConfigConfig } from '@waha/core/config/GlobalWebhookConfig';
import { parseBool } from './helpers';
import { WebhookConfig } from './structures/webhooks.config.dto';
@Injectable()
export class WhatsappConfigService {
constructor(private configService: ConfigService) {}
export class WhatsappConfigService implements OnApplicationBootstrap {
private logger: Logger;
private webhookConfig: GlobalWebhookConfigConfig;
constructor(private configService: ConfigService) {
this.logger = new Logger('WhatsappConfigService');
this.webhookConfig = new GlobalWebhookConfigConfig(configService);
}
get schema() {
return this.configService.get('WHATSAPP_API_SCHEMA', 'http');
@@ -113,21 +119,7 @@ export class WhatsappConfigService {
}
getWebhookConfig(): WebhookConfig | undefined {
const url = this.getWebhookUrl();
const events = this.getWebhookEvents();
if (!url || events.length === 0) {
return undefined;
}
return { url: url, events: events };
}
private getWebhookUrl(): string | undefined {
return this.get('WHATSAPP_HOOK_URL');
}
private getWebhookEvents(): WAHAEvents[] {
const value = this.get('WHATSAPP_HOOK_EVENTS', '');
return value ? value.split(',') : [];
return this.webhookConfig.config;
}
getSessionMongoUrl(): string | undefined {
@@ -182,4 +174,11 @@ export class WhatsappConfigService {
const value = this.configService.get('WAHA_DEBUG_MODE', 'false');
return parseBool(value);
}
onApplicationBootstrap() {
const error = this.webhookConfig.validateConfig();
if (error) {
throw new Error(`Invalid global webhook config:\n${error}\n`);
}
}
}
+6
View File
@@ -150,6 +150,12 @@ export abstract class SessionManager
sessionName: string,
expected: WAHASessionStatus[],
): Promise<WhatsappSession> {
if (!sessionName) {
throw new UnprocessableEntityException({
error: `Session name is required`,
session: sessionName,
});
}
const running = await waitUntil(
async () => this.isRunning(sessionName),
this.WAIT_SESSION_RUNNING_INTERVAL,
+12
View File
@@ -46,6 +46,7 @@ import {
ChatRequest,
CheckNumberStatusQuery,
EditMessageRequest,
MessageButtonReply,
MessageContactVcardRequest,
MessageFileRequest,
MessageForwardRequest,
@@ -397,6 +398,10 @@ export abstract class WhatsappSession {
/**
* Other methods
*/
generateNewMessageId(): Promise<string> {
throw new NotImplementedByEngineError();
}
abstract checkNumberStatus(request: CheckNumberStatusQuery);
abstract sendText(request: MessageTextRequest);
@@ -431,6 +436,10 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
sendButtonsReply(request: MessageButtonReply) {
throw new NotImplementedByEngineError();
}
abstract reply(request: MessageReplyRequest);
abstract sendSeen(chat: SendSeenRequest);
@@ -848,6 +857,9 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
/**
* Engine methods
*/
public async getEngineInfo(): Promise<any> {
return {};
}
+167
View File
@@ -0,0 +1,167 @@
import { ConfigService } from '@nestjs/config';
import { WAHAEvents } from '@waha/structures/enums.dto';
import {
CustomHeader,
HmacConfiguration,
RetriesConfiguration,
WebhookConfig,
} from '@waha/structures/webhooks.config.dto';
import { plainToInstance } from 'class-transformer';
import { validateSync, ValidationError } from 'class-validator';
enum Env {
WHATSAPP_HOOK_URL = 'WHATSAPP_HOOK_URL',
WHATSAPP_HOOK_EVENTS = 'WHATSAPP_HOOK_EVENTS',
WHATSAPP_HOOK_RETRIES_POLICY = 'WHATSAPP_HOOK_RETRIES_POLICY',
WHATSAPP_HOOK_RETRIES_DELAY_SECONDS = 'WHATSAPP_HOOK_RETRIES_DELAY_SECONDS',
WHATSAPP_HOOK_RETRIES_ATTEMPTS = 'WHATSAPP_HOOK_RETRIES_ATTEMPTS',
WHATSAPP_HOOK_HMAC_KEY = 'WHATSAPP_HOOK_HMAC_KEY',
WHATSAPP_HOOK_CUSTOM_HEADERS = 'WHATSAPP_HOOK_CUSTOM_HEADERS',
}
export class GlobalWebhookConfigConfig {
protected _config: WebhookConfig | null = null;
constructor(private configService: ConfigService) {}
private getUrl(): string | undefined {
return this.configService.get(Env.WHATSAPP_HOOK_URL);
}
private getEvents(): WAHAEvents[] {
const value = this.configService.get(Env.WHATSAPP_HOOK_EVENTS, '');
return value ? value.split(',') : [];
}
private getHmac(): HmacConfiguration | null {
const key = this.configService.get(Env.WHATSAPP_HOOK_HMAC_KEY, '');
if (!key) {
return null;
}
return {
key: key,
};
}
private getRetries(): RetriesConfiguration | null {
const policy: any = this.configService.get(
Env.WHATSAPP_HOOK_RETRIES_POLICY,
null,
);
const delaySeconds: any = this.configService.get(
Env.WHATSAPP_HOOK_RETRIES_DELAY_SECONDS,
null,
);
const attempts: any = this.configService.get(
Env.WHATSAPP_HOOK_RETRIES_ATTEMPTS,
null,
);
if (!policy && !delaySeconds && !attempts) {
return null;
}
return {
policy: policy,
delaySeconds: delaySeconds,
attempts: attempts,
};
}
private getCustomHeaders(): CustomHeader[] {
// key:value;key2:value
const value = this.configService.get(Env.WHATSAPP_HOOK_CUSTOM_HEADERS, '');
if (!value) {
return [];
}
const headers = value.split(';');
return headers.map((header: string) => {
const parts = header.split(':');
if (parts.length !== 2) {
throw new Error(
`${Env.WHATSAPP_HOOK_CUSTOM_HEADERS} - Invalid custom header, no ':' found in '${header}'`,
);
}
return {
name: parts[0],
value: parts[1],
};
});
}
get config() {
if (!this._config) {
this._config = this.parseWebhookConfig();
}
return this._config;
}
validateConfig(): string | null {
const config = this.parseWebhookConfig();
if (!config) {
return null;
}
const errors = validateSync(config);
if (errors.length > 0) {
return this.formatErrors([], errors, []).join('\n');
}
return;
}
private getEnv(key: string): string | null {
const keys = {
url: Env.WHATSAPP_HOOK_URL,
events: Env.WHATSAPP_HOOK_EVENTS,
'retries.policy': Env.WHATSAPP_HOOK_RETRIES_POLICY,
'retries.delaySeconds': Env.WHATSAPP_HOOK_RETRIES_DELAY_SECONDS,
'retries.attempts': Env.WHATSAPP_HOOK_RETRIES_ATTEMPTS,
'hmac.key': Env.WHATSAPP_HOOK_HMAC_KEY,
};
return keys[key] || null;
}
private formatErrors(
lines: string[],
errors: ValidationError[],
path: string[],
) {
for (const err of errors) {
path = [...path, err.property];
for (const key in err.constraints) {
if (!err.constraints.hasOwnProperty(key)) {
continue;
}
const property = path.join('.');
const env = this.getEnv(property);
if (env) {
const value = this.configService.get(env, '');
const line = `- ${env} (${property}): '${value}' => '${err.value}' - ${err.constraints[key]}`;
lines.push(line);
} else {
const line = `- ${property}: '${err.value}' - ${err.constraints[key]}`;
lines.push(line);
}
}
if (err.children) {
this.formatErrors(lines, err.children, path);
}
}
return lines;
}
private parseWebhookConfig(): WebhookConfig | null {
const url = this.getUrl();
const events = this.getEvents();
if (!url || events.length === 0) {
return null;
}
const data: WebhookConfig = {
url: url,
events: events,
hmac: this.getHmac(),
retries: this.getRetries(),
customHeaders: this.getCustomHeaders(),
};
return plainToInstance(WebhookConfig, data, {
enableImplicitConversion: true,
});
}
}
+326 -2
View File
@@ -2547,9 +2547,11 @@ export namespace messages {
linkPreview?: boolean;
linkPreviewHighQuality?: boolean;
replyTo?: string;
id?: string;
participants?: string[];
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [11], this.#one_of_decls);
if (!Array.isArray(data) && typeof data == "object") {
if ("session" in data && data.session != undefined) {
this.session = data.session;
@@ -2578,6 +2580,12 @@ export namespace messages {
if ("replyTo" in data && data.replyTo != undefined) {
this.replyTo = data.replyTo;
}
if ("id" in data && data.id != undefined) {
this.id = data.id;
}
if ("participants" in data && data.participants != undefined) {
this.participants = data.participants;
}
}
}
get session() {
@@ -2646,6 +2654,18 @@ export namespace messages {
set replyTo(value: string) {
pb_1.Message.setField(this, 9, value);
}
get id() {
return pb_1.Message.getFieldWithDefault(this, 10, "") as string;
}
set id(value: string) {
pb_1.Message.setField(this, 10, value);
}
get participants() {
return pb_1.Message.getFieldWithDefault(this, 11, []) as string[];
}
set participants(value: string[]) {
pb_1.Message.setField(this, 11, value);
}
static fromObject(data: {
session?: ReturnType<typeof Session.prototype.toObject>;
jid?: string;
@@ -2656,6 +2676,8 @@ export namespace messages {
linkPreview?: boolean;
linkPreviewHighQuality?: boolean;
replyTo?: string;
id?: string;
participants?: string[];
}): MessageRequest {
const message = new MessageRequest({});
if (data.session != null) {
@@ -2685,6 +2707,12 @@ export namespace messages {
if (data.replyTo != null) {
message.replyTo = data.replyTo;
}
if (data.id != null) {
message.id = data.id;
}
if (data.participants != null) {
message.participants = data.participants;
}
return message;
}
toObject() {
@@ -2698,6 +2726,8 @@ export namespace messages {
linkPreview?: boolean;
linkPreviewHighQuality?: boolean;
replyTo?: string;
id?: string;
participants?: string[];
} = {};
if (this.session != null) {
data.session = this.session.toObject();
@@ -2726,6 +2756,12 @@ export namespace messages {
if (this.replyTo != null) {
data.replyTo = this.replyTo;
}
if (this.id != null) {
data.id = this.id;
}
if (this.participants != null) {
data.participants = this.participants;
}
return data;
}
serialize(): Uint8Array;
@@ -2750,6 +2786,10 @@ export namespace messages {
writer.writeBool(8, this.linkPreviewHighQuality);
if (this.replyTo.length)
writer.writeString(9, this.replyTo);
if (this.id.length)
writer.writeString(10, this.id);
if (this.participants.length)
writer.writeRepeatedString(11, this.participants);
if (!w)
return writer.getResultBuffer();
}
@@ -2786,6 +2826,12 @@ export namespace messages {
case 9:
message.replyTo = reader.readString();
break;
case 10:
message.id = reader.readString();
break;
case 11:
pb_1.Message.addToRepeatedField(message, 11, reader.readString());
break;
default: reader.skipField();
}
}
@@ -3076,6 +3122,73 @@ export namespace messages {
return MessageResponse.deserialize(bytes);
}
}
export class NewMessageIDResponse extends pb_1.Message {
#one_of_decls: number[][] = [];
constructor(data?: any[] | {
id?: 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 ("id" in data && data.id != undefined) {
this.id = data.id;
}
}
}
get id() {
return pb_1.Message.getFieldWithDefault(this, 1, "") as string;
}
set id(value: string) {
pb_1.Message.setField(this, 1, value);
}
static fromObject(data: {
id?: string;
}): NewMessageIDResponse {
const message = new NewMessageIDResponse({});
if (data.id != null) {
message.id = data.id;
}
return message;
}
toObject() {
const data: {
id?: string;
} = {};
if (this.id != null) {
data.id = this.id;
}
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.id.length)
writer.writeString(1, this.id);
if (!w)
return writer.getResultBuffer();
}
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): NewMessageIDResponse {
const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new NewMessageIDResponse();
while (reader.nextField()) {
if (reader.isEndGroup())
break;
switch (reader.getFieldNumber()) {
case 1:
message.id = reader.readString();
break;
default: reader.skipField();
}
}
return message;
}
serializeBinary(): Uint8Array {
return this.serialize();
}
static deserializeBinary(bytes: Uint8Array): NewMessageIDResponse {
return NewMessageIDResponse.deserialize(bytes);
}
}
export class ProfilePictureRequest extends pb_1.Message {
#one_of_decls: number[][] = [];
constructor(data?: any[] | {
@@ -3236,6 +3349,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[] | {
@@ -3980,9 +4255,10 @@ export namespace messages {
jid?: string;
sender?: string;
messageId?: string;
participants?: string[];
}) {
super();
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [5], this.#one_of_decls);
if (!Array.isArray(data) && typeof data == "object") {
if ("session" in data && data.session != undefined) {
this.session = data.session;
@@ -3996,6 +4272,9 @@ export namespace messages {
if ("messageId" in data && data.messageId != undefined) {
this.messageId = data.messageId;
}
if ("participants" in data && data.participants != undefined) {
this.participants = data.participants;
}
}
}
get session() {
@@ -4025,11 +4304,18 @@ export namespace messages {
set messageId(value: string) {
pb_1.Message.setField(this, 4, value);
}
get participants() {
return pb_1.Message.getFieldWithDefault(this, 5, []) as string[];
}
set participants(value: string[]) {
pb_1.Message.setField(this, 5, value);
}
static fromObject(data: {
session?: ReturnType<typeof Session.prototype.toObject>;
jid?: string;
sender?: string;
messageId?: string;
participants?: string[];
}): RevokeMessageRequest {
const message = new RevokeMessageRequest({});
if (data.session != null) {
@@ -4044,6 +4330,9 @@ export namespace messages {
if (data.messageId != null) {
message.messageId = data.messageId;
}
if (data.participants != null) {
message.participants = data.participants;
}
return message;
}
toObject() {
@@ -4052,6 +4341,7 @@ export namespace messages {
jid?: string;
sender?: string;
messageId?: string;
participants?: string[];
} = {};
if (this.session != null) {
data.session = this.session.toObject();
@@ -4065,6 +4355,9 @@ export namespace messages {
if (this.messageId != null) {
data.messageId = this.messageId;
}
if (this.participants != null) {
data.participants = this.participants;
}
return data;
}
serialize(): Uint8Array;
@@ -4079,6 +4372,8 @@ export namespace messages {
writer.writeString(3, this.sender);
if (this.messageId.length)
writer.writeString(4, this.messageId);
if (this.participants.length)
writer.writeRepeatedString(5, this.participants);
if (!w)
return writer.getResultBuffer();
}
@@ -4100,6 +4395,9 @@ export namespace messages {
case 4:
message.messageId = reader.readString();
break;
case 5:
pb_1.Message.addToRepeatedField(message, 5, reader.readString());
break;
default: reader.skipField();
}
}
@@ -7282,6 +7580,15 @@ export namespace messages {
responseSerialize: (message: CheckPhonesResponse) => Buffer.from(message.serialize()),
responseDeserialize: (bytes: Buffer) => CheckPhonesResponse.deserialize(new Uint8Array(bytes))
},
GenerateNewMessageID: {
path: "/messages.MessageService/GenerateNewMessageID",
requestStream: false,
responseStream: false,
requestSerialize: (message: Session) => Buffer.from(message.serialize()),
requestDeserialize: (bytes: Buffer) => Session.deserialize(new Uint8Array(bytes)),
responseSerialize: (message: NewMessageIDResponse) => Buffer.from(message.serialize()),
responseDeserialize: (bytes: Buffer) => NewMessageIDResponse.deserialize(new Uint8Array(bytes))
},
SendMessage: {
path: "/messages.MessageService/SendMessage",
requestStream: false,
@@ -7327,6 +7634,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,
@@ -7483,11 +7799,13 @@ export namespace messages {
abstract SendChatPresence(call: grpc_1.ServerUnaryCall<ChatPresenceRequest, Empty>, callback: grpc_1.sendUnaryData<Empty>): void;
abstract SubscribePresence(call: grpc_1.ServerUnaryCall<SubscribePresenceRequest, Empty>, callback: grpc_1.sendUnaryData<Empty>): void;
abstract CheckPhones(call: grpc_1.ServerUnaryCall<CheckPhonesRequest, CheckPhonesResponse>, callback: grpc_1.sendUnaryData<CheckPhonesResponse>): void;
abstract GenerateNewMessageID(call: grpc_1.ServerUnaryCall<Session, NewMessageIDResponse>, callback: grpc_1.sendUnaryData<NewMessageIDResponse>): void;
abstract SendMessage(call: grpc_1.ServerUnaryCall<MessageRequest, MessageResponse>, callback: grpc_1.sendUnaryData<MessageResponse>): void;
abstract SendReaction(call: grpc_1.ServerUnaryCall<MessageReaction, MessageResponse>, callback: grpc_1.sendUnaryData<MessageResponse>): void;
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;
@@ -7591,6 +7909,9 @@ export namespace messages {
CheckPhones: GrpcUnaryServiceInterface<CheckPhonesRequest, CheckPhonesResponse> = (message: CheckPhonesRequest, metadata: grpc_1.Metadata | grpc_1.CallOptions | grpc_1.requestCallback<CheckPhonesResponse>, options?: grpc_1.CallOptions | grpc_1.requestCallback<CheckPhonesResponse>, callback?: grpc_1.requestCallback<CheckPhonesResponse>): grpc_1.ClientUnaryCall => {
return super.CheckPhones(message, metadata, options, callback);
};
GenerateNewMessageID: GrpcUnaryServiceInterface<Session, NewMessageIDResponse> = (message: Session, metadata: grpc_1.Metadata | grpc_1.CallOptions | grpc_1.requestCallback<NewMessageIDResponse>, options?: grpc_1.CallOptions | grpc_1.requestCallback<NewMessageIDResponse>, callback?: grpc_1.requestCallback<NewMessageIDResponse>): grpc_1.ClientUnaryCall => {
return super.GenerateNewMessageID(message, metadata, options, callback);
};
SendMessage: GrpcUnaryServiceInterface<MessageRequest, MessageResponse> = (message: MessageRequest, 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.SendMessage(message, metadata, options, callback);
};
@@ -7606,6 +7927,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);
};
+45 -1
View File
@@ -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');
@@ -279,6 +290,17 @@ function deserialize_messages_MessageResponse(buffer_arg) {
return gows_pb.MessageResponse.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_messages_NewMessageIDResponse(arg) {
if (!(arg instanceof gows_pb.NewMessageIDResponse)) {
throw new Error('Expected argument of type messages.NewMessageIDResponse');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_messages_NewMessageIDResponse(buffer_arg) {
return gows_pb.NewMessageIDResponse.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_messages_Newsletter(arg) {
if (!(arg instanceof gows_pb.Newsletter)) {
throw new Error('Expected argument of type messages.Newsletter');
@@ -899,7 +921,18 @@ getProfilePicture: {
//
// Message
//
sendMessage: {
generateNewMessageID: {
path: '/messages.MessageService/GenerateNewMessageID',
requestStream: false,
responseStream: false,
requestType: gows_pb.Session,
responseType: gows_pb.NewMessageIDResponse,
requestSerialize: serialize_messages_Session,
requestDeserialize: deserialize_messages_Session,
responseSerialize: serialize_messages_NewMessageIDResponse,
responseDeserialize: deserialize_messages_NewMessageIDResponse,
},
sendMessage: {
path: '/messages.MessageService/SendMessage',
requestStream: false,
responseStream: false,
@@ -954,6 +987,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
//
+591 -4
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);
@@ -52,6 +53,7 @@ goog.exportSymbol('proto.messages.MessageFilters', null, global);
goog.exportSymbol('proto.messages.MessageReaction', null, global);
goog.exportSymbol('proto.messages.MessageRequest', null, global);
goog.exportSymbol('proto.messages.MessageResponse', null, global);
goog.exportSymbol('proto.messages.NewMessageIDResponse', null, global);
goog.exportSymbol('proto.messages.Newsletter', null, global);
goog.exportSymbol('proto.messages.NewsletterInfoRequest', null, global);
goog.exportSymbol('proto.messages.NewsletterList', null, global);
@@ -671,7 +673,7 @@ if (goog.DEBUG && !COMPILED) {
* @constructor
*/
proto.messages.MessageRequest = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
jspb.Message.initialize(this, opt_data, 0, -1, proto.messages.MessageRequest.repeatedFields_, null);
};
goog.inherits(proto.messages.MessageRequest, jspb.Message);
if (goog.DEBUG && !COMPILED) {
@@ -723,6 +725,27 @@ if (goog.DEBUG && !COMPILED) {
*/
proto.messages.MessageResponse.displayName = 'proto.messages.MessageResponse';
}
/**
* 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.NewMessageIDResponse = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.messages.NewMessageIDResponse, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.messages.NewMessageIDResponse.displayName = 'proto.messages.NewMessageIDResponse';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
@@ -765,6 +788,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
@@ -923,7 +967,7 @@ if (goog.DEBUG && !COMPILED) {
* @constructor
*/
proto.messages.RevokeMessageRequest = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
jspb.Message.initialize(this, opt_data, 0, -1, proto.messages.RevokeMessageRequest.repeatedFields_, null);
};
goog.inherits(proto.messages.RevokeMessageRequest, jspb.Message);
if (goog.DEBUG && !COMPILED) {
@@ -6294,6 +6338,13 @@ proto.messages.Media.prototype.setFilename = function(value) {
/**
* List of repeated fields within this message type.
* @private {!Array<number>}
* @const
*/
proto.messages.MessageRequest.repeatedFields_ = [11];
if (jspb.Message.GENERATE_TO_OBJECT) {
@@ -6333,7 +6384,9 @@ proto.messages.MessageRequest.toObject = function(includeInstance, msg) {
font: (f = msg.getFont()) && proto.messages.OptionalUInt32.toObject(includeInstance, f),
linkpreview: jspb.Message.getBooleanFieldWithDefault(msg, 7, false),
linkpreviewhighquality: jspb.Message.getBooleanFieldWithDefault(msg, 8, false),
replyto: jspb.Message.getFieldWithDefault(msg, 9, "")
replyto: jspb.Message.getFieldWithDefault(msg, 9, ""),
id: jspb.Message.getFieldWithDefault(msg, 10, ""),
participantsList: (f = jspb.Message.getRepeatedField(msg, 11)) == null ? undefined : f
};
if (includeInstance) {
@@ -6410,6 +6463,14 @@ proto.messages.MessageRequest.deserializeBinaryFromReader = function(msg, reader
var value = /** @type {string} */ (reader.readString());
msg.setReplyto(value);
break;
case 10:
var value = /** @type {string} */ (reader.readString());
msg.setId(value);
break;
case 11:
var value = /** @type {string} */ (reader.readString());
msg.addParticipants(value);
break;
default:
reader.skipField();
break;
@@ -6506,6 +6567,20 @@ proto.messages.MessageRequest.serializeBinaryToWriter = function(message, writer
f
);
}
f = message.getId();
if (f.length > 0) {
writer.writeString(
10,
f
);
}
f = message.getParticipantsList();
if (f.length > 0) {
writer.writeRepeatedString(
11,
f
);
}
};
@@ -6747,6 +6822,61 @@ proto.messages.MessageRequest.prototype.setReplyto = function(value) {
};
/**
* optional string id = 10;
* @return {string}
*/
proto.messages.MessageRequest.prototype.getId = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 10, ""));
};
/**
* @param {string} value
* @return {!proto.messages.MessageRequest} returns this
*/
proto.messages.MessageRequest.prototype.setId = function(value) {
return jspb.Message.setProto3StringField(this, 10, value);
};
/**
* repeated string participants = 11;
* @return {!Array<string>}
*/
proto.messages.MessageRequest.prototype.getParticipantsList = function() {
return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 11));
};
/**
* @param {!Array<string>} value
* @return {!proto.messages.MessageRequest} returns this
*/
proto.messages.MessageRequest.prototype.setParticipantsList = function(value) {
return jspb.Message.setField(this, 11, value || []);
};
/**
* @param {string} value
* @param {number=} opt_index
* @return {!proto.messages.MessageRequest} returns this
*/
proto.messages.MessageRequest.prototype.addParticipants = function(value, opt_index) {
return jspb.Message.addToRepeatedField(this, 11, value, opt_index);
};
/**
* Clears the list making it empty but non-null.
* @return {!proto.messages.MessageRequest} returns this
*/
proto.messages.MessageRequest.prototype.clearParticipantsList = function() {
return this.setParticipantsList([]);
};
@@ -7232,6 +7362,136 @@ proto.messages.MessageResponse.prototype.hasMessage = function() {
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.NewMessageIDResponse.prototype.toObject = function(opt_includeInstance) {
return proto.messages.NewMessageIDResponse.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.NewMessageIDResponse} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.messages.NewMessageIDResponse.toObject = function(includeInstance, msg) {
var f, obj = {
id: jspb.Message.getFieldWithDefault(msg, 1, "")
};
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.NewMessageIDResponse}
*/
proto.messages.NewMessageIDResponse.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.messages.NewMessageIDResponse;
return proto.messages.NewMessageIDResponse.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* @param {!proto.messages.NewMessageIDResponse} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.messages.NewMessageIDResponse}
*/
proto.messages.NewMessageIDResponse.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.setId(value);
break;
default:
reader.skipField();
break;
}
}
return msg;
};
/**
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.messages.NewMessageIDResponse.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.messages.NewMessageIDResponse.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.NewMessageIDResponse} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.messages.NewMessageIDResponse.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getId();
if (f.length > 0) {
writer.writeString(
1,
f
);
}
};
/**
* optional string id = 1;
* @return {string}
*/
proto.messages.NewMessageIDResponse.prototype.getId = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
};
/**
* @param {string} value
* @return {!proto.messages.NewMessageIDResponse} returns this
*/
proto.messages.NewMessageIDResponse.prototype.setId = function(value) {
return jspb.Message.setProto3StringField(this, 1, value);
};
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto.
@@ -7543,6 +7803,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.
@@ -8942,6 +9473,13 @@ proto.messages.CheckPhonesResponse.prototype.clearInfosList = function() {
/**
* List of repeated fields within this message type.
* @private {!Array<number>}
* @const
*/
proto.messages.RevokeMessageRequest.repeatedFields_ = [5];
if (jspb.Message.GENERATE_TO_OBJECT) {
@@ -8976,7 +9514,8 @@ proto.messages.RevokeMessageRequest.toObject = function(includeInstance, msg) {
session: (f = msg.getSession()) && proto.messages.Session.toObject(includeInstance, f),
jid: jspb.Message.getFieldWithDefault(msg, 2, ""),
sender: jspb.Message.getFieldWithDefault(msg, 3, ""),
messageid: jspb.Message.getFieldWithDefault(msg, 4, "")
messageid: jspb.Message.getFieldWithDefault(msg, 4, ""),
participantsList: (f = jspb.Message.getRepeatedField(msg, 5)) == null ? undefined : f
};
if (includeInstance) {
@@ -9030,6 +9569,10 @@ proto.messages.RevokeMessageRequest.deserializeBinaryFromReader = function(msg,
var value = /** @type {string} */ (reader.readString());
msg.setMessageid(value);
break;
case 5:
var value = /** @type {string} */ (reader.readString());
msg.addParticipants(value);
break;
default:
reader.skipField();
break;
@@ -9088,6 +9631,13 @@ proto.messages.RevokeMessageRequest.serializeBinaryToWriter = function(message,
f
);
}
f = message.getParticipantsList();
if (f.length > 0) {
writer.writeRepeatedString(
5,
f
);
}
};
@@ -9182,6 +9732,43 @@ proto.messages.RevokeMessageRequest.prototype.setMessageid = function(value) {
};
/**
* repeated string participants = 5;
* @return {!Array<string>}
*/
proto.messages.RevokeMessageRequest.prototype.getParticipantsList = function() {
return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 5));
};
/**
* @param {!Array<string>} value
* @return {!proto.messages.RevokeMessageRequest} returns this
*/
proto.messages.RevokeMessageRequest.prototype.setParticipantsList = function(value) {
return jspb.Message.setField(this, 5, value || []);
};
/**
* @param {string} value
* @param {number=} opt_index
* @return {!proto.messages.RevokeMessageRequest} returns this
*/
proto.messages.RevokeMessageRequest.prototype.addParticipants = function(value, opt_index) {
return jspb.Message.addToRepeatedField(this, 5, value, opt_index);
};
/**
* Clears the list making it empty but non-null.
* @return {!proto.messages.RevokeMessageRequest} returns this
*/
proto.messages.RevokeMessageRequest.prototype.clearParticipantsList = function() {
return this.setParticipantsList([]);
};
+41 -10
View File
@@ -1,4 +1,5 @@
import {
getContentType,
getUrlFromDirectPath,
isJidGroup,
isJidStatusBroadcast,
@@ -63,6 +64,7 @@ import {
ChatRequest,
CheckNumberStatusQuery,
EditMessageRequest,
MessageButtonReply,
MessageFileRequest,
MessageForwardRequest,
MessageImageRequest,
@@ -128,6 +130,7 @@ import { promisify } from 'util';
import * as gows from './types';
import MessageServiceClient = messages.MessageServiceClient;
import { ReplyToMessage } from '@waha/structures/message.dto';
enum WhatsMeowEvent {
CONNECTED = 'gows.ConnectedEventData',
@@ -537,6 +540,14 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
/**
* Other methods
*/
async generateNewMessageId(): Promise<string> {
const response = await promisify(this.client.GenerateNewMessageID)(
this.session,
);
const data = response.toObject();
return data.id;
}
async sendText(request: MessageTextRequest) {
const jid = toJID(this.ensureSuffix(request.chatId));
const message = new messages.MessageRequest({
@@ -586,19 +597,19 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
return this.messageResponse(jid, data);
}
protected checkStatusRequest(request: StatusRequest) {
if (request.contacts && request.contacts?.length > 0) {
const msg =
"GOWS doesn't accept 'contacts'. Remove the field to send status to all contacts.";
throw new UnprocessableEntityException(msg);
protected async prepareJidsForStatus(contacts: string[]) {
if (!contacts || contacts.length == 0) {
return [];
}
return contacts.map(toJID);
}
public async sendTextStatus(status: TextStatus) {
this.checkStatusRequest(status);
const participants = await this.prepareJidsForStatus(status.contacts);
const message = new messages.MessageRequest({
id: status.id,
jid: Jid.BROADCAST,
participants: participants,
text: status.text,
session: this.session,
backgroundColor: new messages.OptionalString({
@@ -616,13 +627,14 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
}
public async deleteStatus(request: DeleteStatusRequest) {
this.checkStatusRequest(request);
const participants = await this.prepareJidsForStatus(request.contacts);
const key = parseMessageIdSerialized(request.id, true);
const message = new messages.RevokeMessageRequest({
session: this.session,
jid: BROADCAST_ID,
sender: '',
messageId: key.id,
participants: participants,
});
const response = await promisify(this.client.RevokeMessage)(message);
const data = response.toObject();
@@ -1190,7 +1202,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
session: this.session,
id: jid,
});
const response = await promisify(this.client.GetContact)(request);
const response = await promisify(this.client.GetContactById)(request);
const data = parseJson(response);
return this.toWAContact(data);
}
@@ -1386,7 +1398,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
const fromToParticipant = getFromToParticipant(message);
const id = buildMessageId(message);
const body = this.extractBody(message.Message);
const replyTo = null; // TODO: this.extractReplyTo(message.message);
const replyTo = this.extractReplyTo(message.Message);
let ack;
if (message.Status) {
ack = statusToAck(message.Status);
@@ -1444,6 +1456,25 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
return body;
}
protected extractReplyTo(message): ReplyToMessage | null {
const msgType = getContentType(message);
const contextInfo = message[msgType]?.contextInfo;
if (!contextInfo) {
return null;
}
const quotedMessage = contextInfo.quotedMessage;
if (!quotedMessage) {
return null;
}
const body = this.extractBody(quotedMessage);
return {
id: contextInfo.stanzaID,
participant: toCusFormat(contextInfo.participant),
body: body,
_data: quotedMessage,
};
}
public async getEngineInfo() {
const clientState = this.client?.getChannel().getConnectivityState(false);
const streamState = this.stream$?.client
+50
View File
@@ -0,0 +1,50 @@
import * as NodeCache from 'node-cache';
export class PresenceProxy {
private cache: NodeCache;
public proxy: Record<string, any>;
constructor() {
this.cache = new NodeCache({
stdTTL: 60 * 60, // 1 hour
useClones: false,
});
this.proxy = new Proxy(
{},
{
get: (_, prop: string) => {
return this.cache.get(prop);
},
set: (_, prop: string, value) => {
this.cache.set(prop, value);
return true;
},
deleteProperty: (_, prop: string) => {
return this.cache.del(prop) > 0;
},
has: (_, prop: string) => {
return this.cache.has(prop);
},
ownKeys: () => {
return this.cache.keys();
},
getOwnPropertyDescriptor: (_, prop: string) => {
if (this.cache.has(prop)) {
return {
enumerable: true,
configurable: true,
};
}
return undefined;
},
},
);
}
}
+150 -17
View File
@@ -140,6 +140,7 @@ import { MeInfo } from '@waha/structures/sessions.dto';
import {
BROADCAST_ID,
DeleteStatusRequest,
StatusRequest,
TextStatus,
} from '@waha/structures/status.dto';
import {
@@ -154,6 +155,7 @@ import { sleep, waitUntil } from '@waha/utils/promiseTimeout';
import { exclude } from '@waha/utils/reactive/ops/exclude';
import { SingleDelayedJobRunner } from '@waha/utils/SingleDelayedJobRunner';
import { SinglePeriodicJobRunner } from '@waha/utils/SinglePeriodicJobRunner';
import { StatusTracker } from '@waha/utils/StatusTracker';
import * as Buffer from 'buffer';
import { Agent } from 'https';
import * as lodash from 'lodash';
@@ -170,6 +172,8 @@ import {
share,
} from 'rxjs';
import { map } from 'rxjs/operators';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const promiseRetry = require('promise-retry');
import { INowebStore } from './store/INowebStore';
import { NowebPersistentStore } from './store/NowebPersistentStore';
@@ -216,6 +220,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
store: INowebStore;
private qr: QR;
private statusTracker = new StatusTracker();
public constructor(config) {
super(config);
this.shouldRestart = true;
@@ -254,6 +260,15 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
this.authNOWEBStore = null;
}
protected set status(value: WAHASessionStatus) {
this.statusTracker.track(value);
super.status = value;
}
public get status() {
return super.status;
}
async start() {
this.status = WAHASessionStatus.STARTING;
await this.buildClient().catch((err) => {
@@ -419,6 +434,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
);
return;
}
this.startDelayedJob.schedule(async () => {
if (!this.shouldRestart) {
this.logger.warn(
@@ -443,12 +459,36 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
this.resubscribeToKnownPresences();
return;
} else if (connection === 'close') {
const shouldReconnect =
// @ts-ignore: Property output does not exist on type 'Error'
lastDisconnect.error?.output?.statusCode !==
DisconnectReason.loggedOut;
this.qr.save('');
// reconnect if not logged out
const statusCode = lastDisconnect.error?.output?.statusCode;
// Restart required from the server
const restartRequired = statusCode === DisconnectReason.restartRequired;
if (restartRequired) {
this.restartClient();
return;
}
// Stuck in STARTING status
if (this.statusTracker.isStuckInStarting()) {
this.logger.error(
'Session stuck in STARTING status, force stopping the session.',
);
await this.failed();
return;
}
// Do not reconnect if the QR code has not been scanned yet
if (this.status == WAHASessionStatus.SCAN_QR_CODE) {
this.logger.warn(
'QR code has not been scanned yet, force stopping the session.',
);
await this.failed();
return;
}
// Reconnect if not logged out
const shouldReconnect = statusCode !== DisconnectReason.loggedOut;
if (shouldReconnect) {
if (lastDisconnect.error) {
this.logger.info(
@@ -456,12 +496,14 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
);
}
this.restartClient();
} else {
this.logger.error(
`Connection closed due to '${lastDisconnect.error}', do not reconnect the session.`,
);
await this.failed();
return;
}
// Unknown error or logged out
this.logger.error(
`Connection closed due to '${lastDisconnect.error}', do not reconnect the session.`,
);
await this.failed();
}
// Save QR
@@ -691,6 +733,10 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
};
}
async generateNewMessageId(): Promise<string> {
return this.generateMessageID();
}
async sendText(request: MessageTextRequest) {
const chatId = toJID(this.ensureSuffix(request.chatId));
const message = {
@@ -1314,7 +1360,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
public async getPresence(chatId: string): Promise<WAHAChatPresences> {
const remoteJid = toJID(chatId);
if (!(remoteJid in this.store.presences)) {
this.store.presences[remoteJid] = [];
this.store.presences[remoteJid] = {};
await this.sock.presenceSubscribe(remoteJid);
await sleep(1000);
}
@@ -1330,21 +1376,101 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
/**
* Status methods
*/
public async sendStatusMessage(
message: any,
options: any,
jids: string[],
batchSize?: number,
) {
if (!batchSize || batchSize == 0) {
batchSize = 5_000;
}
const chunks = lodash.chunk(jids, batchSize);
if (chunks.length == 0) {
throw new UnprocessableEntityException('No participants to send status');
}
const logger = this.logger.child({
'message.id': options.messageId,
chunks: chunks.length,
size: batchSize,
});
logger.info(`Sending status message to ${jids.length} participants`);
let result = null;
for (const [index, participants] of chunks.entries()) {
const batchOptions = { ...options };
batchOptions.statusJidList = participants;
const r = await this.sendStatusMessageOneChunk(
message,
batchOptions,
logger,
index,
);
result = result || r;
}
logger.info(
`Sending status message to ${jids.length} participants - success`,
);
return result;
}
private async sendStatusMessageOneChunk(
message: any,
options: any,
logger: any,
index: number,
) {
// https://github.com/IndigoUnited/node-promise-retry
const retryOptions = {
retries: 5,
minTimeout: 1000,
maxTimeout: 6000,
};
try {
const resp = await promiseRetry((retry, number) => {
return this.sock
.sendMessage(BROADCAST_ID, message, options)
.catch(retry);
}, retryOptions);
logger.info(`Sending status message (${index + 1} chunk) - success`);
return resp;
} catch (err) {
logger.error(`Sending status message (${index + 1} chunk - failed`);
logger.error(err, err.stack);
throw err;
}
}
public async sendTextStatus(status: TextStatus) {
const message = {
text: status.text,
linkPreview: this.getLinkPreview(status),
};
const jids = await this.prepareJidsForStatus(status.contacts);
const messageId = this.generateMessageID();
if (!status.id) {
this.upsertMeInJIDs(jids);
}
const messageId = this.prepareMessageIdForStatus(status);
const options: MiscMessageGenerationOptions = {
backgroundColor: status.backgroundColor,
font: status.font,
statusJidList: jids,
linkPreviewHighQuality: status.linkPreviewHighQuality,
messageId: messageId,
};
return await this.sock.sendMessage(BROADCAST_ID, message, options);
return await this.sendStatusMessage(
message,
options,
jids,
status.contacts?.length,
);
}
protected prepareMessageIdForStatus(status: StatusRequest) {
if (status.id) {
this.saveSentMessageId(status.id);
return status.id;
}
return this.generateMessageID();
}
protected async prepareJidsForStatus(contacts: string[]) {
@@ -1354,7 +1480,6 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
} else {
jids = await this.fetchMyContactsJids();
}
this.upsertMeInJIDs(jids);
return jids;
}
@@ -1370,12 +1495,18 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
key.fromMe = true;
key.remoteJid = BROADCAST_ID;
const jids = await this.prepareJidsForStatus(request.contacts);
this.upsertMeInJIDs(jids);
const newMessageId = this.generateMessageID();
const options = {
statusJidList: jids,
messageId: newMessageId,
};
return await this.sock.sendMessage(BROADCAST_ID, { delete: key }, options);
return await this.sendStatusMessage(
{ delete: key },
options,
jids,
request.contacts?.length,
);
}
protected upsertMeInJIDs(jids: string[]) {
@@ -1384,7 +1515,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
}
const myJID = jidNormalizedUser(this.sock.authState.creds.me.id);
if (!jids.includes(myJID)) {
jids.push(myJID);
// insert my jid first
jids.unshift(myJID);
}
}
@@ -1908,6 +2040,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
id: contextInfo.stanzaId,
participant: toCusFormat(contextInfo.participant),
body: body,
_data: quotedMessage,
};
}
@@ -7,6 +7,7 @@ import makeWASocket, {
} from '@adiwajshing/baileys';
import { Label } from '@adiwajshing/baileys/lib/Types/Label';
import { BadRequestException } from '@nestjs/common';
import { PresenceProxy } from '@waha/core/engines/noweb/PresenceProxy';
import { GetChatMessagesFilter } from '@waha/structures/chats.dto';
import { PaginationParams } from '@waha/structures/pagination.dto';
import { PaginatorInMemory } from '@waha/utils/Paginator';
@@ -26,6 +27,10 @@ export class NowebInMemoryStore implements INowebStore {
constructor() {
this.store = makeInMemoryStore({ logger: logger });
const presences = new PresenceProxy().proxy;
this.store.presences = presences;
// Adjust inline even handler
this.store.setPresences(presences);
}
init(): Promise<void> {
@@ -18,6 +18,7 @@ import {
LabelAssociation,
LabelAssociationType,
} from '@adiwajshing/baileys/lib/Types/LabelAssociation';
import { PresenceProxy } from '@waha/core/engines/noweb/PresenceProxy';
import { IGroupRepository } from '@waha/core/engines/noweb/store/IGroupRepository';
import { ILabelAssociationRepository } from '@waha/core/engines/noweb/store/ILabelAssociationsRepository';
import { ILabelsRepository } from '@waha/core/engines/noweb/store/ILabelsRepository';
@@ -71,7 +72,7 @@ export class NowebPersistentStore implements INowebStore {
this.messagesRepo = storage.getMessagesRepository();
this.labelsRepo = storage.getLabelsRepository();
this.labelAssociationsRepo = storage.getLabelAssociationRepository();
this.presences = {};
this.presences = new PresenceProxy().proxy;
this.lock = new AsyncLock({ maxPending: Infinity });
}
@@ -363,7 +364,12 @@ export class NowebPersistentStore implements INowebStore {
if (update.imgUrl === 'changed') {
contact.imgUrl = this.socket
? await this.socket?.profilePictureUrl(contact.id)
? await this.socket?.profilePictureUrl(contact.id).catch((error) => {
this.logger.warn(
`failed to get profile picture for contact '${contact.id}': ${error}`,
);
return undefined;
})
: undefined;
} else if (update.imgUrl === 'removed') {
delete contact.imgUrl;
+7 -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,
@@ -1417,6 +1422,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
id: quotedMsg.id?.id,
participant: quotedMsg.author || quotedMsg.from,
body: quotedMsg.caption || quotedMsg.body,
_data: quotedMsg,
};
}
@@ -1443,7 +1449,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,
+3
View File
@@ -31,6 +31,9 @@ function splitAt(str: string, index) {
}
export function sortObjectByValues(obj: any) {
if (!obj) {
return obj;
}
return Object.fromEntries(
// @ts-ignore
Object.entries(obj).sort(([, v1], [, v2]) => v2 - v1),
+1
View File
@@ -16,6 +16,7 @@ export class SessionQuery {
*/
export class SessionBaseRequest {
@IsString()
session: string = WHATSAPP_DEFAULT_SESSION_NAME;
}
+26 -1
View File
@@ -5,7 +5,7 @@ import {
getSchemaPath,
} from '@nestjs/swagger';
import { GetChatMessagesQuery } from '@waha/structures/chats.dto';
import { IsNotEmpty, IsNumber, IsString } from 'class-validator';
import { IsNotEmpty, IsString } from 'class-validator';
import {
SessionBaseRequest,
@@ -64,6 +64,7 @@ export class GetPresenceQuery extends ChatQuery {}
*/
export class ChatRequest extends SessionBaseRequest {
@ChatIdProperty()
@IsString()
chatId: string;
}
@@ -333,3 +334,27 @@ 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;
}
export class NewMessageIDResponse {
@ApiProperty({
description: 'Pre-generated message id',
example: 'BBBBBBBBBBBBBBBBB',
required: true,
})
id: string;
}
+3
View File
@@ -29,6 +29,9 @@ export enum WAHAEvents {
ENGINE_EVENT = 'engine.event',
}
export type AllEventType = WAHAEvents | '*';
export const AllEvents = [...Object.values(WAHAEvents), '*'];
// All but no state.change, it's internal one
export const WAHAEventsWild = Object.values(WAHAEvents).filter(
(e) => e !== WAHAEvents.STATE_CHANGE && e !== WAHAEvents.ENGINE_EVENT,
+5
View File
@@ -15,4 +15,9 @@ export class ReplyToMessage {
example: 'Hello!',
})
body?: string;
@ApiProperty({
description: "Raw data from reply's message",
})
_data?: any;
}
+19 -22
View File
@@ -17,24 +17,30 @@ const ContactsProperty = ApiProperty({
required: false,
});
export interface StatusRequest {
contacts?: string[];
}
export class TextStatus {
text: string = 'Have a look! https://github.com/';
backgroundColor: string = '#38b42f';
font: number = 0;
export class StatusRequest {
@ApiProperty({
description: 'Pre-generated status message id',
example: 'BBBBBBBBBBBBBBBBB',
default: null,
required: false,
})
id?: string;
@ContactsProperty
contacts?: string[];
}
export class TextStatus extends StatusRequest {
text: string = 'Have a look! https://github.com/';
backgroundColor: string = '#38b42f';
font: number = 0;
linkPreview?: boolean = true;
linkPreviewHighQuality?: boolean = false;
}
@ApiExtraModels(RemoteFile, BinaryFile)
export class ImageStatus {
export class ImageStatus extends StatusRequest {
@ApiProperty({
oneOf: [
{ $ref: getSchemaPath(RemoteFile) },
@@ -44,13 +50,10 @@ export class ImageStatus {
file: RemoteFile | BinaryFile;
caption?: string;
@ContactsProperty
contacts?: string[];
}
@ApiExtraModels(VoiceRemoteFile, VoiceBinaryFile)
export class VoiceStatus {
export class VoiceStatus extends StatusRequest {
@ApiProperty({
oneOf: [
{ $ref: getSchemaPath(VoiceRemoteFile) },
@@ -60,13 +63,10 @@ export class VoiceStatus {
file: VoiceRemoteFile | VoiceBinaryFile;
backgroundColor: string = '#38b42f';
@ContactsProperty
contacts?: string[];
}
@ApiExtraModels(VideoRemoteFile, VideoBinaryFile)
export class VideoStatus {
export class VideoStatus extends StatusRequest {
@ApiProperty({
oneOf: [
{ $ref: getSchemaPath(VideoRemoteFile) },
@@ -76,14 +76,11 @@ export class VideoStatus {
file: VideoRemoteFile | VideoBinaryFile;
caption?: string;
@ContactsProperty
contacts?: string[];
}
export class DeleteStatusRequest {
export class DeleteStatusRequest extends StatusRequest {
@ApiProperty({
description: 'status message id',
description: 'Status message id to delete',
example: 'AAAAAAAAAAAAAAAAA',
})
id: string;
+14 -4
View File
@@ -1,15 +1,21 @@
import { ApiProperty } from '@nestjs/swagger';
import { WAHAEvents } from '@waha/structures/enums.dto';
import {
AllEvents,
AllEventType,
WAHAEvents,
} from '@waha/structures/enums.dto';
import { Type } from 'class-transformer';
import {
IsArray,
IsEnum,
IsIn,
IsNumber,
IsOptional,
IsString,
IsUrl,
ValidateNested,
} from 'class-validator';
import { each } from 'lodash';
export enum RetryPolicy {
LINEAR = 'linear',
@@ -70,16 +76,20 @@ export class WebhookConfig {
description:
'You can use https://docs.webhook.site/ to test webhooks and see the payload',
})
@IsUrl({ require_protocol: true, require_tld: false })
@IsUrl({
protocols: ['http', 'https'],
require_protocol: true,
require_tld: false,
})
url: string;
@ApiProperty({
example: ['message', 'session.status'],
required: true,
})
@IsEnum(WAHAEvents, { each: true })
@IsIn(AllEvents, { each: true })
@IsArray()
events: WAHAEvents[];
events: AllEventType[];
@ApiProperty({
example: null,
+25
View File
@@ -0,0 +1,25 @@
import { WAHASessionStatus } from '../structures/enums.dto';
const STUCK_IN_STARTING_THRESHOLD = 20;
/**
* Keeps track of session statuses (with timestamps) for the last `trackPeriodMs` milliseconds.
*/
export class StatusTracker {
private numberOfStarting: number = 0;
public track(status: WAHASessionStatus): void {
if (status == WAHASessionStatus.STARTING) {
this.numberOfStarting += 1;
} else {
this.numberOfStarting = 0;
}
}
/**
* Checks if the session has been continuously 'STARTING'
*/
public isStuckInStarting(): boolean {
return this.numberOfStarting >= STUCK_IN_STARTING_THRESHOLD;
}
}
+3 -2
View File
@@ -33,8 +33,9 @@ export function getEngineName(): string {
}
export const VERSION: WAHAEnvironment = {
version: '2025.3.1',
version: '2025.3.2',
engine: getEngineName(),
tier: getWAHAVersion(),
browser: getBrowserExecutablePath(),
browser:
getEngineName() === WAHAEngine.WEBJS ? getBrowserExecutablePath() : null,
};
+2 -2
View File
@@ -2,11 +2,11 @@
"waha": {
"gows": {
"repo": "devlikeapro/gows",
"ref": "v1.0.1"
"ref": "v1.0.2"
},
"dashboard": {
"repo": "devlikeapro/dashboard",
"ref": "d7440caaefc2e80c0a4a1a61386f448c09cceeab"
"ref": "4dd30223fb0c5bb9bd8f168d0ff47633aa4a3047"
}
}
}
+2 -2
View File
@@ -7,7 +7,7 @@ __metadata:
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-channels-6712":
version: 6.7.12
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=c1b8ab22ad5e6bf5fa52026657ad1e0fd626e860"
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=a6b6fde036f35043326bea8cd7d46644f4222dd6"
dependencies:
"@adiwajshing/keyed-db": ^0.2.4
"@hapi/boom": ^9.1.3
@@ -40,7 +40,7 @@ __metadata:
optional: true
sharp:
optional: true
checksum: fc11e18ac05383cbbda0e6a5c9e605858da84b4d105d702e7560433bd3ca383ddead85ddc09d7918a73643854ec7703e9acd6dc11c322a4fc0c958924bba9e4b
checksum: f463ff68a64860ae729720c06d7759f76444dcecbfc81a24ce1d47b5988b68f9fb3ec5d266b7deed4239ac955a744301aeceb1797c2e452e31a2871ea973a361
languageName: node
linkType: hard