Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba976138b7 | ||
|
|
bb3328f5a3 | ||
|
|
6db12d7913 | ||
|
|
d968fa90f1 | ||
|
|
06c874fea5 | ||
|
|
d2b7d94607 | ||
|
|
c8f067229b | ||
|
|
798d782869 | ||
|
|
f70bcd0b53 | ||
|
|
17a7764b97 | ||
|
|
58affc9fe2 | ||
|
|
3678cb4392 | ||
|
|
6eda96908f | ||
|
|
ddf161bb2b | ||
|
|
5659dc7296 | ||
|
|
bca90199f1 | ||
|
|
bd263b071c | ||
|
|
e54603085c | ||
|
|
7f7857141e | ||
|
|
3b19dc3a61 | ||
|
|
06e5951c68 | ||
|
|
bcf4ed07d8 | ||
|
|
f711fcca98 | ||
|
|
32e57cab98 | ||
|
|
b092d2bfe1 | ||
|
|
f655f715d0 | ||
|
|
e5c77c60f2 | ||
|
|
a38d4145c2 | ||
|
|
2c47b784b3 | ||
|
|
724dc61717 | ||
|
|
9c44256209 | ||
|
|
1fb82c7678 | ||
|
|
55f7d4dac7 | ||
|
|
10e8869903 |
No files matched your search
@@ -7,7 +7,9 @@
|
||||
"Bash(npm show:*)",
|
||||
"Bash(python3:*)",
|
||||
"Bash(yarn test:unit:*)",
|
||||
"Bash(npx tsc:*)"
|
||||
"Bash(npx tsc:*)",
|
||||
"Bash(pre-commit run:*)",
|
||||
"Bash(yarn test:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
+16
-16
@@ -6,20 +6,20 @@ nodeLinker: node-modules
|
||||
|
||||
supportedArchitectures:
|
||||
cpu:
|
||||
- 'arm'
|
||||
- 'arm64'
|
||||
- 'ia32'
|
||||
- 'loong64'
|
||||
- 'mips'
|
||||
- 'mipsel'
|
||||
- 'ppc64'
|
||||
- 'riscv64'
|
||||
- 's390'
|
||||
- 's390x'
|
||||
- 'x64'
|
||||
- 'wasm32'
|
||||
- arm
|
||||
- arm64
|
||||
- ia32
|
||||
- loong64
|
||||
- mips
|
||||
- mipsel
|
||||
- ppc64
|
||||
- riscv64
|
||||
- s390
|
||||
- s390x
|
||||
- x64
|
||||
- wasm32
|
||||
os:
|
||||
- 'current'
|
||||
- 'darwin'
|
||||
- 'linux'
|
||||
- 'win32'
|
||||
- current
|
||||
- darwin
|
||||
- linux
|
||||
- win32
|
||||
@@ -111,8 +111,7 @@ This guide summarizes how to explore, modify, and validate the WhatsApp HTTP API
|
||||
2. Lean on existing services/managers; extend the appropriate session manager
|
||||
rather than branching logic inline.
|
||||
3. After edits run:
|
||||
- Do **not** run `pre-commit run --all-files` unless the user explicitly
|
||||
asks.
|
||||
- `pre-commit run --all-files`
|
||||
- `yarn build`
|
||||
- `yarn test --watchman=false`
|
||||
4. Do **not** start the application yourself; ask the user to run it if runtime
|
||||
|
||||
@@ -45,6 +45,7 @@ up-webjs:
|
||||
|
||||
up-wpp:
|
||||
yarn up @wppconnect-team/wppconnect
|
||||
yarn up @wppconnect/wa-js
|
||||
|
||||
up-rust-bridge:
|
||||
yarn up -R whatsapp-rust-bridge
|
||||
@@ -57,9 +58,12 @@ proto-gows:
|
||||
|
||||
gows:
|
||||
cd ../gows && \
|
||||
export PATH=${HOME}/go/bin:${PATH} && \
|
||||
(export PATH=${HOME}/go/bin:${PATH} || echo failed) && \
|
||||
make all
|
||||
|
||||
up-dashboard:
|
||||
node scripts/up-dashboard.js
|
||||
|
||||
copy-dashboard:
|
||||
cd ../waha-hub/ui && \
|
||||
make copy-waha
|
||||
@@ -24,7 +24,7 @@ services:
|
||||
- 8.8.8.8
|
||||
|
||||
base: &base
|
||||
image: chatwoot/chatwoot:v4.7.0
|
||||
image: chatwoot/chatwoot:v4.12.1
|
||||
env_file: .chatwoot.env ## Change this file for customized env variables
|
||||
volumes:
|
||||
- chatwoot_storage:/app/storage
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
"@types/sqlite3": "^5.1.0",
|
||||
"@types/ws": "^8.5.4",
|
||||
"@wppconnect-team/wppconnect": "^1.41.1",
|
||||
"@wppconnect/wa-js": "^3.23.3",
|
||||
"adm-zip": "0.5.10",
|
||||
"agentkeepalive": "^4.5.0",
|
||||
"async-lock": "^1.4.1",
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const fs = require('fs');
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const CONFIG_FILE = 'waha.config.json';
|
||||
|
||||
const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
||||
const { repo } = config.waha.dashboard;
|
||||
if (!repo) {
|
||||
console.error(`Missing dashboard.repo in ${CONFIG_FILE}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const branch = 'gh-pages';
|
||||
console.log(`Fetching latest commit for ${repo}@${branch}...`);
|
||||
|
||||
const output = execSync(
|
||||
`git ls-remote https://github.com/${repo} refs/heads/${branch}`,
|
||||
{ encoding: 'utf8' },
|
||||
);
|
||||
|
||||
const sha = output.split('\t')[0].trim();
|
||||
if (!sha) {
|
||||
console.error(`Could not resolve ref ${branch} for ${repo}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
config.waha.dashboard.ref = sha;
|
||||
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
||||
console.log(`Updated ${CONFIG_FILE}: dashboard.ref = ${sha}`);
|
||||
@@ -90,10 +90,10 @@ export class AppsEnabledService implements IAppsService {
|
||||
}
|
||||
|
||||
const service = this.getAppService(app);
|
||||
if (!service && !AppRuntimeConfig.HasApp(app.app)) {
|
||||
if (app.enabled && !service && !AppRuntimeConfig.HasApp(app.app)) {
|
||||
throw new AppDisableError(app.app);
|
||||
}
|
||||
service.validate(app);
|
||||
service?.validate(app);
|
||||
// Only run beforeCreated when app is enabled (default true if omitted)
|
||||
if (app.enabled !== false) {
|
||||
await service.beforeCreated(app);
|
||||
@@ -147,21 +147,21 @@ export class AppsEnabledService implements IAppsService {
|
||||
}
|
||||
|
||||
const service = this.getAppService(app);
|
||||
if (!service && !AppRuntimeConfig.HasApp(app.app)) {
|
||||
if (app.enabled && !service && !AppRuntimeConfig.HasApp(app.app)) {
|
||||
throw new AppDisableError(app.app);
|
||||
}
|
||||
service.validate(app);
|
||||
service?.validate(app);
|
||||
|
||||
const hasEnabledChange = savedApp.enabled !== app.enabled;
|
||||
|
||||
if (hasEnabledChange) {
|
||||
if (app.enabled) {
|
||||
await service.beforeEnabled(savedApp, app);
|
||||
await service?.beforeEnabled(savedApp, app);
|
||||
} else {
|
||||
await service.beforeDisabled(savedApp, app);
|
||||
await service?.beforeDisabled(savedApp, app);
|
||||
}
|
||||
} else {
|
||||
await service.beforeUpdated(savedApp, app);
|
||||
await service?.beforeUpdated(savedApp, app);
|
||||
}
|
||||
await repo.update(app.id, app);
|
||||
const updated = await repo.getById(app.id);
|
||||
|
||||
@@ -2,8 +2,10 @@ import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
Min,
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
|
||||
@@ -23,6 +25,26 @@ export class CallsAppChannelConfig {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
message?: string;
|
||||
|
||||
@ApiProperty({
|
||||
description:
|
||||
'Seconds to wait before declining the call. If not set or undefined, the call is declined immediately (0 seconds).',
|
||||
required: false,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
waitBeforeDecline?: number;
|
||||
|
||||
@ApiProperty({
|
||||
description:
|
||||
'Seconds to wait before sending the auto-reply message. If not set or undefined, the message is sent immediately (0 seconds).',
|
||||
required: false,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
waitBeforeResponse?: number;
|
||||
}
|
||||
|
||||
export class CallsAppConfig {
|
||||
|
||||
@@ -92,10 +92,14 @@ export class CallsListener {
|
||||
}
|
||||
|
||||
if (shouldReject) {
|
||||
const waitBeforeDeclineMs = (config.waitBeforeDecline ?? 0) * 1000;
|
||||
await sleep(waitBeforeDeclineMs);
|
||||
await this.rejectCall(call);
|
||||
}
|
||||
|
||||
if (shouldMessage) {
|
||||
const waitBeforeResponseMs = (config.waitBeforeResponse ?? 0) * 1000;
|
||||
await sleep(waitBeforeResponseMs);
|
||||
await this.replyWithTyping(call.from, message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { AttributeKey, INBOX_CONTACT_CHAT_ID } from '@waha/apps/chatwoot/const';
|
||||
import * as lodash from 'lodash';
|
||||
import { WhatsAppMessage } from '@waha/apps/chatwoot/storage';
|
||||
import { buildMessageId } from '@waha/core/engines/noweb/session.noweb.core';
|
||||
import { isLidUser } from '@waha/core/utils/jids';
|
||||
import { isLidUser, normalizeJid } from '@waha/core/utils/jids';
|
||||
|
||||
export function GetJID(contact: any): string | null {
|
||||
return contact?.custom_attributes?.[AttributeKey.WA_JID];
|
||||
@@ -23,20 +23,13 @@ export function GetAllChatIDs(contact: any): Array<string> {
|
||||
attrs[AttributeKey.WA_JID],
|
||||
attrs[AttributeKey.WA_LID],
|
||||
];
|
||||
return lodash.uniq(ids.filter(Boolean));
|
||||
return lodash.uniq(ids.filter(Boolean).map(normalizeJid));
|
||||
}
|
||||
|
||||
export function FindChatID(contact: any): string | null {
|
||||
if (GetChatID(contact)) {
|
||||
return GetChatID(contact);
|
||||
}
|
||||
if (GetJID(contact)) {
|
||||
return GetJID(contact);
|
||||
}
|
||||
if (GetLID(contact)) {
|
||||
return GetLID(contact);
|
||||
}
|
||||
return null;
|
||||
const chatId =
|
||||
GetChatID(contact) ?? GetJID(contact) ?? GetLID(contact) ?? null;
|
||||
return chatId ? normalizeJid(chatId) : null;
|
||||
}
|
||||
|
||||
export function SerializeWhatsAppKey(message: WhatsAppMessage): string {
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
PollMessage,
|
||||
ListMessage,
|
||||
ShareContactMessage,
|
||||
StatusReplyMessage,
|
||||
TextMessage,
|
||||
UnsupportedMessage,
|
||||
AlbumMessage,
|
||||
@@ -86,6 +87,18 @@ export class MessageAnyHandler extends MessageBaseHandler<WAMessage> {
|
||||
return msg;
|
||||
}
|
||||
|
||||
converter = new StatusReplyMessage(
|
||||
this.l,
|
||||
this.logger,
|
||||
this.waha,
|
||||
this.job,
|
||||
this.session,
|
||||
);
|
||||
msg = await converter.convert(payload, protoMessage);
|
||||
if (msg) {
|
||||
return msg;
|
||||
}
|
||||
|
||||
converter = new TextMessage(this.l, this.logger, this.waha, this.job);
|
||||
msg = await converter.convert(payload, null);
|
||||
if (msg) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
isJidStatusBroadcast,
|
||||
isLidUser,
|
||||
isPnUser,
|
||||
normalizeJid,
|
||||
} from '@waha/core/utils/jids';
|
||||
import { UnknownJIDFormat } from '@waha/apps/chatwoot/errors';
|
||||
import { E164Parser } from '@waha/core/utils/PhoneJidNormalizer';
|
||||
@@ -270,7 +271,7 @@ export function WhatsAppContactInfo(
|
||||
} else if (isJidBroadcast(chatId)) {
|
||||
return new BroadcastContactInfo(session, chatId, locale);
|
||||
} else if (isLidUser(chatId)) {
|
||||
return new LidContactInfo(session, chatId, locale);
|
||||
return new LidContactInfo(session, normalizeJid(chatId), locale);
|
||||
} else if (isPnUser(chatId)) {
|
||||
return new JidContactInfo(session, chatId, locale);
|
||||
} else {
|
||||
|
||||
@@ -335,12 +335,14 @@ waha.core.version.used: |-
|
||||
[**🎁 ادعمنا**]({{{supportUrl}}})
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -487,6 +489,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **تم إرسال PIX نسخ ولصق**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **رد على حالة واتساب**
|
||||
🏷️ **النوع:** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **نص الحالة:** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.unsupported: |-
|
||||
⚠️ **هذا النوع من الرسائل غير مدعوم في هذا الصندوق الوارد.**
|
||||
📱 يرجى فتح **WhatsApp** لعرضه.
|
||||
|
||||
@@ -335,12 +335,14 @@ waha.core.version.used: |-
|
||||
[**🎁 আমাদের সমর্থন করুন**]({{{supportUrl}}})
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -487,6 +489,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX কপি এবং পেস্ট পাঠানো হয়েছে**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **WhatsApp স্ট্যাটাসের জবাব**
|
||||
🏷️ **ধরন:** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **স্ট্যাটাসের টেক্সট:** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.unsupported: |-
|
||||
⚠️ **এই ধরনের বার্তা এই ইনবক্সে সমর্থিত নয়।**
|
||||
📱 এটি দেখতে অনুগ্রহ করে **WhatsApp** খুলুন।
|
||||
|
||||
@@ -338,12 +338,14 @@ waha.core.version.used: |-
|
||||
[**🎁 Unterstützen Sie uns**]({{{supportUrl}}})
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -490,6 +492,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX Kopieren und Einfügen gesendet**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **Antwort auf WhatsApp-Status**
|
||||
🏷️ **Typ:** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **Status-Text:** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.unsupported: |-
|
||||
⚠️ **Dieser Nachrichtentyp wird in dieser Inbox nicht unterstützt.**
|
||||
📱 Bitte öffne **WhatsApp**, um ihn anzusehen.
|
||||
|
||||
@@ -116,12 +116,14 @@ whatsapp.contact.broadcast.suffix: 'Broadcast List'
|
||||
whatsapp.contact.status.name: 'Status'
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -268,6 +270,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX Copy and Paste sent**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **Reply to WhatsApp Status**
|
||||
🏷️ **Type:** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **Status Text:** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.has.media.no.media: |-
|
||||
{{#content}}
|
||||
{{{content}}}
|
||||
|
||||
@@ -335,12 +335,14 @@ waha.core.version.used: |-
|
||||
[**🎁 Apóyanos**]({{{supportUrl}}})
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -487,6 +489,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX Copiar y Pegar enviado**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **Respuesta al estado de WhatsApp**
|
||||
🏷️ **Tipo:** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **Texto del Estado:** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.unsupported: |-
|
||||
⚠️ **Este tipo de mensaje no es compatible con esta bandeja de entrada.**
|
||||
📱 Por favor, abre **WhatsApp** para verlo.
|
||||
|
||||
@@ -335,12 +335,14 @@ waha.core.version.used: |-
|
||||
[**🎁 از ما حمایت کنید**]({{{supportUrl}}})
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -487,6 +489,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX کپی و چسباندن ارسال شد**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **پاسخ به وضعیت واتساپ**
|
||||
🏷️ **نوع:** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **متن وضعیت:** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.unsupported: |-
|
||||
⚠️ **این نوع پیام در این صندوق ورودی پشتیبانی نمیشود.**
|
||||
📱 لطفاً برای مشاهده آن **WhatsApp** را باز کنید.
|
||||
|
||||
@@ -335,12 +335,14 @@ waha.core.version.used: |-
|
||||
[**🎁 Soutenez-nous**]({{{supportUrl}}})
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -487,6 +489,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX Copier et Coller envoyé**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **Réponse au statut WhatsApp**
|
||||
🏷️ **Type :** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **Texte du Statut :** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.unsupported: |-
|
||||
⚠️ **Ce type de message n’est pas pris en charge dans cette boîte de réception.**
|
||||
📱 Veuillez ouvrir **WhatsApp** pour l’afficher.
|
||||
|
||||
@@ -335,12 +335,14 @@ waha.core.version.used: |-
|
||||
[**🎁 תמוך בנו**]({{{supportUrl}}})
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -487,6 +489,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX העתק והדבק נשלח**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **תגובה לסטטוס ב-WhatsApp**
|
||||
🏷️ **סוג:** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **טקסט הסטטוס:** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.unsupported: |-
|
||||
⚠️ **סוג הודעה זה אינו נתמך בתיבה זו.**
|
||||
📱 אנא פתח/י את **WhatsApp** כדי לצפות בזה.
|
||||
|
||||
@@ -335,12 +335,14 @@ waha.core.version.used: |-
|
||||
[**🎁 हमें समर्थन दें**]({{{supportUrl}}})
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -487,6 +489,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX कॉपी और पेस्ट भेजा गया**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **WhatsApp स्टेटस का जवाब**
|
||||
🏷️ **प्रकार:** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **स्टेटस टेक्स्ट:** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.unsupported: |-
|
||||
⚠️ **इस प्रकार का संदेश इस इनबॉक्स में समर्थित नहीं है।**
|
||||
📱 इसे देखने के लिए कृपया **WhatsApp** खोलें।
|
||||
|
||||
@@ -335,12 +335,14 @@ waha.core.version.used: |-
|
||||
[**🎁 Dukung Kami**]({{{supportUrl}}})
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -487,6 +489,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX Salin dan Tempel terkirim**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **Balasan ke Status WhatsApp**
|
||||
🏷️ **Tipe:** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **Teks Status:** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.unsupported: |-
|
||||
⚠️ **Jenis pesan ini tidak didukung di kotak masuk ini.**
|
||||
📱 Silakan buka **WhatsApp** untuk melihatnya.
|
||||
|
||||
@@ -116,12 +116,14 @@ whatsapp.contact.broadcast.suffix: 'Lista Broadcast'
|
||||
whatsapp.contact.status.name: 'Stato'
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -268,6 +270,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX Copia e Incolla inviato**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **Risposta allo stato di WhatsApp**
|
||||
🏷️ **Tipo:** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **Testo dello Stato:** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.has.media.no.media: |-
|
||||
{{#content}}
|
||||
{{{content}}}
|
||||
|
||||
@@ -335,12 +335,14 @@ waha.core.version.used: |-
|
||||
[**🎁 ਸਾਡਾ ਸਮਰਥਨ ਕਰੋ**]({{{supportUrl}}})
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -487,6 +489,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX ਕਾਪੀ ਅਤੇ ਪੇਸਟ ਭੇਜਿਆ ਗਿਆ**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **WhatsApp ਸਟੇਟਸ ਦਾ ਜਵਾਬ**
|
||||
🏷️ **ਕਿਸਮ:** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **ਸਟੇਟਸ ਲਿਖਤ:** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.unsupported: |-
|
||||
⚠️ **ਇਹ ਸੁਨੇਹੇ ਦੀ ਕਿਸਮ ਇਸ ਇਨਬਾਕਸ ਵਿੱਚ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ।**
|
||||
📱 ਇਸਨੂੰ ਦੇਖਣ ਲਈ ਕਿਰਪਾ ਕਰਕੇ **WhatsApp** ਖੋਲ੍ਹੋ।
|
||||
|
||||
@@ -336,12 +336,14 @@ waha.core.version.used: |-
|
||||
[**🎁 Apoie-nos**]({{{supportUrl}}})
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -492,6 +494,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX Copia e Cola enviado**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **Resposta a Status do WhatsApp**
|
||||
🏷️ **Tipo:** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **Texto do Status:** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.unsupported: |-
|
||||
⚠️ **Esse tipo de mensagem não é suportado nesta caixa de entrada.**
|
||||
📱 Por favor, abra o **WhatsApp** para visualizá-la.
|
||||
|
||||
@@ -335,12 +335,14 @@ waha.core.version.used: |-
|
||||
[**🎁 Поддержать нас**]({{{supportUrl}}})
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -487,6 +489,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX Копировать и вставить отправлен**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **Ответ на статус WhatsApp**
|
||||
🏷️ **Тип:** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **Текст Статуса:** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.unsupported: |-
|
||||
⚠️ **Этот тип сообщения не поддерживается в этом Inbox.**
|
||||
📱 Пожалуйста, откройте **WhatsApp**, чтобы просмотреть.
|
||||
|
||||
@@ -335,12 +335,14 @@ waha.core.version.used: |-
|
||||
[**🎁 Bizi Destekleyin**]({{{supportUrl}}})
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -487,6 +489,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX Kopyala ve Yapıştır gönderildi**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **WhatsApp Durumuna Yanıt**
|
||||
🏷️ **Tür:** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **Durum Metni:** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.unsupported: |-
|
||||
⚠️ **Bu mesaj türü bu gelen kutusunda desteklenmiyor.**
|
||||
📱 Görüntülemek için lütfen **WhatsApp’ı** açın.
|
||||
|
||||
@@ -335,12 +335,14 @@ waha.core.version.used: |-
|
||||
[**🎁 Підтримайте нас**]({{{supportUrl}}})
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -487,6 +489,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX Копіювати та Вставити надіслано**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **Відповідь на статус WhatsApp**
|
||||
🏷️ **Тип:** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **Текст Статусу:** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.unsupported: |-
|
||||
⚠️ **Цей тип повідомлення не підтримується в цих вхідних.**
|
||||
📱 Будь ласка, відкрийте **WhatsApp**, щоб переглянути.
|
||||
|
||||
@@ -335,12 +335,14 @@ waha.core.version.used: |-
|
||||
[**🎁 ہماری حمایت کریں**]({{{supportUrl}}})
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -487,6 +489,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX کاپی اور پیسٹ بھیج دیا گیا**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **WhatsApp اسٹیٹس کا جواب**
|
||||
🏷️ **قسم:** {{type}}
|
||||
{{#quotedText}}
|
||||
📝 **اسٹیٹس کا متن:** {{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.unsupported: |-
|
||||
⚠️ **اس قسم کا پیغام اس انبکس میں معاونت یافتہ نہیں ہے۔**
|
||||
📱 اسے دیکھنے کے لیے براہ کرم **WhatsApp** کھولیں۔
|
||||
|
||||
@@ -335,12 +335,14 @@ waha.core.version.used: |-
|
||||
[**🎁 支持我们**]({{{supportUrl}}})
|
||||
|
||||
chatwoot.to.whatsapp.message.text: |-
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
|
||||
chatwoot.to.whatsapp.message.media.caption: |-
|
||||
{{#singleAttachment}}
|
||||
{{#content}}
|
||||
{{{ content }}}
|
||||
{{#chatwoot.sender.name}}*{{{chatwoot.sender.name}}}*:
|
||||
{{/chatwoot.sender.name}}{{{ content }}}
|
||||
{{/content}}
|
||||
{{/singleAttachment}}
|
||||
|
||||
@@ -487,6 +489,15 @@ whatsapp.to.chatwoot.message.pix: |-
|
||||
💳 **PIX 复制粘贴已发送**
|
||||
{{/pixData}}
|
||||
|
||||
whatsapp.to.chatwoot.message.status.reply: |-
|
||||
📣 **回复 WhatsApp 状态**
|
||||
🏷️ **类型:**{{type}}
|
||||
{{#quotedText}}
|
||||
📝 **状态文本:**{{quotedText}}
|
||||
{{/quotedText}}
|
||||
|
||||
{{content}}
|
||||
|
||||
whatsapp.to.chatwoot.message.unsupported: |-
|
||||
⚠️ **此收件箱不支持此消息类型。**
|
||||
📱 请打开 **WhatsApp** 查看。
|
||||
|
||||
@@ -54,6 +54,7 @@ export enum TKey {
|
||||
WA_TO_CW_MESSAGE_POLL = 'whatsapp.to.chatwoot.message.poll',
|
||||
WA_TO_CW_MESSAGE_EVENT = 'whatsapp.to.chatwoot.message.event',
|
||||
WA_TO_CW_MESSAGE_PIX = 'whatsapp.to.chatwoot.message.pix',
|
||||
WA_TO_CW_MESSAGE_STATUS_REPLY = 'whatsapp.to.chatwoot.message.status.reply',
|
||||
WA_TO_CW_MESSAGE_UNSUPPORTED = 'whatsapp.to.chatwoot.message.unsupported',
|
||||
WA_TO_CW_MESSAGE_FACEBOOK_AD = 'whatsapp.to.chatwoot.message.facebook.ad',
|
||||
WA_TO_CW_MESSAGE_LIST = 'whatsapp.to.chatwoot.message.list',
|
||||
@@ -189,6 +190,11 @@ export type TemplatePayloads = {
|
||||
message: proto.Message | null;
|
||||
pixData: PixTemplatePayload;
|
||||
};
|
||||
[TKey.WA_TO_CW_MESSAGE_STATUS_REPLY]: {
|
||||
type: string;
|
||||
quotedText?: string;
|
||||
content: string;
|
||||
};
|
||||
[TKey.WA_TO_CW_MESSAGE_UNSUPPORTED]: { details: Link };
|
||||
[TKey.WA_TO_CW_MESSAGE_FACEBOOK_AD]: {
|
||||
payload: WAMessage;
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
import type { proto } from '@adiwajshing/baileys';
|
||||
import { ILogger } from '@waha/apps/app_sdk/ILogger';
|
||||
import { WAHASelf, WAHASessionAPI } from '@waha/apps/app_sdk/waha/WAHASelf';
|
||||
import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base';
|
||||
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
|
||||
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
|
||||
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/markdown';
|
||||
import { EngineHelper, QuotedMedia } from '@waha/apps/chatwoot/waha';
|
||||
import { Jid } from '@waha/core/engines/const';
|
||||
import { WAMessage } from '@waha/structures/responses.dto';
|
||||
import { Job } from 'bullmq';
|
||||
import { TextMessage } from './TextMessage';
|
||||
import { isEmptyString } from './utils/text';
|
||||
import { SendAttachment } from '@waha/apps/chatwoot/client/types';
|
||||
import esm from '@waha/vendor/esm';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const mime = require('mime-types');
|
||||
|
||||
type StatusReplyType = 'text' | 'image' | 'audio' | 'video' | 'unknown';
|
||||
|
||||
interface StatusQuotedMessageInfo {
|
||||
data?: any;
|
||||
id: string;
|
||||
type: StatusReplyType;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
export class StatusReplyMessage extends TextMessage {
|
||||
constructor(
|
||||
locale: Locale,
|
||||
logger: ILogger,
|
||||
waha: WAHASelf,
|
||||
job: Job,
|
||||
private readonly session: WAHASessionAPI,
|
||||
) {
|
||||
super(locale, logger, waha, job);
|
||||
}
|
||||
|
||||
async convert(
|
||||
payload: WAMessage,
|
||||
protoMessage: proto.Message | null,
|
||||
): Promise<ChatWootMessagePartial | null> {
|
||||
if (!payload.replyTo?.id) {
|
||||
return null;
|
||||
}
|
||||
if (!EngineHelper.IsReplyToStatus(payload, protoMessage)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let attachments: SendAttachment[] = [];
|
||||
const quoted = this.getStatusQuotedMessageInfo(payload);
|
||||
let wamessage = null; // aka "quoted from store"
|
||||
|
||||
// Media - Use provided replyTo.media if present
|
||||
if (attachments.length === 0 && payload.replyTo.media) {
|
||||
const extension = mime.extension(payload.replyTo.media.mimetype);
|
||||
payload.replyTo.media.filename = `status-message.${extension}`;
|
||||
const quotedAttachments = await super.getAttachments(payload.replyTo);
|
||||
attachments = [...attachments, ...quotedAttachments];
|
||||
}
|
||||
|
||||
// Media - fetch message by id from store and use it's "media"
|
||||
if (attachments.length == 0) {
|
||||
wamessage = await this.fetchStatusMessage(quoted.id);
|
||||
if (wamessage?.media?.url) {
|
||||
const extension = mime.extension(wamessage.media.mimetype);
|
||||
wamessage.media.filename = `status-message.${extension}`;
|
||||
const quotedAttachments = await super.getAttachments(wamessage);
|
||||
attachments = [...attachments, ...quotedAttachments];
|
||||
}
|
||||
}
|
||||
|
||||
// Media - get media url, key and try to decrypt it
|
||||
if (attachments.length == 0) {
|
||||
const replyData = payload.replyTo._data;
|
||||
const quotedMedia = EngineHelper.ExtractQuotedMedia(replyData);
|
||||
if (quotedMedia) {
|
||||
const attachment =
|
||||
await this.downloadQuotedMediaAttachment(quotedMedia);
|
||||
if (attachment) {
|
||||
attachments.push(attachment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Act as regular text message, but we'll adjust some properties
|
||||
const msg = await super.convert(payload, protoMessage);
|
||||
// First goes Quoted attachments, then what sender sent
|
||||
msg.attachments = [...attachments, ...msg.attachments];
|
||||
|
||||
// Wrap content with additional info
|
||||
const quotedText = quoted.text ?? wamessage?.body ?? payload.replyTo?.body;
|
||||
if (
|
||||
isEmptyString(quotedText) &&
|
||||
isEmptyString(msg.content) &&
|
||||
attachments.length === 0
|
||||
) {
|
||||
// Nothing to send.
|
||||
// Must check msg.content before wrapping it in status reply
|
||||
return null;
|
||||
}
|
||||
|
||||
msg.content = this.locale.r(TKey.WA_TO_CW_MESSAGE_STATUS_REPLY, {
|
||||
type: quoted.type,
|
||||
quotedText: WhatsappToMarkdown(quotedText),
|
||||
content: msg.content,
|
||||
});
|
||||
return msg;
|
||||
}
|
||||
|
||||
private getStatusQuotedMessageInfo(
|
||||
payload: WAMessage,
|
||||
): StatusQuotedMessageInfo {
|
||||
const replyData = payload.replyTo._data;
|
||||
const imageMessage =
|
||||
replyData?.imageMessage || this.getWebjsMediaData(replyData, 'image');
|
||||
if (imageMessage) {
|
||||
return {
|
||||
data: replyData,
|
||||
id: payload.replyTo.id,
|
||||
type: 'image',
|
||||
text: imageMessage.caption ?? payload.replyTo.body ?? replyData?.body,
|
||||
};
|
||||
}
|
||||
|
||||
const videoMessage =
|
||||
replyData?.videoMessage || this.getWebjsMediaData(replyData, 'video');
|
||||
if (videoMessage) {
|
||||
return {
|
||||
data: replyData,
|
||||
id: payload.replyTo.id,
|
||||
type: 'video',
|
||||
text: videoMessage.caption ?? payload.replyTo.body ?? replyData?.body,
|
||||
};
|
||||
}
|
||||
|
||||
const audioMessage =
|
||||
replyData?.audioMessage || this.getWebjsMediaData(replyData, 'audio');
|
||||
if (audioMessage) {
|
||||
return {
|
||||
data: replyData,
|
||||
id: payload.replyTo.id,
|
||||
type: 'audio',
|
||||
text: audioMessage.caption ?? payload.replyTo.body ?? replyData?.body,
|
||||
};
|
||||
}
|
||||
|
||||
const quotedText =
|
||||
replyData?.extendedTextMessage?.text ??
|
||||
replyData?.conversation ??
|
||||
payload.replyTo.body ??
|
||||
replyData?.body;
|
||||
return {
|
||||
data: replyData,
|
||||
id: payload.replyTo.id,
|
||||
type: quotedText ? 'text' : 'unknown',
|
||||
text: quotedText,
|
||||
};
|
||||
}
|
||||
|
||||
private getWebjsMediaData(
|
||||
replyData: any,
|
||||
expectedKind: 'image' | 'video' | 'audio',
|
||||
): any | null {
|
||||
if (!replyData) {
|
||||
return null;
|
||||
}
|
||||
const kind = replyData.kind ?? replyData.type;
|
||||
if (kind !== expectedKind) {
|
||||
return null;
|
||||
}
|
||||
return replyData;
|
||||
}
|
||||
|
||||
private async downloadQuotedMediaAttachment(
|
||||
quotedMedia: QuotedMedia,
|
||||
): Promise<SendAttachment | null> {
|
||||
try {
|
||||
const decrypt = !!quotedMedia.mediaKey;
|
||||
const mediaKeyBuffer = decrypt
|
||||
? Buffer.from(quotedMedia.mediaKey, 'base64')
|
||||
: undefined;
|
||||
const stream = await esm.b.downloadContentFromMessage(
|
||||
{
|
||||
mediaKey: mediaKeyBuffer,
|
||||
directPath: quotedMedia.directPath,
|
||||
url: quotedMedia.url,
|
||||
},
|
||||
quotedMedia.mediaType as any,
|
||||
{},
|
||||
decrypt,
|
||||
);
|
||||
const chunks: Buffer[] = [];
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
const buffer = Buffer.concat(chunks);
|
||||
const extension = mime.extension(quotedMedia.mimetype);
|
||||
const filename = `status-message.${extension}`;
|
||||
return {
|
||||
content: buffer.toString('base64'),
|
||||
filename: filename,
|
||||
encoding: 'base64',
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.warn('Failed to download quoted status media directly');
|
||||
this.logger.debug(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async fetchStatusMessage(
|
||||
replyMessageId: string,
|
||||
): Promise<WAMessage | null> {
|
||||
try {
|
||||
const quotedMessage = await this.session.getMessageById(
|
||||
Jid.BROADCAST,
|
||||
replyMessageId,
|
||||
true,
|
||||
);
|
||||
return quotedMessage || null;
|
||||
} catch (error) {
|
||||
this.logger.warn(
|
||||
`Failed to fetch original status message '${replyMessageId}'`,
|
||||
);
|
||||
this.logger.debug(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,16 +11,17 @@ import { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chat
|
||||
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/markdown';
|
||||
import { JobLink } from '@waha/apps/app_sdk/JobUtils';
|
||||
import { Job } from 'bullmq';
|
||||
import { WAMedia } from '@waha/structures/media.dto';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const mime = require('mime-types');
|
||||
|
||||
export class TextMessage implements MessageToChatWootConverter {
|
||||
constructor(
|
||||
private readonly locale: Locale,
|
||||
private readonly logger: ILogger,
|
||||
private readonly waha: WAHASelf,
|
||||
private readonly job: Job,
|
||||
protected readonly locale: Locale,
|
||||
protected readonly logger: ILogger,
|
||||
protected readonly waha: WAHASelf,
|
||||
protected readonly job: Job,
|
||||
) {}
|
||||
|
||||
async convert(
|
||||
@@ -52,12 +53,14 @@ export class TextMessage implements MessageToChatWootConverter {
|
||||
}
|
||||
return {
|
||||
content: WhatsappToMarkdown(content),
|
||||
attachments,
|
||||
attachments: attachments,
|
||||
private: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
private async getAttachments(payload: WAMessage): Promise<SendAttachment[]> {
|
||||
protected async getAttachments(payload: {
|
||||
media?: WAMedia;
|
||||
}): Promise<SendAttachment[]> {
|
||||
const hasMedia = payload.media?.url;
|
||||
if (!hasMedia) {
|
||||
return [];
|
||||
|
||||
@@ -13,6 +13,7 @@ export { EventMessage } from './EventMessage';
|
||||
export { PixMessage } from './PixMessage';
|
||||
export { ListMessage } from './ListMessage';
|
||||
export { AlbumMessage } from './AlbumMessage';
|
||||
export { StatusReplyMessage } from './StatusReplyMessage';
|
||||
|
||||
export type Awaitable<T> = T | Promise<T>;
|
||||
|
||||
|
||||
@@ -1,18 +1,34 @@
|
||||
import * as lodash from 'lodash';
|
||||
import type { proto } from '@adiwajshing/baileys';
|
||||
import { WhatsAppMessage } from '@waha/apps/chatwoot/storage';
|
||||
import { WAHAEngine } from '@waha/structures/enums.dto';
|
||||
import { getEngineName } from '@waha/version';
|
||||
import { Message as MessageInstance } from 'whatsapp-web.js/src/structures';
|
||||
import { Jid } from '@waha/core/engines/const';
|
||||
import { isLidUser, isPnUser, toCusFormat } from '@waha/core/utils/jids';
|
||||
import { parseMessageIdSerialized } from '@waha/core/utils/ids';
|
||||
import { WAMessage } from '@waha/structures/responses.dto';
|
||||
import { CallData } from '@waha/structures/calls.dto';
|
||||
import { getContextInfo } from '@waha/core/utils/pwa';
|
||||
|
||||
export interface QuotedMedia {
|
||||
url?: string;
|
||||
directPath: string;
|
||||
mediaKey?: string;
|
||||
mimetype: string;
|
||||
mediaType: string;
|
||||
}
|
||||
|
||||
interface IEngineHelper {
|
||||
ChatID(message: WAMessage | any): string;
|
||||
|
||||
CallChatID(call: CallData | any): string;
|
||||
|
||||
IsReplyToStatus(
|
||||
message: WAMessage,
|
||||
protoMessage: proto.Message | null,
|
||||
): boolean;
|
||||
|
||||
WhatsAppMessageKeys(message: any): WhatsAppMessage;
|
||||
|
||||
IterateMessages<T extends { timestamp: number }>(
|
||||
@@ -24,6 +40,8 @@ interface IEngineHelper {
|
||||
FilterChatIdsForMessages(chats: string[]): string[];
|
||||
|
||||
SupportsAllChatForMessage(): boolean;
|
||||
|
||||
ExtractQuotedMedia(replyData: any): QuotedMedia | null;
|
||||
}
|
||||
|
||||
class NOWEBHelper implements IEngineHelper {
|
||||
@@ -35,6 +53,14 @@ class NOWEBHelper implements IEngineHelper {
|
||||
return call.from;
|
||||
}
|
||||
|
||||
IsReplyToStatus(
|
||||
message: WAMessage,
|
||||
protoMessage: proto.Message | null,
|
||||
): boolean {
|
||||
void message;
|
||||
return getContextInfo(protoMessage)?.remoteJid === Jid.BROADCAST;
|
||||
}
|
||||
|
||||
WhatsAppMessageKeys(message: any): WhatsAppMessage {
|
||||
const timestamp = parseInt(message.messageTimestamp) * 1000;
|
||||
return {
|
||||
@@ -63,6 +89,10 @@ class NOWEBHelper implements IEngineHelper {
|
||||
SupportsAllChatForMessage(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
ExtractQuotedMedia(replyData: any): QuotedMedia | null {
|
||||
return extractProtoQuotedMedia(replyData);
|
||||
}
|
||||
}
|
||||
|
||||
class GOWSHelper implements IEngineHelper {
|
||||
@@ -74,6 +104,14 @@ class GOWSHelper implements IEngineHelper {
|
||||
return call._data?.CallCreator || call.from;
|
||||
}
|
||||
|
||||
IsReplyToStatus(
|
||||
message: WAMessage,
|
||||
protoMessage: proto.Message | null,
|
||||
): boolean {
|
||||
void message;
|
||||
return getContextInfo(protoMessage)?.remoteJid === Jid.BROADCAST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse API response and get the data
|
||||
* API Response depends on engine right now
|
||||
@@ -107,6 +145,10 @@ class GOWSHelper implements IEngineHelper {
|
||||
ContactIsMy(contact) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ExtractQuotedMedia(replyData: any): QuotedMedia | null {
|
||||
return extractProtoQuotedMedia(replyData, true);
|
||||
}
|
||||
}
|
||||
|
||||
class WEBJSHelper implements IEngineHelper {
|
||||
@@ -118,6 +160,14 @@ class WEBJSHelper implements IEngineHelper {
|
||||
return call.from;
|
||||
}
|
||||
|
||||
IsReplyToStatus(
|
||||
message: WAMessage,
|
||||
protoMessage: proto.Message | null,
|
||||
): boolean {
|
||||
void protoMessage;
|
||||
return message._data?.quotedRemoteJid === Jid.BROADCAST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse API response and get the data for WEBJS engine
|
||||
*/
|
||||
@@ -161,6 +211,10 @@ class WEBJSHelper implements IEngineHelper {
|
||||
ContactIsMy(contact) {
|
||||
return contact.isMyContact;
|
||||
}
|
||||
|
||||
ExtractQuotedMedia(replyData: any): QuotedMedia | null {
|
||||
return extractBrowserQuotedMedia(replyData);
|
||||
}
|
||||
}
|
||||
|
||||
class WPPHelper implements IEngineHelper {
|
||||
@@ -172,6 +226,14 @@ class WPPHelper implements IEngineHelper {
|
||||
return call.from;
|
||||
}
|
||||
|
||||
IsReplyToStatus(
|
||||
message: WAMessage,
|
||||
protoMessage: proto.Message | null,
|
||||
): boolean {
|
||||
void protoMessage;
|
||||
return message._data?.quotedRemoteJid === Jid.BROADCAST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse API response and get the data for WPP engine.
|
||||
* WPP returns message.id as a composite string "fromMe_chatId_msgId".
|
||||
@@ -217,6 +279,10 @@ class WPPHelper implements IEngineHelper {
|
||||
ContactIsMy(contact) {
|
||||
return contact.isMyContact;
|
||||
}
|
||||
|
||||
ExtractQuotedMedia(replyData: any): QuotedMedia | null {
|
||||
return extractBrowserQuotedMedia(replyData);
|
||||
}
|
||||
}
|
||||
|
||||
// Choose the right EngineHelper based on getEngineName() function
|
||||
@@ -241,6 +307,66 @@ switch (getEngineName()) {
|
||||
|
||||
export const EngineHelper = engineHelper;
|
||||
|
||||
//
|
||||
// Quoted media extraction helpers
|
||||
//
|
||||
|
||||
function extractProtoQuotedMedia(
|
||||
replyData: any,
|
||||
capitalUrl = false,
|
||||
): QuotedMedia | null {
|
||||
if (!replyData) {
|
||||
return null;
|
||||
}
|
||||
const mediaTypes: Array<[string, string]> = [
|
||||
['imageMessage', 'image'],
|
||||
['videoMessage', 'video'],
|
||||
['audioMessage', 'audio'],
|
||||
];
|
||||
for (const [key, mediaType] of mediaTypes) {
|
||||
const msg = replyData[key];
|
||||
if (!msg) {
|
||||
continue;
|
||||
}
|
||||
const url: string | undefined = capitalUrl ? msg.URL ?? msg.url : msg.url;
|
||||
const directPath: string = msg.directPath;
|
||||
const mimetype: string = msg.mimetype;
|
||||
if (!directPath || !mimetype) {
|
||||
continue;
|
||||
}
|
||||
return {
|
||||
url: url,
|
||||
directPath: directPath,
|
||||
mediaKey: msg.mediaKey,
|
||||
mimetype: mimetype,
|
||||
mediaType: mediaType,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function extractBrowserQuotedMedia(replyData: any): QuotedMedia | null {
|
||||
if (!replyData) {
|
||||
return null;
|
||||
}
|
||||
const kind: string = replyData.kind ?? replyData.type;
|
||||
if (kind !== 'image' && kind !== 'video' && kind !== 'audio') {
|
||||
return null;
|
||||
}
|
||||
const directPath: string = replyData.directPath;
|
||||
const mimetype: string = replyData.mimetype;
|
||||
if (!directPath || !mimetype) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
url: replyData.deprecatedMms3Url,
|
||||
directPath: directPath,
|
||||
mediaKey: replyData.mediaKey,
|
||||
mimetype: mimetype,
|
||||
mediaType: kind,
|
||||
};
|
||||
}
|
||||
|
||||
function preferPnChats(chats: string[]): string[] {
|
||||
const unique = lodash.uniq(chats ?? []);
|
||||
const hasPn = unique.some(isPnUser);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { EngineHelper } from './engines';
|
||||
import type { QuotedMedia } from './engines';
|
||||
|
||||
export { EngineHelper };
|
||||
export type { QuotedMedia };
|
||||
@@ -195,6 +195,8 @@ export class SwaggerConfiguratorCore {
|
||||
'/ping',
|
||||
'/ws',
|
||||
'/webhooks/',
|
||||
'/jobs',
|
||||
'/jobs/',
|
||||
...config.getExcludedFullPaths(),
|
||||
]);
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import {
|
||||
getBrowserExecutablePath as getBrowserExecutablePathAutodetect,
|
||||
} from '@waha/core/abc/session.browser';
|
||||
import {
|
||||
CoreMediaConverter,
|
||||
IMediaConverter,
|
||||
@@ -142,15 +145,6 @@ const qrcode = require('qrcode-terminal');
|
||||
|
||||
axiosRetry(axios, { retries: 3 });
|
||||
|
||||
const CHROME_PATH = '/usr/bin/google-chrome-stable';
|
||||
const CHROMIUM_PATH = '/usr/bin/chromium';
|
||||
|
||||
export function getBrowserExecutablePath() {
|
||||
if (fs.existsSync(CHROME_PATH)) {
|
||||
return CHROME_PATH;
|
||||
}
|
||||
return CHROMIUM_PATH;
|
||||
}
|
||||
|
||||
export function ensureSuffix(phone) {
|
||||
const suffix = '@c.us';
|
||||
@@ -360,7 +354,7 @@ export abstract class WhatsappSession {
|
||||
}
|
||||
|
||||
getBrowserExecutablePath() {
|
||||
return getBrowserExecutablePath();
|
||||
return getBrowserExecutablePathAutodetect();
|
||||
}
|
||||
|
||||
getBrowserArgsForPuppeteer() {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import * as fs from 'fs';
|
||||
|
||||
//
|
||||
// Browser executable path detection
|
||||
//
|
||||
|
||||
// Ordered: Chrome paths first, then Chromium.
|
||||
// Windows: only global Program Files paths (no per-user %LOCALAPPDATA%).
|
||||
const BROWSER_PATHS: Record<string, string[]> = {
|
||||
darwin: [
|
||||
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
||||
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
||||
],
|
||||
win32: [
|
||||
'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
||||
'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
|
||||
'C:\\Program Files\\Chromium\\Application\\chrome.exe',
|
||||
],
|
||||
linux: [
|
||||
'/usr/bin/google-chrome',
|
||||
'/usr/bin/google-chrome-stable',
|
||||
'/usr/bin/chromium',
|
||||
'/usr/bin/chromium-browser',
|
||||
],
|
||||
};
|
||||
|
||||
export function getBrowserExecutablePath(): string {
|
||||
const paths = BROWSER_PATHS[process.platform] ?? BROWSER_PATHS['linux'];
|
||||
for (const p of paths) {
|
||||
if (fs.existsSync(p)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return paths[0];
|
||||
}
|
||||
|
||||
// A path belongs to Google Chrome when it contains "google" in any casing.
|
||||
// All Chrome paths include "Google" (macOS/Windows) or "google-chrome" (Linux),
|
||||
// while Chromium paths never do.
|
||||
export function isChromeExecutablePath(executablePath: string): boolean {
|
||||
return executablePath.toLowerCase().includes('google');
|
||||
}
|
||||
@@ -314,7 +314,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
grpc.credentials.createInsecure(),
|
||||
);
|
||||
|
||||
promisify(this.client.StartSession)(request).catch((err) => {
|
||||
await promisify(this.client.StartSession)(request).catch((err) => {
|
||||
this.logger.error('Failed to start the client');
|
||||
this.logger.error(err, err.stack);
|
||||
this.status = WAHASessionStatus.FAILED;
|
||||
@@ -2196,6 +2196,16 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
const media = await this.downloadMediaSafe(message);
|
||||
wamessage.media = media;
|
||||
}
|
||||
if (downloadMedia && wamessage.replyTo?.hasMedia) {
|
||||
const msg = {
|
||||
Message: wamessage.replyTo._data,
|
||||
Info: {
|
||||
Chat: message.Info.Chat,
|
||||
ID: wamessage.replyTo.id || '',
|
||||
},
|
||||
};
|
||||
wamessage.replyTo.media = await this.downloadMediaSafe(msg);
|
||||
}
|
||||
return wamessage;
|
||||
}
|
||||
|
||||
@@ -2405,10 +2415,13 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return null;
|
||||
}
|
||||
const body = extractBody(quotedMessage);
|
||||
const mediaContent = extractMediaContent(quotedMessage);
|
||||
return {
|
||||
id: contextInfo.stanzaID,
|
||||
participant: toCusFormat(contextInfo.participant),
|
||||
body: body,
|
||||
hasMedia: Boolean(mediaContent),
|
||||
media: null,
|
||||
_data: quotedMessage,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Browsers, WABrowserDescription } from '@adiwajshing/baileys';
|
||||
import makeWASocket, {
|
||||
Browsers,
|
||||
Chat,
|
||||
Contact,
|
||||
decryptPollVote,
|
||||
@@ -19,6 +19,7 @@ import makeWASocket, {
|
||||
PresenceData,
|
||||
proto,
|
||||
SocketConfig,
|
||||
WABrowserDescription,
|
||||
WAMessageContent,
|
||||
WAMessageKey,
|
||||
WAMessageUpdate,
|
||||
@@ -183,13 +184,13 @@ import * as NodeCache from 'node-cache';
|
||||
import {
|
||||
filter,
|
||||
fromEvent,
|
||||
groupBy,
|
||||
identity,
|
||||
merge,
|
||||
mergeAll,
|
||||
mergeMap,
|
||||
Observable,
|
||||
partition,
|
||||
groupBy,
|
||||
share,
|
||||
tap,
|
||||
} from 'rxjs';
|
||||
@@ -2482,9 +2483,24 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return null;
|
||||
}
|
||||
// Media
|
||||
if (downloadMedia) {
|
||||
const media = await this.downloadMediaSafe(message);
|
||||
wamessage.media = media;
|
||||
if (downloadMedia && wamessage.hasMedia) {
|
||||
wamessage.media = await this.downloadMediaSafe(message);
|
||||
}
|
||||
|
||||
if (downloadMedia && wamessage.replyTo?.hasMedia) {
|
||||
const mediaContent = extractMediaContent(wamessage.replyTo._data);
|
||||
const m = {
|
||||
message: wamessage.replyTo._data,
|
||||
key: {
|
||||
id:
|
||||
wamessage.replyTo.id ||
|
||||
mediaContent.fileSha256 ||
|
||||
mediaContent.fileEncSha256 ||
|
||||
mediaContent.mediaKeyTimestamp,
|
||||
remoteJid: message.key.remoteJid,
|
||||
},
|
||||
};
|
||||
wamessage.replyTo.media = await this.downloadMediaSafe(m);
|
||||
}
|
||||
return wamessage;
|
||||
}
|
||||
@@ -2544,10 +2560,15 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return null;
|
||||
}
|
||||
const body = extractBody(quotedMessage);
|
||||
const mediaContent = extractMediaContent(quotedMessage);
|
||||
return {
|
||||
id: contextInfo.stanzaId,
|
||||
participant: toCusFormat(contextInfo.participant),
|
||||
body: body,
|
||||
// Media
|
||||
hasMedia: Boolean(mediaContent),
|
||||
media: null,
|
||||
// Data
|
||||
_data: quotedMessage,
|
||||
};
|
||||
}
|
||||
@@ -2848,9 +2869,12 @@ export class NOWEBEngineMediaProcessor implements IMediaEngineProcessor<any> {
|
||||
content.url = null;
|
||||
}
|
||||
|
||||
return (await downloadMediaMessage(
|
||||
// Use 'stream' mode instead of 'buffer' to fix 0-byte audio files
|
||||
// 'buffer' mode silently returns empty buffer for audio/voice messages
|
||||
// See: https://github.com/devlikeapro/waha/issues/1996
|
||||
const stream = await downloadMediaMessage(
|
||||
message,
|
||||
'buffer',
|
||||
'stream',
|
||||
{},
|
||||
{
|
||||
logger: this.logger,
|
||||
@@ -2859,7 +2883,12 @@ export class NOWEBEngineMediaProcessor implements IMediaEngineProcessor<any> {
|
||||
).finally(() => {
|
||||
// Set url back in case we removed it
|
||||
content.url = url;
|
||||
})) as Buffer;
|
||||
});
|
||||
const chunks: Buffer[] = [];
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
return Buffer.concat(chunks);
|
||||
}
|
||||
|
||||
getFilename(message: any): string | null {
|
||||
|
||||
@@ -596,6 +596,9 @@ export class NowebPersistentStore implements INowebStore {
|
||||
data = await this.messagesRepo.getById(id);
|
||||
} else {
|
||||
data = await this.messagesRepo.getByJidById(jid, id);
|
||||
if (!data) {
|
||||
data = await this.messagesRepo.getById(id);
|
||||
}
|
||||
}
|
||||
if (!data) {
|
||||
return null;
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Expose a function to the page if it does not exist
|
||||
*
|
||||
* NOTE:
|
||||
* Rewrite it to 'upsertFunction' after updating Puppeteer to 20.6 or higher
|
||||
* using page.removeExposedFunction
|
||||
* https://pptr.dev/api/puppeteer.page.removeExposedFunction
|
||||
*
|
||||
* @param {import(puppeteer).Page} page
|
||||
* @param {string} name
|
||||
* @param {Function} fn
|
||||
*/
|
||||
export async function exposeFunctionIfAbsent(page, name, fn) {
|
||||
const exist = await page.evaluate((name) => {
|
||||
return !!window[name];
|
||||
}, name);
|
||||
if (exist) {
|
||||
return;
|
||||
}
|
||||
await page.exposeFunction(name, fn);
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import { exposeFunctionIfAbsent } from '@waha/core/engines/webjs/Puppeteer';
|
||||
import { WebJSPresence } from '@waha/core/engines/webjs/types';
|
||||
import { GetChatMessagesFilter } from '@waha/structures/chats.dto';
|
||||
import { Label } from '@waha/structures/labels.dto';
|
||||
@@ -26,6 +25,11 @@ const { LoadPaginator } = require('./_Paginator.js');
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const ChatFactory = require('whatsapp-web.js/src/factories/ChatFactory');
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const {
|
||||
exposeFunctionIfAbsent,
|
||||
} = require('whatsapp-web.js/src/util/Puppeteer');
|
||||
|
||||
export class WebjsClientCore extends Client {
|
||||
public events = new EventEmitter();
|
||||
private wpage: WPage = null;
|
||||
@@ -300,25 +304,132 @@ export class WebjsClientCore extends Client {
|
||||
|
||||
// @ts-ignore
|
||||
const chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
|
||||
let msgs = chat.msgs.getModelsArray().filter(msgFilter);
|
||||
if (!chat) return [];
|
||||
|
||||
while (msgs.length < pagination.limit + pagination.offset) {
|
||||
const loadedMessages =
|
||||
let msgs = [];
|
||||
|
||||
// Try new WhatsApp API (>= 2.3000.1034162388) which replaced loadEarlierMsgs
|
||||
// @ts-ignore
|
||||
const WAWebDBMessageFindLocal = window.require(
|
||||
'WAWebDBMessageFindLocal',
|
||||
);
|
||||
if (WAWebDBMessageFindLocal?.msgFindByDirection) {
|
||||
const BATCH_SIZE = 20;
|
||||
|
||||
// Construct the initial anchor the same way wa-js does:
|
||||
// serialize to string then reconstruct via MsgKey.fromString so the
|
||||
// object has the exact shape msgFindByDirection expects.
|
||||
const lastReceivedSerialized = chat.lastReceivedKey?._serialized;
|
||||
if (!lastReceivedSerialized) return [];
|
||||
// @ts-ignore
|
||||
let currentAnchorKey = window.Store.MsgKey.fromString(
|
||||
lastReceivedSerialized,
|
||||
);
|
||||
|
||||
// msgFindByDirection is exclusive of the anchor; include the anchor
|
||||
// message itself (the most recent message in the chat) upfront
|
||||
// @ts-ignore
|
||||
const anchorMsg = window.Store.Msg.get(lastReceivedSerialized);
|
||||
if (anchorMsg) {
|
||||
msgs.push(anchorMsg);
|
||||
}
|
||||
|
||||
const neededFiltered = Number.isFinite(
|
||||
pagination.limit + pagination.offset,
|
||||
)
|
||||
? pagination.limit + pagination.offset
|
||||
: Infinity;
|
||||
|
||||
// @ts-ignore
|
||||
const toModel = (m) => {
|
||||
if (m && typeof m.serialize === 'function') return m;
|
||||
const serializedId = m?.id?._serialized;
|
||||
if (serializedId) {
|
||||
// @ts-ignore
|
||||
const stored = window.Store.Msg.get(serializedId);
|
||||
if (stored) return stored;
|
||||
}
|
||||
// @ts-ignore
|
||||
await window.Store.ConversationMsgs.loadEarlierMsgs(chat);
|
||||
if (!loadedMessages || loadedMessages.length == 0) break;
|
||||
return new window.Store.Msg.modelClass(m);
|
||||
};
|
||||
|
||||
msgs = [...loadedMessages.filter(msgFilter), ...msgs];
|
||||
msgs = msgs.sort((a, b) => b.t - a.t);
|
||||
while (true) {
|
||||
const result = await WAWebDBMessageFindLocal.msgFindByDirection({
|
||||
anchor: currentAnchorKey,
|
||||
count: BATCH_SIZE,
|
||||
direction: 'before',
|
||||
});
|
||||
|
||||
// Check if the earliest message is already outside the timerange filter
|
||||
const earliest = msgs[msgs.length - 1];
|
||||
if (earliest.t < (filter['filter.timestamp.gte'] || Infinity)) {
|
||||
// Add only messages that pass the filter and stop loading more
|
||||
break;
|
||||
const batch = Array.isArray(result)
|
||||
? result
|
||||
: result?.messages || [];
|
||||
if (!batch || batch.length === 0) break;
|
||||
|
||||
const batchModels = batch.map(toModel);
|
||||
|
||||
// msgFindByDirection returns newest-first (descending) so prepend
|
||||
// to keep the accumulated list in approximate ascending order
|
||||
msgs = [...batchModels, ...msgs];
|
||||
|
||||
// Deduplicate by serialized id — the same Backbone model object can
|
||||
// appear in multiple batches when anchors overlap
|
||||
const seenIds = new Set();
|
||||
msgs = msgs.filter((m) => {
|
||||
const sid = m?.id?._serialized;
|
||||
if (!sid || seenIds.has(sid)) return false;
|
||||
seenIds.add(sid);
|
||||
return true;
|
||||
});
|
||||
|
||||
// Stop once we have enough messages that pass the filter
|
||||
if (msgs.filter(msgFilter).length >= neededFiltered) break;
|
||||
|
||||
// Stop if the oldest message in this batch is already before the
|
||||
// lower timestamp bound - no earlier messages can be in range
|
||||
if (filter['filter.timestamp.gte'] != null) {
|
||||
const batchMinT = batchModels.reduce(
|
||||
(min, m) => Math.min(min, m.t ?? Infinity),
|
||||
Infinity,
|
||||
);
|
||||
if (batchMinT < filter['filter.timestamp.gte']) break;
|
||||
}
|
||||
|
||||
// No more messages available
|
||||
if (batch.length < BATCH_SIZE) break;
|
||||
|
||||
// msgFindByDirection returns newest-first, so the LAST element is
|
||||
// the oldest message in this batch — use it as the next anchor to
|
||||
// walk further back in history without overlap
|
||||
const oldestInBatch = batchModels[batchModels.length - 1];
|
||||
const oldestSerialized = oldestInBatch?.id?._serialized;
|
||||
if (!oldestSerialized) break;
|
||||
// @ts-ignore
|
||||
currentAnchorKey = window.Store.MsgKey.fromString(oldestSerialized);
|
||||
}
|
||||
} else {
|
||||
// Legacy fallback: loadEarlierMsgs loop
|
||||
msgs = chat.msgs.getModelsArray();
|
||||
while (msgs.length < pagination.limit + pagination.offset) {
|
||||
const loadedMessages =
|
||||
// @ts-ignore
|
||||
await window.Store.ConversationMsgs.loadEarlierMsgs(
|
||||
chat,
|
||||
chat.msgs,
|
||||
);
|
||||
if (!loadedMessages || loadedMessages.length == 0) break;
|
||||
|
||||
msgs = [...loadedMessages, ...msgs];
|
||||
msgs = msgs.sort((a, b) => b.t - a.t);
|
||||
|
||||
const earliest = msgs[msgs.length - 1];
|
||||
if (earliest.t < (filter['filter.timestamp.gte'] || Infinity)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
msgs = msgs.filter(msgFilter);
|
||||
|
||||
// Always sort newest first before applying the pagination window.
|
||||
msgs = msgs.sort((a, b) => b.t - a.t);
|
||||
|
||||
|
||||
@@ -173,8 +173,10 @@ import {
|
||||
|
||||
import { WAJSPresenceChatStateType, WebJSPresence } from './types';
|
||||
import {
|
||||
isJidCus,
|
||||
isJidGroup,
|
||||
isJidStatusBroadcast,
|
||||
isLidUser,
|
||||
normalizeJid,
|
||||
toCusFormat,
|
||||
} from '@waha/core/utils/jids';
|
||||
@@ -205,6 +207,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
private engineStateCheckDelayedJob: SingleDelayedJobRunner;
|
||||
private shouldRestart: boolean;
|
||||
private lastQRDate: Date = null;
|
||||
private static readonly REACTION_MAX_AGE_MS = 2 * 24 * 60 * 60 * 1000;
|
||||
|
||||
whatsapp: WebjsClientCore;
|
||||
protected qr: QR;
|
||||
@@ -1028,7 +1031,50 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
messageId: string,
|
||||
query: GetChatMessageQuery,
|
||||
): Promise<null | WAMessage> {
|
||||
const message = await this.whatsapp.getMessageById(messageId);
|
||||
chatId = this.ensureSuffix(chatId);
|
||||
|
||||
// WEBJS waits the serializer messageId
|
||||
// {fromMe}_{chatId}_{id}[_{participant}]
|
||||
if (isJidStatusBroadcast(chatId) && !messageId.includes('_')) {
|
||||
// For status - resolve it as "my" if no details provided
|
||||
const me = this.getSessionMeInfo();
|
||||
const lid = me.lid || (await this.whatsapp.findLIDByPhoneNumber(me.id));
|
||||
messageId = SerializeMessageKey({
|
||||
fromMe: true,
|
||||
id: messageId,
|
||||
remoteJid: Jid.BROADCAST,
|
||||
participant: lid,
|
||||
});
|
||||
}
|
||||
|
||||
let message: Message | null = null;
|
||||
if (isLidUser(chatId) || isJidCus(chatId)) {
|
||||
// If in DM chats and not fromMe specified - try both fromMe and not
|
||||
if (!messageId.includes('_')) {
|
||||
// FromMe - true
|
||||
if (!message) {
|
||||
const id = SerializeMessageKey({
|
||||
fromMe: true,
|
||||
id: messageId,
|
||||
remoteJid: chatId,
|
||||
});
|
||||
message = await this.whatsapp.getMessageById(id);
|
||||
}
|
||||
// FromMe - false
|
||||
if (!message) {
|
||||
const id = SerializeMessageKey({
|
||||
fromMe: false,
|
||||
id: messageId,
|
||||
remoteJid: chatId,
|
||||
});
|
||||
message = await this.whatsapp.getMessageById(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!message) {
|
||||
message = await this.whatsapp.getMessageById(messageId);
|
||||
}
|
||||
if (!message) return null;
|
||||
if (
|
||||
isJidGroup(message.id.remote) ||
|
||||
@@ -1946,6 +1992,12 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
const media = await this.downloadMediaSafe(message);
|
||||
wamessage.media = media;
|
||||
}
|
||||
if (downloadMedia && wamessage.replyTo?.hasMedia) {
|
||||
const quotedMessage = await message.getQuotedMessage().catch(() => null);
|
||||
if (quotedMessage) {
|
||||
wamessage.replyTo.media = await this.downloadMediaSafe(quotedMessage);
|
||||
}
|
||||
}
|
||||
return wamessage;
|
||||
}
|
||||
|
||||
@@ -1975,6 +2027,14 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore reactions older than 2 days to prevent stale reactions
|
||||
// when session reconnects and replays buffered events
|
||||
const twoDaysAgoSec =
|
||||
(Date.now() - WhatsappSessionWebJSCore.REACTION_MAX_AGE_MS) / 1000;
|
||||
if (reaction.timestamp < twoDaysAgoSec) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const source = this.getMessageSource(reaction.id.id);
|
||||
return {
|
||||
id: reaction.id._serialized,
|
||||
@@ -2100,15 +2160,21 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
}
|
||||
|
||||
protected extractReplyTo(message: Message): ReplyToMessage | null {
|
||||
const rawData: any = message.rawData;
|
||||
// @ts-ignore
|
||||
const quotedMsg = message.rawData?.quotedMsg;
|
||||
if (!quotedMsg) {
|
||||
const quotedMsg = rawData?.quotedMsg;
|
||||
const quotedStanzaId = rawData?.quotedStanzaID || rawData?.quotedStanzaId;
|
||||
if (!quotedMsg && !quotedStanzaId) {
|
||||
return;
|
||||
}
|
||||
const quotedParticipant =
|
||||
rawData?.quotedParticipant || quotedMsg?.author || quotedMsg?.from;
|
||||
return {
|
||||
id: quotedMsg.id?.id,
|
||||
participant: quotedMsg.author || quotedMsg.from,
|
||||
body: quotedMsg.caption || quotedMsg.body,
|
||||
id: quotedStanzaId || quotedMsg?.id?.id,
|
||||
participant: quotedParticipant,
|
||||
body: quotedMsg?.caption || quotedMsg?.body,
|
||||
hasMedia: Boolean(quotedMsg?.directPath),
|
||||
media: null,
|
||||
_data: quotedMsg,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1091,7 +1091,7 @@ export class WhatsappSessionWPPCore extends WhatsappSession {
|
||||
//
|
||||
|
||||
@Activity()
|
||||
public createGroup(request: CreateGroupRequest) {
|
||||
public createGroup(request: CreateGroupRequest): Promise<any> {
|
||||
const participants = request.participants.map((participant) =>
|
||||
this.ensureSuffix(participant.id),
|
||||
);
|
||||
@@ -2089,12 +2089,15 @@ export class WhatsappSessionWPPCore extends WhatsappSession {
|
||||
quotedMessage?.author ||
|
||||
quotedMessage?.from ||
|
||||
null;
|
||||
const hasMedia = getHasMedia(quotedMessage);
|
||||
return {
|
||||
id: quotedMessageId,
|
||||
participant: toCusFormat(
|
||||
Deserialized(quotedParticipant) || quotedParticipant,
|
||||
),
|
||||
body: getMessageBody(quotedMessage),
|
||||
hasMedia: hasMedia,
|
||||
media: null,
|
||||
_data: quotedMessage,
|
||||
};
|
||||
}
|
||||
@@ -2105,6 +2108,12 @@ export class WhatsappSessionWPPCore extends WhatsappSession {
|
||||
const media = await this.downloadMediaSafe(message);
|
||||
wamessage.media = media;
|
||||
}
|
||||
if (downloadMedia && wamessage.replyTo?.hasMedia) {
|
||||
const quotedMessage = message?.quotedMsg || message?._data?.quotedMsg;
|
||||
if (quotedMessage) {
|
||||
wamessage.replyTo.media = await this.downloadMediaSafe(quotedMessage);
|
||||
}
|
||||
}
|
||||
return wamessage;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ export class MediaManager implements IMediaManager {
|
||||
// https://github.com/IndigoUnited/node-promise-retry
|
||||
RETRY_OPTIONS = {
|
||||
retries: 5,
|
||||
minTimeout: 100,
|
||||
maxTimeout: 500,
|
||||
minTimeout: 1000,
|
||||
maxTimeout: 3000,
|
||||
};
|
||||
|
||||
constructor(
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as path from 'path';
|
||||
import { Logger } from 'pino';
|
||||
import fs = require('fs');
|
||||
import { fileExists } from '@waha/utils/files';
|
||||
import { deleteAsync } from 'del';
|
||||
import { setLongTimeout } from '@waha/utils/promiseTimeout';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const writeFileAtomic = require('write-file-atomic');
|
||||
@@ -58,16 +58,33 @@ export class MediaLocalStorage implements IMediaStorage {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fs.existsSync(this.filesFolder)) {
|
||||
deleteAsync([`${this.filesFolder}/*`], { force: true }).then((paths) => {
|
||||
if (paths.length === 0) {
|
||||
return;
|
||||
}
|
||||
this.log.info('Deleted files and directories:\n', paths.join('\n'));
|
||||
});
|
||||
} else {
|
||||
if (!fs.existsSync(this.filesFolder)) {
|
||||
fs.mkdirSync(this.filesFolder);
|
||||
this.log.info(`Directory '${this.filesFolder}' created from scratch`);
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const entries = await fsp.readdir(this.filesFolder, { recursive: true });
|
||||
for (const entry of entries) {
|
||||
try {
|
||||
const filepath = path.join(this.filesFolder, entry as string);
|
||||
const stat = await fsp.stat(filepath);
|
||||
if (!stat.isFile()) {
|
||||
continue;
|
||||
}
|
||||
// Schedule removal:
|
||||
// - expired files get a random jitter to spread deletions out,
|
||||
// - non-expired files are scheduled for the time they have left.
|
||||
const age = now - stat.mtimeMs;
|
||||
const expired = age >= this.lifetimeMs;
|
||||
const left = this.lifetimeMs - age;
|
||||
const jitter = Math.random() * 10 * SECOND;
|
||||
const remaining = expired ? jitter : left;
|
||||
this.postponeRemoval(filepath, remaining);
|
||||
} catch (err) {
|
||||
this.log.warn(`Failed to schedule removal for file ${entry}`, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,16 +97,17 @@ export class MediaLocalStorage implements IMediaStorage {
|
||||
return path.resolve(`${this.filesFolder}/${filepath}`);
|
||||
}
|
||||
|
||||
private postponeRemoval(filepath: string) {
|
||||
private postponeRemoval(filepath: string, delayMs: number | null = null) {
|
||||
if (this.lifetimeMs === 0) {
|
||||
return;
|
||||
}
|
||||
setTimeout(
|
||||
const delay = delayMs ?? this.lifetimeMs;
|
||||
setLongTimeout(
|
||||
() =>
|
||||
fs.unlink(filepath, () => {
|
||||
this.log.info(`File ${filepath} was removed`);
|
||||
this.log.debug(`File ${filepath} was removed`);
|
||||
}),
|
||||
this.lifetimeMs,
|
||||
delay,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import { normalizeMessageContent, proto } from '@adiwajshing/baileys';
|
||||
import esm from '@waha/vendor/esm';
|
||||
|
||||
export function IsEditedMessage(message: proto.IMessage): boolean {
|
||||
message = normalizeMessageContent(message);
|
||||
@@ -37,3 +38,11 @@ export function IsHistorySyncNotification(message: proto.IMessage): boolean {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getContextInfo(
|
||||
protoMessage: proto.Message | null,
|
||||
): proto.IContextInfo | null {
|
||||
const type = esm.b.getContentType(protoMessage);
|
||||
const message = protoMessage[type] as any;
|
||||
return message?.contextInfo;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
ChatIdProperty,
|
||||
MessageIdOnlyProperty,
|
||||
} from '@waha/structures/properties.dto';
|
||||
import { WAMedia } from '@waha/structures/media.dto';
|
||||
|
||||
export class ReplyToMessage {
|
||||
@MessageIdOnlyProperty()
|
||||
@@ -16,6 +17,16 @@ export class ReplyToMessage {
|
||||
})
|
||||
body?: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Indicates if the message has media available for download',
|
||||
})
|
||||
hasMedia: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Media object for the message if any and downloaded',
|
||||
})
|
||||
media?: WAMedia;
|
||||
|
||||
@ApiProperty({
|
||||
description: "Raw data from reply's message",
|
||||
})
|
||||
|
||||
@@ -53,3 +53,16 @@ export async function waitUntil(
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle setTimeout 32-bit overflow
|
||||
*/
|
||||
export function setLongTimeout(callback, delayMs) {
|
||||
const MAX_DELAY = 2_147_483_647; // ~24.8 days
|
||||
|
||||
if (delayMs > MAX_DELAY) {
|
||||
setTimeout(() => setLongTimeout(callback, delayMs - MAX_DELAY), MAX_DELAY);
|
||||
} else {
|
||||
setTimeout(callback, delayMs);
|
||||
}
|
||||
}
|
||||
+8
-3
@@ -1,7 +1,10 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
import { getEngineName } from '@waha/config';
|
||||
|
||||
import { getBrowserExecutablePath } from './core/abc/session.abc';
|
||||
import {
|
||||
getBrowserExecutablePath,
|
||||
isChromeExecutablePath,
|
||||
} from './core/abc/session.browser';
|
||||
import { WAHAEngine } from './structures/enums.dto';
|
||||
import { WAHAEnvironment } from './structures/environment.dto';
|
||||
|
||||
@@ -45,7 +48,7 @@ function getPlatform() {
|
||||
}
|
||||
|
||||
export const VERSION: WAHAEnvironment = {
|
||||
version: '2026.3.4',
|
||||
version: '2026.4.1',
|
||||
engine: getEngineName(),
|
||||
tier: getWAHAVersion(),
|
||||
browser: getBrowser(),
|
||||
@@ -53,6 +56,8 @@ export const VERSION: WAHAEnvironment = {
|
||||
worker: getWorker(),
|
||||
};
|
||||
|
||||
export const IsChrome = VERSION.browser?.includes('chrome');
|
||||
export const IsChrome = VERSION.browser
|
||||
? isChromeExecutablePath(VERSION.browser)
|
||||
: false;
|
||||
|
||||
export { getEngineName };
|
||||
+2
-2
@@ -2,11 +2,11 @@
|
||||
"waha": {
|
||||
"gows": {
|
||||
"repo": "devlikeapro/gows",
|
||||
"ref": "v1.0.36"
|
||||
"ref": "v1.0.37"
|
||||
},
|
||||
"dashboard": {
|
||||
"repo": "devlikeapro/dashboard",
|
||||
"ref": "24d3aca7113688eb2abaad9ff3716cdff540ae59"
|
||||
"ref": "968c44f1f9a607bd04dc0133eb0a40db3a1a28ec"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ __metadata:
|
||||
|
||||
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2026-02-11":
|
||||
version: 7.0.0-rc.9
|
||||
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=555d5cc1826d49d10eaf7254ff6b9c5d64d106cc"
|
||||
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=92a9f60f6ab1eeb93ee9957e536f2e355f859189"
|
||||
dependencies:
|
||||
"@cacheable/node-cache": "npm:^1.4.0"
|
||||
"@hapi/boom": "npm:^9.1.3"
|
||||
@@ -32,7 +32,7 @@ __metadata:
|
||||
optional: true
|
||||
link-preview-js:
|
||||
optional: true
|
||||
checksum: 10/94b18c0b49ab75248438067be08c73c0dd37f628be99a025ea74f6d08aa443c9719bf6975d0409dcb9034e248521cf8425d043abe941b9cf3bd2844852ff5845
|
||||
checksum: 10/0ff00980c1abf919de39170082677b71a774b1b4dc1d748556fb696d67ed68540b0d5c018ba6bfbfae4b7af6eea0e8c705f06b44248538ce0e1016fd35e676e3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -4442,6 +4442,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@wppconnect/wa-js@npm:^3.23.3":
|
||||
version: 3.23.3
|
||||
resolution: "@wppconnect/wa-js@npm:3.23.3"
|
||||
checksum: 10/8b4407f3f90f781f63800b1023943ff1978ced614c4f9b7075e6470d5feac8176efe25a44b327234c5d2d265b2cf4f490c3fa09dea63720a94be6489cf98cd30
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@wppconnect/wa-version@npm:^1.5.3379":
|
||||
version: 1.5.3523
|
||||
resolution: "@wppconnect/wa-version@npm:1.5.3523"
|
||||
@@ -13547,6 +13554,7 @@ __metadata:
|
||||
"@types/user-agents": "npm:^1"
|
||||
"@types/ws": "npm:^8.5.4"
|
||||
"@wppconnect-team/wppconnect": "npm:^1.41.1"
|
||||
"@wppconnect/wa-js": "npm:^3.23.3"
|
||||
adm-zip: "npm:0.5.10"
|
||||
agentkeepalive: "npm:^4.5.0"
|
||||
async-lock: "npm:^1.4.1"
|
||||
@@ -13725,7 +13733,7 @@ __metadata:
|
||||
|
||||
"whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-2026-02-18":
|
||||
version: 1.34.6
|
||||
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=3b8999cec79a37bf4d3aea62988db175f73eb19f"
|
||||
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=d9f107887470bd4349cd9f639fb9cb1d63027efc"
|
||||
dependencies:
|
||||
archiver: "npm:^5.3.1"
|
||||
fluent-ffmpeg: "npm:2.1.3"
|
||||
@@ -13742,7 +13750,7 @@ __metadata:
|
||||
optional: true
|
||||
unzipper:
|
||||
optional: true
|
||||
checksum: 10/e67e8b5910bf2cc5fb34e1167b334598a86a4f2d3e9b462647687f3befb8fbae069814fbb6f9dbbd6bfb062b4ebb952bb6cb98cc5f85bd31a0ac2cc9b98521e3
|
||||
checksum: 10/705f16bd7c70afae7d8a76fbfc640fbb2be7d8d99695a694f0953507716c65cbbd44f68dcf5298bc5d61fa8cc55dd5d2ec9bc3d1cfc51281a35723b936c0994c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in new issue
Block a user