1 parent
fd0438f80b
commit
52c5d85487
7 files changed
+152
-39
No files matched your search
@@ -18,7 +18,7 @@ import { SendButtonsRequest } from '@waha/structures/chatting.buttons.dto';
|
||||
import { BinaryFile, RemoteFile } from '@waha/structures/files.dto';
|
||||
import { Label, LabelDTO, LabelID } from '@waha/structures/labels.dto';
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
import { WAMessage } from '@waha/structures/responses.dto';
|
||||
import { MessageSource, WAMessage } from '@waha/structures/responses.dto';
|
||||
import { DefaultMap } from '@waha/utils/DefaultMap';
|
||||
import { generatePrefixedId } from '@waha/utils/ids';
|
||||
import { LoggerBuilder } from '@waha/utils/logging';
|
||||
@@ -26,7 +26,6 @@ import { complete } from '@waha/utils/reactive/complete';
|
||||
import { SwitchObservable } from '@waha/utils/reactive/SwitchObservable';
|
||||
import * as fs from 'fs';
|
||||
import * as lodash from 'lodash';
|
||||
import { PinoLogger } from 'nestjs-pino';
|
||||
import * as NodeCache from 'node-cache';
|
||||
import { Logger } from 'pino';
|
||||
import {
|
||||
@@ -73,7 +72,6 @@ import {
|
||||
CreateGroupRequest,
|
||||
GroupField,
|
||||
GroupsListFields,
|
||||
GroupsPaginationParams,
|
||||
ParticipantsRequest,
|
||||
SettingsSecurityChangeInfo,
|
||||
} from '../../structures/groups.dto';
|
||||
@@ -148,6 +146,12 @@ export abstract class WhatsappSession {
|
||||
stdTTL: 24 * 60 * 60, // 1 day
|
||||
});
|
||||
|
||||
// Save sent messages ids in cache so we can determine if a message was sent
|
||||
// via API or APP
|
||||
private sentMessageIds: NodeCache = new NodeCache({
|
||||
stdTTL: 10 * 60, // 10 minutes
|
||||
});
|
||||
|
||||
public constructor({
|
||||
name,
|
||||
printQR,
|
||||
@@ -885,6 +889,18 @@ export abstract class WhatsappSession {
|
||||
);
|
||||
qrcode.generate(qr.raw, { small: true });
|
||||
}
|
||||
|
||||
protected saveSentMessageId(id: string) {
|
||||
this.sentMessageIds.set(id, true);
|
||||
}
|
||||
|
||||
protected getMessageSource(id: string): MessageSource {
|
||||
if (!id) {
|
||||
return MessageSource.APP;
|
||||
}
|
||||
const api = this.sentMessageIds.has(id);
|
||||
return api ? MessageSource.API : MessageSource.APP;
|
||||
}
|
||||
}
|
||||
|
||||
export function isNewsletter(jid: string) {
|
||||
@@ -916,3 +932,19 @@ export function parseChannelInviteLink(link: string): string {
|
||||
export function getPublicUrlFromDirectPath(directPath: string) {
|
||||
return `https://pps.whatsapp.net${directPath}`;
|
||||
}
|
||||
|
||||
const deviceRegexp = /^.*:(\d+)@.*$/;
|
||||
|
||||
/**
|
||||
* Extracts the device ID from a JID string.
|
||||
*
|
||||
* @param jid - The JID string (e.g., "123123:12@c.us")
|
||||
* @return The extracted device ID (e.g., "12") or null if the format is invalid.
|
||||
*/
|
||||
export function extractDeviceId(jid: string): string | null {
|
||||
if (!jid) {
|
||||
return null;
|
||||
}
|
||||
const match = jid.match(deviceRegexp);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
@@ -8,19 +8,33 @@ import * as grpc from '@grpc/grpc-js';
|
||||
import { connectivityState } from '@grpc/grpc-js';
|
||||
import { UnprocessableEntityException } from '@nestjs/common';
|
||||
import {
|
||||
extractDeviceId,
|
||||
getChannelInviteLink,
|
||||
WhatsappSession,
|
||||
} from '@waha/core/abc/session.abc';
|
||||
import { Jid } from '@waha/core/engines/const';
|
||||
import { EventsFromObservable } from '@waha/core/engines/gows/EventsFromObservable';
|
||||
import { GowsEventStreamObservable } from '@waha/core/engines/gows/GowsEventStreamObservable';
|
||||
import {
|
||||
ToGroupV2JoinEvent,
|
||||
ToGroupV2LeaveEvent,
|
||||
ToGroupV2ParticipantsEvents,
|
||||
ToGroupV2UpdateEvent,
|
||||
} from '@waha/core/engines/gows/groups.gows';
|
||||
import { messages } from '@waha/core/engines/gows/grpc/gows';
|
||||
import {
|
||||
optional,
|
||||
parseJson,
|
||||
parseJsonList,
|
||||
statusToAck,
|
||||
} from '@waha/core/engines/gows/helpers';
|
||||
import { GowsAuthFactoryCore } from '@waha/core/engines/gows/store/GowsAuthFactoryCore';
|
||||
import {
|
||||
parseMessageIdSerialized,
|
||||
toCusFormat,
|
||||
toJID,
|
||||
} from '@waha/core/engines/noweb/session.noweb.core';
|
||||
import { extractMediaContent } from '@waha/core/engines/noweb/utils';
|
||||
import {
|
||||
AvailableInPlusVersion,
|
||||
NotImplementedByEngineError,
|
||||
@@ -38,6 +52,13 @@ import {
|
||||
ListChannelsQuery,
|
||||
PreviewChannelMessages,
|
||||
} from '@waha/structures/channels.dto';
|
||||
import {
|
||||
ChatSortField,
|
||||
ChatSummary,
|
||||
GetChatMessageQuery,
|
||||
GetChatMessagesFilter,
|
||||
GetChatMessagesQuery,
|
||||
} from '@waha/structures/chats.dto';
|
||||
import {
|
||||
ChatRequest,
|
||||
CheckNumberStatusQuery,
|
||||
@@ -53,6 +74,7 @@ import {
|
||||
SendSeenRequest,
|
||||
WANumberExistResult,
|
||||
} from '@waha/structures/chatting.dto';
|
||||
import { ContactQuery } from '@waha/structures/contacts.dto';
|
||||
import {
|
||||
ACK_UNKNOWN,
|
||||
WAHAEvents,
|
||||
@@ -60,11 +82,24 @@ import {
|
||||
WAHASessionStatus,
|
||||
WAMessageAck,
|
||||
} from '@waha/structures/enums.dto';
|
||||
import { BinaryFile, RemoteFile } from '@waha/structures/files.dto';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
GroupSortField,
|
||||
Participant,
|
||||
ParticipantsRequest,
|
||||
SettingsSecurityChangeInfo,
|
||||
} from '@waha/structures/groups.dto';
|
||||
import { PaginationParams, SortOrder } from '@waha/structures/pagination.dto';
|
||||
import {
|
||||
WAHAChatPresences,
|
||||
WAHAPresenceData,
|
||||
} from '@waha/structures/presence.dto';
|
||||
import { WAMessage, WAMessageReaction } from '@waha/structures/responses.dto';
|
||||
import {
|
||||
MessageSource,
|
||||
WAMessage,
|
||||
WAMessageReaction,
|
||||
} from '@waha/structures/responses.dto';
|
||||
import { MeInfo, ProxyConfig } from '@waha/structures/sessions.dto';
|
||||
import {
|
||||
BROADCAST_ID,
|
||||
@@ -73,6 +108,7 @@ import {
|
||||
TextStatus,
|
||||
} from '@waha/structures/status.dto';
|
||||
import { EnginePayload, WAMessageAckBody } from '@waha/structures/webhooks.dto';
|
||||
import { PaginatorInMemory } from '@waha/utils/Paginator';
|
||||
import { sleep, waitUntil } from '@waha/utils/promiseTimeout';
|
||||
import { onlyEvent } from '@waha/utils/reactive/ops/onlyEvent';
|
||||
import * as NodeCache from 'node-cache';
|
||||
@@ -92,37 +128,6 @@ import { promisify } from 'util';
|
||||
|
||||
import * as gows from './types';
|
||||
import MessageServiceClient = messages.MessageServiceClient;
|
||||
import {
|
||||
ToGroupV2JoinEvent,
|
||||
ToGroupV2LeaveEvent,
|
||||
ToGroupV2ParticipantsEvents,
|
||||
ToGroupV2UpdateEvent,
|
||||
} from '@waha/core/engines/gows/groups.gows';
|
||||
import {
|
||||
optional,
|
||||
parseJson,
|
||||
parseJsonList,
|
||||
statusToAck,
|
||||
} from '@waha/core/engines/gows/helpers';
|
||||
import { extractMediaContent } from '@waha/core/engines/noweb/utils';
|
||||
import {
|
||||
ChatSortField,
|
||||
ChatSummary,
|
||||
GetChatMessageQuery,
|
||||
GetChatMessagesFilter,
|
||||
GetChatMessagesQuery,
|
||||
} from '@waha/structures/chats.dto';
|
||||
import { ContactQuery } from '@waha/structures/contacts.dto';
|
||||
import { BinaryFile, RemoteFile } from '@waha/structures/files.dto';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
GroupSortField,
|
||||
Participant,
|
||||
ParticipantsRequest,
|
||||
SettingsSecurityChangeInfo,
|
||||
} from '@waha/structures/groups.dto';
|
||||
import { PaginationParams, SortOrder } from '@waha/structures/pagination.dto';
|
||||
import { PaginatorInMemory } from '@waha/utils/Paginator';
|
||||
|
||||
enum WhatsMeowEvent {
|
||||
CONNECTED = 'gows.ConnectedEventData',
|
||||
@@ -256,6 +261,8 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
id: toCusFormat(jidNormalizedUser(data.ID)),
|
||||
pushName: data.PushName,
|
||||
};
|
||||
// @ts-ignore
|
||||
this.me.jid = data.ID;
|
||||
});
|
||||
|
||||
events.on(WhatsMeowEvent.DISCONNECTED, () => {
|
||||
@@ -1378,7 +1385,6 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
const id = buildMessageId(message);
|
||||
const body = this.extractBody(message.Message);
|
||||
const replyTo = null; // TODO: this.extractReplyTo(message.message);
|
||||
|
||||
let ack;
|
||||
if (message.Status) {
|
||||
ack = statusToAck(message.Status);
|
||||
@@ -1386,12 +1392,14 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
ack = message.Info.IsFromMe ? WAMessageAck.SERVER : WAMessageAck.DEVICE;
|
||||
}
|
||||
const mediaContent = extractMediaContent(message.Message);
|
||||
const source = this.getSourceDeviceByMsg(message);
|
||||
|
||||
return {
|
||||
id: id,
|
||||
timestamp: parseTimestampToSeconds(message.Info.Timestamp),
|
||||
from: toCusFormat(fromToParticipant.from),
|
||||
fromMe: message.Info.IsFromMe,
|
||||
source: source,
|
||||
body: body,
|
||||
to: toCusFormat(fromToParticipant.to),
|
||||
participant: toCusFormat(fromToParticipant.participant),
|
||||
@@ -1408,6 +1416,17 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
};
|
||||
}
|
||||
|
||||
private getSourceDeviceByMsg(message): MessageSource {
|
||||
if (!message.Info.IsFromMe) {
|
||||
return MessageSource.APP;
|
||||
}
|
||||
// @ts-ignore
|
||||
const myJid = this.me.jid;
|
||||
const myDeviceId = extractDeviceId(myJid);
|
||||
const sentDeviceId = extractDeviceId(message.Info.Sender);
|
||||
return sentDeviceId === myDeviceId ? MessageSource.API : MessageSource.APP;
|
||||
}
|
||||
|
||||
private extractBody(message) {
|
||||
if (!message) {
|
||||
return null;
|
||||
@@ -1514,11 +1533,13 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
const fromToParticipant = getFromToParticipant(message);
|
||||
const reactionMessage = message.Message.reactionMessage;
|
||||
const messageId = this.buildMessageIdFromKey(reactionMessage.key);
|
||||
const source = this.getSourceDeviceByMsg(message);
|
||||
const reaction: WAMessageReaction = {
|
||||
id: id,
|
||||
timestamp: parseTimestampToSeconds(message.Info.Timestamp),
|
||||
from: toCusFormat(fromToParticipant.from),
|
||||
fromMe: message.Info.IsFromMe,
|
||||
source: source,
|
||||
to: toCusFormat(fromToParticipant.to),
|
||||
participant: toCusFormat(fromToParticipant.participant),
|
||||
reaction: {
|
||||
|
||||
@@ -5,6 +5,7 @@ import makeWASocket, {
|
||||
DisconnectReason,
|
||||
downloadMediaMessage,
|
||||
extractMessageContent,
|
||||
generateMessageIDV2,
|
||||
getAggregateVotesInPollMessage,
|
||||
getContentType,
|
||||
getKeyAuthor,
|
||||
@@ -151,6 +152,7 @@ import {
|
||||
WAHAPresenceData,
|
||||
} from '../../../structures/presence.dto';
|
||||
import {
|
||||
MessageSource,
|
||||
WAMessage,
|
||||
WAMessageReaction,
|
||||
} from '../../../structures/responses.dto';
|
||||
@@ -1765,11 +1767,13 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
const fromToParticipant = getFromToParticipant(message);
|
||||
const reactionMessage = message.message.reactionMessage;
|
||||
const messageId = buildMessageId(reactionMessage.key);
|
||||
const source = this.getMessageSource(message.key.id);
|
||||
const reaction: WAMessageReaction = {
|
||||
id: id,
|
||||
timestamp: ensureNumber(message.messageTimestamp),
|
||||
from: toCusFormat(fromToParticipant.from),
|
||||
fromMe: message.key.fromMe,
|
||||
source: source,
|
||||
to: toCusFormat(fromToParticipant.to),
|
||||
participant: toCusFormat(fromToParticipant.participant),
|
||||
reaction: {
|
||||
@@ -1828,11 +1832,13 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
const replyTo = this.extractReplyTo(message.message);
|
||||
const ack = message.ack || message.status - 1;
|
||||
const mediaContent = extractMediaContent(message.message);
|
||||
const source = this.getMessageSource(message.key.id);
|
||||
return Promise.resolve({
|
||||
id: id,
|
||||
timestamp: ensureNumber(message.messageTimestamp),
|
||||
from: toCusFormat(fromToParticipant.from),
|
||||
fromMe: message.key.fromMe,
|
||||
source: source,
|
||||
body: body || null,
|
||||
to: toCusFormat(fromToParticipant.to),
|
||||
participant: toCusFormat(fromToParticipant.participant),
|
||||
@@ -2074,9 +2080,11 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
quoted = await this.store.loadMessage(jid, key.id);
|
||||
}
|
||||
const chat = await this.store.getChat(jid);
|
||||
const messageId = this.generateMessageID();
|
||||
return {
|
||||
quoted: quoted,
|
||||
ephemeralExpiration: chat?.ephemeralExpiration,
|
||||
messageId: messageId,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2095,6 +2103,12 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
}
|
||||
return linkPreview;
|
||||
}
|
||||
|
||||
protected generateMessageID() {
|
||||
const id = generateMessageIDV2(this.sock.user?.id);
|
||||
this.saveSentMessageId(id);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
function hasPath(url: string) {
|
||||
|
||||
@@ -2,9 +2,11 @@ import { GetChatMessagesFilter } from '@waha/structures/chats.dto';
|
||||
import { Label } from '@waha/structures/labels.dto';
|
||||
import { PaginationParams } from '@waha/structures/pagination.dto';
|
||||
import { TextStatus } from '@waha/structures/status.dto';
|
||||
import { EventEmitter } from 'events';
|
||||
import * as lodash from 'lodash';
|
||||
import { Client, Events } from 'whatsapp-web.js';
|
||||
import { Message } from 'whatsapp-web.js/src/structures';
|
||||
import { exposeFunctionIfAbsent } from 'whatsapp-web.js/src/util/Puppeter';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { LoadWAHA } = require('./_WAHA.js');
|
||||
@@ -19,10 +21,13 @@ const { LoadPaginator } = require('./_Paginator.js');
|
||||
const ChatFactory = require('whatsapp-web.js/src/factories/ChatFactory');
|
||||
|
||||
export class WebjsClientCore extends Client {
|
||||
public events = new EventEmitter();
|
||||
|
||||
constructor(options) {
|
||||
super(options);
|
||||
// Wait until it's READY and inject more utils
|
||||
this.on(Events.READY, async () => {
|
||||
await this.attachCustomEventListeners();
|
||||
await this.injectWaha();
|
||||
});
|
||||
}
|
||||
@@ -33,6 +38,22 @@ export class WebjsClientCore extends Client {
|
||||
await this.pupPage.evaluate(LoadWAHA);
|
||||
}
|
||||
|
||||
async attachCustomEventListeners() {
|
||||
await exposeFunctionIfAbsent(
|
||||
this.pupPage,
|
||||
'onNewMessageId',
|
||||
(messageId: string) => {
|
||||
this.events.emit('message.id', { id: messageId });
|
||||
return;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async destroy() {
|
||||
this.events.removeAllListeners();
|
||||
await super.destroy();
|
||||
}
|
||||
|
||||
async setPushName(name: string) {
|
||||
await this.pupPage.evaluate(async (pushName) => {
|
||||
return await window['WAHA'].WAWebSetPushnameConnAction.setPushname(
|
||||
|
||||
@@ -73,7 +73,11 @@ import {
|
||||
import { Label, LabelDTO, LabelID } from '@waha/structures/labels.dto';
|
||||
import { ReplyToMessage } from '@waha/structures/message.dto';
|
||||
import { PaginationParams, SortOrder } from '@waha/structures/pagination.dto';
|
||||
import { WAMessage, WAMessageReaction } from '@waha/structures/responses.dto';
|
||||
import {
|
||||
MessageSource,
|
||||
WAMessage,
|
||||
WAMessageReaction,
|
||||
} from '@waha/structures/responses.dto';
|
||||
import { MeInfo } from '@waha/structures/sessions.dto';
|
||||
import { StatusRequest, TextStatus } from '@waha/structures/status.dto';
|
||||
import {
|
||||
@@ -1154,6 +1158,11 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
* END - Methods for API
|
||||
*/
|
||||
subscribeEngineEvents2() {
|
||||
// Save sent message in cache
|
||||
this.whatsapp.events.on('message.id', (data) => {
|
||||
this.saveSentMessageId(data.id);
|
||||
});
|
||||
|
||||
//
|
||||
// All
|
||||
//
|
||||
@@ -1353,10 +1362,12 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
}
|
||||
|
||||
private processMessageReaction(reaction: Reaction): WAMessageReaction {
|
||||
const source = this.getMessageSource(reaction.id.id);
|
||||
return {
|
||||
id: reaction.id._serialized,
|
||||
from: reaction.senderId,
|
||||
fromMe: reaction.id.fromMe,
|
||||
source: source,
|
||||
participant: reaction.senderId,
|
||||
to: reaction.id.remote,
|
||||
timestamp: reaction.timestamp,
|
||||
@@ -1369,12 +1380,14 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
|
||||
protected toWAMessage(message: Message): WAMessage {
|
||||
const replyTo = this.extractReplyTo(message);
|
||||
const source = this.getMessageSource(message.id.id);
|
||||
// @ts-ignore
|
||||
return {
|
||||
id: message.id._serialized,
|
||||
timestamp: message.timestamp,
|
||||
from: message.from,
|
||||
fromMe: message.fromMe,
|
||||
source: source,
|
||||
to: message.to,
|
||||
body: message.body,
|
||||
// Media
|
||||
|
||||
@@ -11,6 +11,11 @@ export class WALocation {
|
||||
longitude: string;
|
||||
}
|
||||
|
||||
export enum MessageSource {
|
||||
API = 'api',
|
||||
APP = 'app',
|
||||
}
|
||||
|
||||
class WAMessageBase {
|
||||
@MessageIdProperty()
|
||||
id: string;
|
||||
@@ -33,6 +38,13 @@ class WAMessageBase {
|
||||
})
|
||||
fromMe: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description:
|
||||
'The device that sent the message - either API or APP. Available in events (webhooks/websockets) only and only "fromMe: true" messages.',
|
||||
example: MessageSource.API,
|
||||
})
|
||||
source: MessageSource;
|
||||
|
||||
@ChatIdProperty({
|
||||
description: `
|
||||
* ID for who this message is for.
|
||||
|
||||
@@ -12931,7 +12931,7 @@ __metadata:
|
||||
|
||||
"whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-channels":
|
||||
version: 1.26.0
|
||||
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=bb4525a80667d83122108a03d936e41cefd179a4"
|
||||
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=c73f3cfb4e1290103ee946ca7f56d5638f1a66bd"
|
||||
dependencies:
|
||||
"@pedroslopez/moduleraid": ^5.0.2
|
||||
archiver: ^5.3.1
|
||||
@@ -12949,7 +12949,7 @@ __metadata:
|
||||
optional: true
|
||||
unzipper:
|
||||
optional: true
|
||||
checksum: a06d30c9f52a1ff0268e4e98e3c0d80112a5dc8a9445d0df460fac3cbe0168b6f1c5ad494518b1e9a98acb54da8c46a2f6644e9f41b1b2987771fac101747c55
|
||||
checksum: 831a4a2819041f63c2a51a98e1ad7dd9ff8474b1005b11314b613c6dadac545f3c780e38081b7d260d32afa9ffd333a25f9c6407b630a0660bd08dadbc8ead89
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in new issue
Block a user