Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d745279fec | ||
|
|
df34c9f2fd | ||
|
|
37da9aabe3 | ||
|
|
3a2205147c | ||
|
|
5c6acaf10a | ||
|
|
d916f2dea5 | ||
|
|
254a1aedcb | ||
|
|
dce3175388 | ||
|
|
e2b6b45418 | ||
|
|
e210d1e9fc | ||
|
|
df0f449e13 | ||
|
|
895105c900 | ||
|
|
209538709b | ||
|
|
ef7c35286b | ||
|
|
70af0828cb | ||
|
|
ef18e311b1 | ||
|
|
68de84973a | ||
|
|
ef6d4f9998 | ||
|
|
9f5413963b | ||
|
|
ec7c0a5cb7 | ||
|
|
f82bd9eadf | ||
|
|
843c064ebe | ||
|
|
8a52e1d1d0 | ||
|
|
8ec220dc9a | ||
|
|
9c4b9f4433 | ||
|
|
6059a29660 | ||
|
|
e65e6764fa | ||
|
|
1e77cb234a | ||
|
|
4a357087d1 | ||
|
|
6a3823cdff | ||
|
|
c2372ce96e | ||
|
|
0f5e79b163 | ||
|
|
fa4026c83f | ||
|
|
3e4d66ca27 |
No files matched your search
@@ -18,10 +18,12 @@ import {
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
GetMessageQuery,
|
||||
MessageButtonReply,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageForwardRequest,
|
||||
MessageImageRequest,
|
||||
MessageLinkCustomPreviewRequest,
|
||||
MessageLinkPreviewRequest,
|
||||
MessageLocationRequest,
|
||||
MessagePollRequest,
|
||||
@@ -94,6 +96,25 @@ export class ChattingController {
|
||||
return whatsapp.sendVideo(request);
|
||||
}
|
||||
|
||||
@Post('/send/link-custom-preview')
|
||||
@ApiOperation({
|
||||
summary: 'Send a text message with a CUSTOM link preview.',
|
||||
description:
|
||||
'You can use regular /api/sendText if you wanna send auto-generated link preview.',
|
||||
})
|
||||
@UsePipes(new WAHAValidationPipe())
|
||||
async sendLinkCustomPreview(
|
||||
@Body() request: MessageLinkCustomPreviewRequest,
|
||||
): Promise<any> {
|
||||
const whatsapp = await this.manager.getWorkingSession(request.session);
|
||||
if (!request.text.includes(request.preview.url)) {
|
||||
throw new Error(
|
||||
'"text" must include the URL provided in the "preview.url"',
|
||||
);
|
||||
}
|
||||
return whatsapp.sendLinkCustomPreview(request);
|
||||
}
|
||||
|
||||
@Post('/sendButtons')
|
||||
@ApiOperation({
|
||||
summary: 'Send buttons (interactive message)',
|
||||
@@ -165,18 +186,22 @@ export class ChattingController {
|
||||
return whatsapp.sendLocation(request);
|
||||
}
|
||||
|
||||
@Post('/sendLinkPreview')
|
||||
async sendLinkPreview(@Body() request: MessageLinkPreviewRequest) {
|
||||
const whatsapp = await this.manager.getWorkingSession(request.session);
|
||||
return whatsapp.sendLinkPreview(request);
|
||||
}
|
||||
|
||||
@Post('/sendContactVcard')
|
||||
async sendContactVcard(@Body() request: MessageContactVcardRequest) {
|
||||
const whatsapp = await this.manager.getWorkingSession(request.session);
|
||||
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) {
|
||||
@@ -225,4 +250,11 @@ export class ChattingController {
|
||||
const whatsapp = await this.manager.getWorkingSession(request.session);
|
||||
return whatsapp.reply(request);
|
||||
}
|
||||
|
||||
@Post('/sendLinkPreview')
|
||||
@ApiOperation({ deprecated: true })
|
||||
async sendLinkPreview_DEPRECATED(@Body() request: MessageLinkPreviewRequest) {
|
||||
const whatsapp = await this.manager.getWorkingSession(request.session);
|
||||
return whatsapp.sendLinkPreview(request);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isJidBroadcast } from '@adiwajshing/baileys/lib/WABinary/jid-utils';
|
||||
import {
|
||||
Channel,
|
||||
ChannelListResult,
|
||||
@@ -46,10 +47,12 @@ import {
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
EditMessageRequest,
|
||||
MessageButtonReply,
|
||||
MessageContactVcardRequest,
|
||||
MessageFileRequest,
|
||||
MessageForwardRequest,
|
||||
MessageImageRequest,
|
||||
MessageLinkCustomPreviewRequest,
|
||||
MessageLinkPreviewRequest,
|
||||
MessageLocationRequest,
|
||||
MessagePollRequest,
|
||||
@@ -397,6 +400,10 @@ export abstract class WhatsappSession {
|
||||
/**
|
||||
* Other methods
|
||||
*/
|
||||
generateNewMessageId(): Promise<string> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
abstract checkNumberStatus(request: CheckNumberStatusQuery);
|
||||
|
||||
abstract sendText(request: MessageTextRequest);
|
||||
@@ -415,6 +422,12 @@ export abstract class WhatsappSession {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
sendLinkCustomPreview(
|
||||
request: MessageLinkCustomPreviewRequest,
|
||||
): Promise<any> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
abstract forwardMessage(request: MessageForwardRequest): Promise<WAMessage>;
|
||||
|
||||
abstract sendImage(request: MessageImageRequest);
|
||||
@@ -431,6 +444,10 @@ export abstract class WhatsappSession {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
sendButtonsReply(request: MessageButtonReply) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
abstract reply(request: MessageReplyRequest);
|
||||
|
||||
abstract sendSeen(chat: SendSeenRequest);
|
||||
@@ -588,7 +605,9 @@ export abstract class WhatsappSession {
|
||||
|
||||
protected async refreshProfilePicture(id: string) {
|
||||
let fn: Promise<string>;
|
||||
if (isNewsletter(id)) {
|
||||
if (isJidBroadcast(id)) {
|
||||
return null;
|
||||
} else if (isNewsletter(id)) {
|
||||
fn = this.channelsGetChannel(id).then(
|
||||
(channel: Channel) => channel.picture || channel.preview,
|
||||
);
|
||||
@@ -848,6 +867,9 @@ export abstract class WhatsappSession {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Engine methods
|
||||
*/
|
||||
public async getEngineInfo(): Promise<any> {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2535,6 +2535,142 @@ export namespace messages {
|
||||
return Media.deserialize(bytes);
|
||||
}
|
||||
}
|
||||
export class LinkPreview extends pb_1.Message {
|
||||
#one_of_decls: number[][] = [];
|
||||
constructor(data?: any[] | {
|
||||
url?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
image?: Uint8Array;
|
||||
}) {
|
||||
super();
|
||||
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls);
|
||||
if (!Array.isArray(data) && typeof data == "object") {
|
||||
if ("url" in data && data.url != undefined) {
|
||||
this.url = data.url;
|
||||
}
|
||||
if ("title" in data && data.title != undefined) {
|
||||
this.title = data.title;
|
||||
}
|
||||
if ("description" in data && data.description != undefined) {
|
||||
this.description = data.description;
|
||||
}
|
||||
if ("image" in data && data.image != undefined) {
|
||||
this.image = data.image;
|
||||
}
|
||||
}
|
||||
}
|
||||
get url() {
|
||||
return pb_1.Message.getFieldWithDefault(this, 1, "") as string;
|
||||
}
|
||||
set url(value: string) {
|
||||
pb_1.Message.setField(this, 1, value);
|
||||
}
|
||||
get title() {
|
||||
return pb_1.Message.getFieldWithDefault(this, 2, "") as string;
|
||||
}
|
||||
set title(value: string) {
|
||||
pb_1.Message.setField(this, 2, value);
|
||||
}
|
||||
get description() {
|
||||
return pb_1.Message.getFieldWithDefault(this, 3, "") as string;
|
||||
}
|
||||
set description(value: string) {
|
||||
pb_1.Message.setField(this, 3, value);
|
||||
}
|
||||
get image() {
|
||||
return pb_1.Message.getFieldWithDefault(this, 4, new Uint8Array(0)) as Uint8Array;
|
||||
}
|
||||
set image(value: Uint8Array) {
|
||||
pb_1.Message.setField(this, 4, value);
|
||||
}
|
||||
static fromObject(data: {
|
||||
url?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
image?: Uint8Array;
|
||||
}): LinkPreview {
|
||||
const message = new LinkPreview({});
|
||||
if (data.url != null) {
|
||||
message.url = data.url;
|
||||
}
|
||||
if (data.title != null) {
|
||||
message.title = data.title;
|
||||
}
|
||||
if (data.description != null) {
|
||||
message.description = data.description;
|
||||
}
|
||||
if (data.image != null) {
|
||||
message.image = data.image;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
toObject() {
|
||||
const data: {
|
||||
url?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
image?: Uint8Array;
|
||||
} = {};
|
||||
if (this.url != null) {
|
||||
data.url = this.url;
|
||||
}
|
||||
if (this.title != null) {
|
||||
data.title = this.title;
|
||||
}
|
||||
if (this.description != null) {
|
||||
data.description = this.description;
|
||||
}
|
||||
if (this.image != null) {
|
||||
data.image = this.image;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
serialize(): Uint8Array;
|
||||
serialize(w: pb_1.BinaryWriter): void;
|
||||
serialize(w?: pb_1.BinaryWriter): Uint8Array | void {
|
||||
const writer = w || new pb_1.BinaryWriter();
|
||||
if (this.url.length)
|
||||
writer.writeString(1, this.url);
|
||||
if (this.title.length)
|
||||
writer.writeString(2, this.title);
|
||||
if (this.description.length)
|
||||
writer.writeString(3, this.description);
|
||||
if (this.image.length)
|
||||
writer.writeBytes(4, this.image);
|
||||
if (!w)
|
||||
return writer.getResultBuffer();
|
||||
}
|
||||
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): LinkPreview {
|
||||
const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new LinkPreview();
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup())
|
||||
break;
|
||||
switch (reader.getFieldNumber()) {
|
||||
case 1:
|
||||
message.url = reader.readString();
|
||||
break;
|
||||
case 2:
|
||||
message.title = reader.readString();
|
||||
break;
|
||||
case 3:
|
||||
message.description = reader.readString();
|
||||
break;
|
||||
case 4:
|
||||
message.image = reader.readBytes();
|
||||
break;
|
||||
default: reader.skipField();
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
serializeBinary(): Uint8Array {
|
||||
return this.serialize();
|
||||
}
|
||||
static deserializeBinary(bytes: Uint8Array): LinkPreview {
|
||||
return LinkPreview.deserialize(bytes);
|
||||
}
|
||||
}
|
||||
export class MessageRequest extends pb_1.Message {
|
||||
#one_of_decls: number[][] = [];
|
||||
constructor(data?: any[] | {
|
||||
@@ -2547,9 +2683,12 @@ export namespace messages {
|
||||
linkPreview?: boolean;
|
||||
linkPreviewHighQuality?: boolean;
|
||||
replyTo?: string;
|
||||
id?: string;
|
||||
participants?: string[];
|
||||
preview?: LinkPreview;
|
||||
}) {
|
||||
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 +2717,15 @@ 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;
|
||||
}
|
||||
if ("preview" in data && data.preview != undefined) {
|
||||
this.preview = data.preview;
|
||||
}
|
||||
}
|
||||
}
|
||||
get session() {
|
||||
@@ -2646,6 +2794,27 @@ 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);
|
||||
}
|
||||
get preview() {
|
||||
return pb_1.Message.getWrapperField(this, LinkPreview, 12) as LinkPreview;
|
||||
}
|
||||
set preview(value: LinkPreview) {
|
||||
pb_1.Message.setWrapperField(this, 12, value);
|
||||
}
|
||||
get has_preview() {
|
||||
return pb_1.Message.getField(this, 12) != null;
|
||||
}
|
||||
static fromObject(data: {
|
||||
session?: ReturnType<typeof Session.prototype.toObject>;
|
||||
jid?: string;
|
||||
@@ -2656,6 +2825,9 @@ export namespace messages {
|
||||
linkPreview?: boolean;
|
||||
linkPreviewHighQuality?: boolean;
|
||||
replyTo?: string;
|
||||
id?: string;
|
||||
participants?: string[];
|
||||
preview?: ReturnType<typeof LinkPreview.prototype.toObject>;
|
||||
}): MessageRequest {
|
||||
const message = new MessageRequest({});
|
||||
if (data.session != null) {
|
||||
@@ -2685,6 +2857,15 @@ 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;
|
||||
}
|
||||
if (data.preview != null) {
|
||||
message.preview = LinkPreview.fromObject(data.preview);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
toObject() {
|
||||
@@ -2698,6 +2879,9 @@ export namespace messages {
|
||||
linkPreview?: boolean;
|
||||
linkPreviewHighQuality?: boolean;
|
||||
replyTo?: string;
|
||||
id?: string;
|
||||
participants?: string[];
|
||||
preview?: ReturnType<typeof LinkPreview.prototype.toObject>;
|
||||
} = {};
|
||||
if (this.session != null) {
|
||||
data.session = this.session.toObject();
|
||||
@@ -2726,6 +2910,15 @@ 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;
|
||||
}
|
||||
if (this.preview != null) {
|
||||
data.preview = this.preview.toObject();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
serialize(): Uint8Array;
|
||||
@@ -2750,6 +2943,12 @@ 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 (this.has_preview)
|
||||
writer.writeMessage(12, this.preview, () => this.preview.serialize(writer));
|
||||
if (!w)
|
||||
return writer.getResultBuffer();
|
||||
}
|
||||
@@ -2786,6 +2985,15 @@ 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;
|
||||
case 12:
|
||||
reader.readMessage(message.preview, () => message.preview = LinkPreview.deserialize(reader));
|
||||
break;
|
||||
default: reader.skipField();
|
||||
}
|
||||
}
|
||||
@@ -3076,6 +3284,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 +3511,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 +4417,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 +4434,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 +4466,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 +4492,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 +4503,7 @@ export namespace messages {
|
||||
jid?: string;
|
||||
sender?: string;
|
||||
messageId?: string;
|
||||
participants?: string[];
|
||||
} = {};
|
||||
if (this.session != null) {
|
||||
data.session = this.session.toObject();
|
||||
@@ -4065,6 +4517,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 +4534,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 +4557,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 +7742,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 +7796,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 +7961,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 +8071,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 +8089,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');
|
||||
@@ -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
|
||||
//
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
getContentType,
|
||||
getUrlFromDirectPath,
|
||||
isJidGroup,
|
||||
isJidStatusBroadcast,
|
||||
jidNormalizedUser,
|
||||
} from '@adiwajshing/baileys';
|
||||
import * as grpc from '@grpc/grpc-js';
|
||||
@@ -66,6 +66,7 @@ import {
|
||||
MessageFileRequest,
|
||||
MessageForwardRequest,
|
||||
MessageImageRequest,
|
||||
MessageLinkCustomPreviewRequest,
|
||||
MessageLocationRequest,
|
||||
MessageReactionRequest,
|
||||
MessageReplyRequest,
|
||||
@@ -128,6 +129,8 @@ import { promisify } from 'util';
|
||||
|
||||
import * as gows from './types';
|
||||
import MessageServiceClient = messages.MessageServiceClient;
|
||||
import { isJidBroadcast } from '@adiwajshing/baileys/lib/WABinary/jid-utils';
|
||||
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();
|
||||
@@ -630,11 +642,11 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
}
|
||||
|
||||
protected messageResponse(jid, data) {
|
||||
const message = parseJson(data.message);
|
||||
const message = data.message ? parseJson(data.message) : null;
|
||||
const id = buildMessageId({
|
||||
ID: data.id,
|
||||
IsFromMe: true,
|
||||
IsGroup: isJidGroup(jid) || isJidStatusBroadcast(jid),
|
||||
IsGroup: isJidGroup(jid) || isJidBroadcast(jid),
|
||||
Chat: jid,
|
||||
Sender: this.me.id,
|
||||
});
|
||||
@@ -680,6 +692,12 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
throw new AvailableInPlusVersion();
|
||||
}
|
||||
|
||||
sendLinkCustomPreview(
|
||||
request: MessageLinkCustomPreviewRequest,
|
||||
): Promise<any> {
|
||||
throw new AvailableInPlusVersion();
|
||||
}
|
||||
|
||||
reply(request: MessageReplyRequest) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
@@ -908,7 +926,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
jid: key.remoteJid,
|
||||
messageId: key.id,
|
||||
reaction: request.reaction,
|
||||
sender: key.fromMe ? this.me.id : key.participant,
|
||||
sender: key.fromMe ? this.me.id : key.participant || key.remoteJid,
|
||||
});
|
||||
const response = await promisify(this.client.SendReaction)(message);
|
||||
const data = response.toObject();
|
||||
@@ -1190,7 +1208,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 +1404,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 +1462,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
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import * as NodeCache from 'node-cache';
|
||||
|
||||
export class PresenceProxy {
|
||||
private cache: NodeCache;
|
||||
|
||||
public proxy: Record<string, any>;
|
||||
|
||||
constructor() {
|
||||
this.cache = new NodeCache({
|
||||
stdTTL: 60, // 1 minute
|
||||
checkperiod: 60, // 1 minute
|
||||
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;
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,6 @@ import makeWASocket, {
|
||||
getContentType,
|
||||
getKeyAuthor,
|
||||
isJidGroup,
|
||||
isJidNewsletter,
|
||||
isJidStatusBroadcast,
|
||||
isJidUser,
|
||||
isRealMessage,
|
||||
jidNormalizedUser,
|
||||
@@ -38,7 +36,10 @@ import {
|
||||
LabelAssociationType,
|
||||
} from '@adiwajshing/baileys/lib/Types/LabelAssociation';
|
||||
import { MessageUserReceiptUpdate } from '@adiwajshing/baileys/lib/Types/Message';
|
||||
import { isLidUser } from '@adiwajshing/baileys/lib/WABinary/jid-utils';
|
||||
import {
|
||||
isJidBroadcast,
|
||||
isLidUser,
|
||||
} from '@adiwajshing/baileys/lib/WABinary/jid-utils';
|
||||
import { Logger as BaileysLogger } from '@adiwajshing/baileys/node_modules/pino';
|
||||
import { UnprocessableEntityException } from '@nestjs/common';
|
||||
import {
|
||||
@@ -96,6 +97,7 @@ import {
|
||||
MessageFileRequest,
|
||||
MessageForwardRequest,
|
||||
MessageImageRequest,
|
||||
MessageLinkCustomPreviewRequest,
|
||||
MessageLinkPreviewRequest,
|
||||
MessageLocationRequest,
|
||||
MessagePollRequest,
|
||||
@@ -140,6 +142,7 @@ import { MeInfo } from '@waha/structures/sessions.dto';
|
||||
import {
|
||||
BROADCAST_ID,
|
||||
DeleteStatusRequest,
|
||||
StatusRequest,
|
||||
TextStatus,
|
||||
} from '@waha/structures/status.dto';
|
||||
import {
|
||||
@@ -154,6 +157,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 +174,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 +222,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 +262,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 +436,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.startDelayedJob.schedule(async () => {
|
||||
if (!this.shouldRestart) {
|
||||
this.logger.warn(
|
||||
@@ -443,12 +461,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 +498,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 +735,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 = {
|
||||
@@ -777,6 +825,12 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
throw new AvailableInPlusVersion();
|
||||
}
|
||||
|
||||
sendLinkCustomPreview(
|
||||
request: MessageLinkCustomPreviewRequest,
|
||||
): Promise<any> {
|
||||
throw new AvailableInPlusVersion();
|
||||
}
|
||||
|
||||
protected async uploadMedia(
|
||||
file: RemoteFile | BinaryFile,
|
||||
type,
|
||||
@@ -924,13 +978,34 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
|
||||
async setReaction(request: MessageReactionRequest) {
|
||||
const key = parseMessageIdSerialized(request.messageId);
|
||||
const reactionMessage = {
|
||||
react: {
|
||||
text: request.reaction,
|
||||
key: key,
|
||||
},
|
||||
};
|
||||
return this.sock.sendMessage(key.remoteJid, reactionMessage);
|
||||
if (isNewsletter(key.remoteJid)) {
|
||||
let serverId = Number(key.id);
|
||||
if (!serverId) {
|
||||
const msg = await this.store.getMessageById(key.remoteJid, key.id);
|
||||
if (msg) {
|
||||
// @ts-ignore
|
||||
serverId = Number(msg.key.server_id);
|
||||
}
|
||||
}
|
||||
if (!serverId) {
|
||||
throw new UnprocessableEntityException(
|
||||
`Unable to get server id for channel message '${key.id}'`,
|
||||
);
|
||||
}
|
||||
return this.sock.newsletterReactMessage(
|
||||
key.remoteJid,
|
||||
serverId.toString(),
|
||||
request.reaction,
|
||||
);
|
||||
} else {
|
||||
const reactionMessage = {
|
||||
react: {
|
||||
text: request.reaction,
|
||||
key: key,
|
||||
},
|
||||
};
|
||||
return this.sock.sendMessage(key.remoteJid, reactionMessage);
|
||||
}
|
||||
}
|
||||
|
||||
async setStar(request: MessageStarRequest) {
|
||||
@@ -1150,12 +1225,6 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
|
||||
public async fetchContactProfilePicture(id: string) {
|
||||
const contact = this.ensureSuffix(id);
|
||||
if (isJidNewsletter(id)) {
|
||||
return null;
|
||||
}
|
||||
if (isJidStatusBroadcast(id)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const url = await this.sock.profilePictureUrl(contact, 'image');
|
||||
return url;
|
||||
@@ -1314,7 +1383,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 +1399,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 +1503,6 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
} else {
|
||||
jids = await this.fetchMyContactsJids();
|
||||
}
|
||||
this.upsertMeInJIDs(jids);
|
||||
return jids;
|
||||
}
|
||||
|
||||
@@ -1370,12 +1518,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 +1538,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1811,6 +1966,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
proto.Message.ProtocolMessage.Type.EPHEMERAL_SYNC_RESPONSE
|
||||
)
|
||||
return;
|
||||
// Ignore key distribution messages
|
||||
if (message.message.senderKeyDistributionMessage) return;
|
||||
|
||||
if (downloadMedia) {
|
||||
try {
|
||||
@@ -1908,6 +2065,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
id: contextInfo.stanzaId,
|
||||
participant: toCusFormat(contextInfo.participant),
|
||||
body: body,
|
||||
_data: quotedMessage,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2202,7 +2360,7 @@ export function toCusFormat(remoteJid) {
|
||||
if (isJidGroup(remoteJid)) {
|
||||
return remoteJid;
|
||||
}
|
||||
if (isJidStatusBroadcast(remoteJid)) {
|
||||
if (isJidBroadcast(remoteJid)) {
|
||||
return remoteJid;
|
||||
}
|
||||
if (isLidUser(remoteJid)) {
|
||||
@@ -2233,12 +2391,15 @@ export function toJID(chatId) {
|
||||
if (isJidGroup(chatId)) {
|
||||
return chatId;
|
||||
}
|
||||
if (isJidStatusBroadcast(chatId)) {
|
||||
if (isJidBroadcast(chatId)) {
|
||||
return chatId;
|
||||
}
|
||||
if (isNewsletter(chatId)) {
|
||||
return chatId;
|
||||
}
|
||||
if (isLidUser(chatId)) {
|
||||
return chatId;
|
||||
}
|
||||
const number = chatId.split('@')[0];
|
||||
return number + '@s.whatsapp.net';
|
||||
}
|
||||
|
||||
@@ -26,6 +26,10 @@ export class NowebInMemoryStore implements INowebStore {
|
||||
|
||||
constructor() {
|
||||
this.store = makeInMemoryStore({ logger: logger });
|
||||
const presences = {};
|
||||
this.store.presences = presences;
|
||||
// Adjust inline even handler
|
||||
this.store.setPresences(presences);
|
||||
}
|
||||
|
||||
init(): Promise<void> {
|
||||
|
||||
@@ -363,7 +363,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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import {
|
||||
registerDecorator,
|
||||
ValidationArguments,
|
||||
ValidationOptions,
|
||||
} from 'class-validator';
|
||||
|
||||
export function IsFileType(validationOptions?: ValidationOptions) {
|
||||
return function (object: any, propertyName: string) {
|
||||
registerDecorator({
|
||||
name: 'isFileType',
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
options: validationOptions,
|
||||
validator: {
|
||||
validate(value: any, _args: ValidationArguments) {
|
||||
return (
|
||||
value &&
|
||||
typeof value === 'object' &&
|
||||
(typeof value.data === 'string' || typeof value.url === 'string')
|
||||
);
|
||||
},
|
||||
defaultMessage() {
|
||||
return 'must contain either "data" or "url".';
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -16,6 +16,7 @@ export class SessionQuery {
|
||||
*/
|
||||
|
||||
export class SessionBaseRequest {
|
||||
@IsString()
|
||||
session: string = WHATSAPP_DEFAULT_SESSION_NAME;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,17 @@ import {
|
||||
ApiProperty,
|
||||
getSchemaPath,
|
||||
} from '@nestjs/swagger';
|
||||
import { IsFileType } from '@waha/nestjs/validation/IsFileType';
|
||||
import { GetChatMessagesQuery } from '@waha/structures/chats.dto';
|
||||
import { IsNotEmpty, IsNumber, IsString } from 'class-validator';
|
||||
import { plainToInstance, Transform, Type } from 'class-transformer';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUrl,
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
|
||||
import {
|
||||
SessionBaseRequest,
|
||||
@@ -14,6 +23,9 @@ import {
|
||||
} from './base.dto';
|
||||
import {
|
||||
BinaryFile,
|
||||
FileContent,
|
||||
FileType,
|
||||
FileURL,
|
||||
RemoteFile,
|
||||
VideoBinaryFile,
|
||||
VideoRemoteFile,
|
||||
@@ -64,6 +76,7 @@ export class GetPresenceQuery extends ChatQuery {}
|
||||
*/
|
||||
export class ChatRequest extends SessionBaseRequest {
|
||||
@ChatIdProperty()
|
||||
@IsString()
|
||||
chatId: string;
|
||||
}
|
||||
|
||||
@@ -161,6 +174,73 @@ export class MessageTextRequest extends ChatRequest {
|
||||
linkPreviewHighQuality?: boolean = false;
|
||||
}
|
||||
|
||||
@ApiExtraModels(FileURL, FileContent)
|
||||
export class LinkPreviewData {
|
||||
@IsNotEmpty()
|
||||
@IsUrl({
|
||||
protocols: ['http', 'https'],
|
||||
require_protocol: true,
|
||||
require_tld: false,
|
||||
})
|
||||
url: string = 'https://github.com/';
|
||||
|
||||
@IsString()
|
||||
title: string = 'Your Title';
|
||||
|
||||
@IsString()
|
||||
description: string = 'Check this out, amazing!';
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Transform(
|
||||
({ value }) => {
|
||||
if (value?.url) {
|
||||
return plainToInstance(FileURL, value);
|
||||
}
|
||||
if (value?.data) {
|
||||
return plainToInstance(FileContent, value);
|
||||
}
|
||||
return value;
|
||||
},
|
||||
{ toClassOnly: true },
|
||||
)
|
||||
@IsFileType({ message: 'Image must contain either "data" or "url".' })
|
||||
@ApiProperty({
|
||||
oneOf: [
|
||||
{ $ref: getSchemaPath(FileURL) },
|
||||
{ $ref: getSchemaPath(FileContent) },
|
||||
],
|
||||
example: {
|
||||
url:
|
||||
process.env.WHATSAPP_SWAGGER_JPG_EXAMPLE_URL ||
|
||||
'https://github.com/devlikeapro/waha/raw/core/examples/waha.jpg',
|
||||
},
|
||||
})
|
||||
image?: FileType;
|
||||
}
|
||||
|
||||
export class MessageLinkCustomPreviewRequest extends ChatRequest {
|
||||
@IsString()
|
||||
@ApiProperty({
|
||||
description:
|
||||
'The text to send. MUST include the URL provided in preview.url',
|
||||
})
|
||||
text: string = 'Check this out! https://github.com/';
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
linkPreviewHighQuality?: boolean = true;
|
||||
|
||||
@ValidateNested()
|
||||
@Type(() => LinkPreviewData)
|
||||
preview: LinkPreviewData;
|
||||
|
||||
@ReplyToProperty()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
reply_to?: string;
|
||||
}
|
||||
|
||||
export class EditMessageRequest {
|
||||
text: string = 'Hello, world!';
|
||||
|
||||
@@ -333,3 +413,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;
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsBase64, IsNotEmpty, IsString, IsUrl } from 'class-validator';
|
||||
|
||||
export class Base64File {
|
||||
mimetype: string;
|
||||
@@ -117,3 +118,33 @@ export class VideoRemoteFile {
|
||||
})
|
||||
url: string;
|
||||
}
|
||||
|
||||
export class FileURL {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@IsUrl({
|
||||
protocols: ['http', 'https'],
|
||||
require_protocol: true,
|
||||
require_tld: false,
|
||||
})
|
||||
@ApiProperty({
|
||||
example:
|
||||
process.env.WHATSAPP_SWAGGER_JPG_EXAMPLE_URL ||
|
||||
'https://github.com/devlikeapro/waha/raw/core/examples/waha.jpg',
|
||||
})
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export class FileContent {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@IsBase64()
|
||||
@ApiProperty({
|
||||
description: 'Base64-encoded data of the file',
|
||||
example:
|
||||
'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
Line truncated
|
||||
})
|
||||
data?: string;
|
||||
}
|
||||
|
||||
export type FileType = FileURL | FileContent;
|
||||
@@ -15,4 +15,9 @@ export class ReplyToMessage {
|
||||
example: 'Hello!',
|
||||
})
|
||||
body?: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Raw data from reply's message",
|
||||
})
|
||||
_data?: any;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -33,8 +33,9 @@ export function getEngineName(): string {
|
||||
}
|
||||
|
||||
export const VERSION: WAHAEnvironment = {
|
||||
version: '2025.3.1',
|
||||
version: '2025.4.1',
|
||||
engine: getEngineName(),
|
||||
tier: getWAHAVersion(),
|
||||
browser: getBrowserExecutablePath(),
|
||||
browser:
|
||||
getEngineName() === WAHAEngine.WEBJS ? getBrowserExecutablePath() : null,
|
||||
};
|
||||
+2
-2
@@ -2,11 +2,11 @@
|
||||
"waha": {
|
||||
"gows": {
|
||||
"repo": "devlikeapro/gows",
|
||||
"ref": "v1.0.1"
|
||||
"ref": "v1.0.3"
|
||||
},
|
||||
"dashboard": {
|
||||
"repo": "devlikeapro/dashboard",
|
||||
"ref": "d7440caaefc2e80c0a4a1a61386f448c09cceeab"
|
||||
"ref": "0e7e2e7edc715b0c30fff89c10ee2d2fc3402d2d"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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=4e51470a1e1f6d56b3e48feabf886b80c0a75bd0"
|
||||
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: d2d5fe4f4ed2a519fd30c486b079978fac1894b4ad195d337387acc841110f663e4525b89336f8dcccc63e6e7b54f4542e00006e7d42ee196dbce7b7d790b7fc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in new issue
Block a user