Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57f6db4252 | ||
|
|
68a91cb55c | ||
|
|
869a343e43 | ||
|
|
fd2b22b339 | ||
|
|
90d3a5b1ad | ||
|
|
b4a2ad6b96 | ||
|
|
27ff7e7a21 | ||
|
|
94ae624662 | ||
|
|
52e018aa93 | ||
|
|
8bd13a233e | ||
|
|
87f79c1dc5 | ||
|
|
31533b5d19 | ||
|
|
27cc9f4764 | ||
|
|
4fdfce3944 | ||
|
|
1e12a83cdf | ||
|
|
1c41b8be65 | ||
|
|
f72ee7c1f8 | ||
|
|
e75912435b | ||
|
|
da294a58a6 | ||
|
|
7689199e11 | ||
|
|
324ab11f09 | ||
|
|
b1f2e17e3d | ||
|
|
90822cd582 | ||
|
|
9eec72b641 | ||
|
|
050c652480 |
No files matched your search
@@ -10,6 +10,8 @@ docs
|
||||
examples
|
||||
.github
|
||||
.git
|
||||
.envs/*
|
||||
.env
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
src/core/engines/gows/proto
|
||||
.env
|
||||
.envs/*
|
||||
*.heapsnapshot
|
||||
.yarn
|
||||
example.ts
|
||||
|
||||
+17
@@ -163,6 +163,7 @@ RUN if [ "$USE_BROWSER" = "chromium" ]; then \
|
||||
# Available versions:
|
||||
# https://www.ubuntuupdates.org/package/google_chrome/stable/main/base/google-chrome-stable
|
||||
ARG CHROME_VERSION="140.0.7339.80-1"
|
||||
ARG OPUSTAGS_VERSION="1.10.1"
|
||||
RUN if [ "$USE_BROWSER" = "chrome" ]; then \
|
||||
wget --no-verbose -O /tmp/chrome.deb https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${CHROME_VERSION}_amd64.deb \
|
||||
&& apt-get update \
|
||||
@@ -176,6 +177,22 @@ RUN apt-get update \
|
||||
&& apt-get install -y curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Build and install opustags so audio metadata can be cleaned up inside the container
|
||||
RUN set -eux; \
|
||||
buildDeps='build-essential cmake pkg-config libogg-dev'; \
|
||||
apt-get update; \
|
||||
apt-get install -y --no-install-recommends ${buildDeps}; \
|
||||
mkdir -p /tmp/opustags; \
|
||||
curl -L https://github.com/fmang/opustags/archive/refs/tags/${OPUSTAGS_VERSION}.tar.gz \
|
||||
| tar -xz -C /tmp/opustags; \
|
||||
cd /tmp/opustags/opustags-${OPUSTAGS_VERSION}; \
|
||||
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release; \
|
||||
cmake --build build --config Release; \
|
||||
cmake --install build; \
|
||||
rm -rf /tmp/opustags; \
|
||||
apt-get purge -y --auto-remove ${buildDeps}; \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# GOWS requirements
|
||||
# libc6
|
||||
RUN apt-get update \
|
||||
|
||||
@@ -35,13 +35,13 @@ for-swagger:
|
||||
WHATSAPP_SWAGGER_CONFIG_ADVANCED=true . ${NVM_DIR}/nvm.sh && nvm exec yarn start
|
||||
|
||||
up-noweb:
|
||||
yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-10-19
|
||||
yarn up @adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-11-07
|
||||
|
||||
up-noweb-libsignal:
|
||||
yarn up libsignal@github:devlikeapro/libsignal-node#fork-master
|
||||
|
||||
up-webjs:
|
||||
yarn up whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-2025-09-10
|
||||
yarn up whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-2025-11-05
|
||||
|
||||
start-proxy:
|
||||
docker run --rm -d --name squid-container -e TZ=UTC -p 3128:3128 ubuntu/squid:5.2-22.04_beta
|
||||
|
||||
+4
-3
@@ -26,7 +26,7 @@
|
||||
"gows:proto": "yarn gows:proto:fetch && yarn gows:proto:build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-10-19",
|
||||
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-11-07",
|
||||
"@adiwajshing/keyed-db": "^0.2.4",
|
||||
"@aws-sdk/client-s3": "^3.633.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.633.0",
|
||||
@@ -59,7 +59,7 @@
|
||||
"audio-decode": "^2.2.2",
|
||||
"axios": "^1.9.0",
|
||||
"axios-retry": "^4.5.0",
|
||||
"better-sqlite3": "11.3.0",
|
||||
"better-sqlite3": "^12.4.1",
|
||||
"bullmq": "^5.48.1",
|
||||
"check-disk-space": "^3.4.0",
|
||||
"chokidar": "^3.6.0",
|
||||
@@ -106,7 +106,7 @@
|
||||
"swagger-ui-express": "^4.1.4",
|
||||
"ulid": "^2.3.0",
|
||||
"undici": "^7.16.0",
|
||||
"whatsapp-web.js": "github:devlikeapro/whatsapp-web.js#fork-main-2025-09-10",
|
||||
"whatsapp-web.js": "github:devlikeapro/whatsapp-web.js#fork-main-2025-11-05",
|
||||
"write-file-atomic": "^6.0.0",
|
||||
"yaml": "^2.7.1"
|
||||
},
|
||||
@@ -132,6 +132,7 @@
|
||||
"@types/node-fetch": "^2.6.13",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"dotenv": "^17.2.3",
|
||||
"grpc-tools": "^1.13.0",
|
||||
"jest": "^29.7.0",
|
||||
"oxlint": "^1.13.0",
|
||||
|
||||
@@ -122,6 +122,9 @@ class ChatsController {
|
||||
@WorkingSessionParam session: WhatsappSession,
|
||||
@Param('chatId') chatId: string,
|
||||
) {
|
||||
if (query.sortBy == MessageSortField.MESSAGE_TIMESTAMP) {
|
||||
query.sortBy = MessageSortField.TIMESTAMP;
|
||||
}
|
||||
query.sortBy = query.sortBy || MessageSortField.TIMESTAMP;
|
||||
query.sortOrder = query.sortOrder || SortOrder.DESC;
|
||||
filter = transformAck(filter);
|
||||
|
||||
@@ -44,6 +44,10 @@ import {
|
||||
WANumberExistResult,
|
||||
} from '../structures/chatting.dto';
|
||||
import { WAMessage } from '../structures/responses.dto';
|
||||
import {
|
||||
mentionsAll,
|
||||
validateRequestMentions,
|
||||
} from '@waha/core/utils/mentions.all';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api')
|
||||
@@ -55,6 +59,10 @@ export class ChattingController {
|
||||
@ApiOperation({ summary: 'Send a text message' })
|
||||
async sendText(@Body() request: MessageTextRequest): Promise<WAMessage> {
|
||||
const whatsapp = await this.manager.getWorkingSession(request.session);
|
||||
if (mentionsAll(request)) {
|
||||
validateRequestMentions(request);
|
||||
request.mentions = await whatsapp.resolveMentionsAll(request.chatId);
|
||||
}
|
||||
return whatsapp.sendText(request);
|
||||
}
|
||||
|
||||
@@ -66,6 +74,10 @@ export class ChattingController {
|
||||
})
|
||||
async sendImage(@Body() request: MessageImageRequest) {
|
||||
const whatsapp = await this.manager.getWorkingSession(request.session);
|
||||
if (mentionsAll(request)) {
|
||||
validateRequestMentions(request);
|
||||
request.mentions = await whatsapp.resolveMentionsAll(request.chatId);
|
||||
}
|
||||
return whatsapp.sendImage(request);
|
||||
}
|
||||
|
||||
@@ -77,6 +89,10 @@ export class ChattingController {
|
||||
})
|
||||
async sendFile(@Body() request: MessageFileRequest) {
|
||||
const whatsapp = await this.manager.getWorkingSession(request.session);
|
||||
if (mentionsAll(request)) {
|
||||
validateRequestMentions(request);
|
||||
request.mentions = await whatsapp.resolveMentionsAll(request.chatId);
|
||||
}
|
||||
return whatsapp.sendFile(request);
|
||||
}
|
||||
|
||||
@@ -99,6 +115,10 @@ export class ChattingController {
|
||||
})
|
||||
async sendVideo(@Body() request: MessageVideoRequest) {
|
||||
const whatsapp = await this.manager.getWorkingSession(request.session);
|
||||
if (mentionsAll(request)) {
|
||||
validateRequestMentions(request);
|
||||
request.mentions = await whatsapp.resolveMentionsAll(request.chatId);
|
||||
}
|
||||
return whatsapp.sendVideo(request);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
CreateGroupRequest,
|
||||
DescriptionRequest,
|
||||
GroupField,
|
||||
GroupParticipant,
|
||||
GroupsListFields,
|
||||
GroupsPaginationParams,
|
||||
JoinGroupRequest,
|
||||
@@ -320,6 +321,17 @@ export class GroupsController {
|
||||
return session.getParticipants(id);
|
||||
}
|
||||
|
||||
@Get(':id/participants/v2')
|
||||
@GroupIdApiParam
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Get group participants.' })
|
||||
getGroupParticipants(
|
||||
@WorkingSessionParam session: WhatsappSession,
|
||||
@Param('id') id: string,
|
||||
): Promise<GroupParticipant[]> {
|
||||
return session.getGroupParticipants(id);
|
||||
}
|
||||
|
||||
@Post(':id/participants/add')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@SessionApiParam
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Controller, Get } from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
import { PingResponse } from '@waha/structures/ping.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('ping')
|
||||
@ApiTags('🔍 Observability')
|
||||
export class PingController {
|
||||
|
||||
@@ -51,6 +51,17 @@ export function HasBeenRetried(job: Job): boolean {
|
||||
return attemptsMade > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide chatwoot and waha from queue name for user faced messages
|
||||
*/
|
||||
export function QueueNameRepr(name: string): string {
|
||||
// No "chatwoot" in the name always (it start with chatwoot)
|
||||
name = name.replace('chatwoot.', '');
|
||||
// Replace "waha" with "whatsapp"
|
||||
name = name.replace('waha |', 'whatsapp |');
|
||||
return name;
|
||||
}
|
||||
|
||||
let base =
|
||||
process.env.WAHA_PUBLIC_URL ||
|
||||
process.env.WAHA_BASE_URL ||
|
||||
@@ -59,7 +70,10 @@ let base =
|
||||
base = base.replace(/\/+$/, '');
|
||||
|
||||
export function JobLink(job: Job): { text: string; url: string } {
|
||||
const text = `${job.queueName} => ${job.id}`;
|
||||
// Use repr name for text
|
||||
const name = QueueNameRepr(job.queueName);
|
||||
const text = `${name} => ${job.id}`;
|
||||
// Use original queue name in the URL
|
||||
const queue = encodeURIComponent(job.queueName);
|
||||
const id = encodeURIComponent(job.id);
|
||||
const url = `${base}/jobs/queue/${queue}/${id}`;
|
||||
|
||||
@@ -3,14 +3,7 @@ import { QueueManager } from '@waha/apps/chatwoot/services/QueueManager';
|
||||
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
|
||||
import { Conversation } from '@waha/apps/chatwoot/client/Conversation';
|
||||
import { QueueRegistry } from '@waha/apps/chatwoot/services/QueueRegistry';
|
||||
|
||||
function repr(name: string): string {
|
||||
// No chatwoot in the name always (it start with chatwoot)
|
||||
name = name.replace('chatwoot.', '');
|
||||
// No waha in the name
|
||||
name = name.replace('waha |', 'whatsapp |');
|
||||
return name;
|
||||
}
|
||||
import { QueueNameRepr } from '@waha/apps/app_sdk/JobUtils';
|
||||
|
||||
export interface QueueCommandContext {
|
||||
queues: {
|
||||
@@ -25,7 +18,7 @@ export async function QueueStatus(ctx: QueueCommandContext, name: string) {
|
||||
const names = manager.resolve(name);
|
||||
let result = await manager.status(names);
|
||||
for (const status of result) {
|
||||
status.name = repr(status.name);
|
||||
status.name = QueueNameRepr(status.name);
|
||||
}
|
||||
// locked: true - last
|
||||
result = lodash.sortBy(result, [(x) => !!x.locked, 'name']);
|
||||
|
||||
@@ -77,7 +77,7 @@ export class MessageAnyHandler extends MessageBaseHandler<WAMessage> {
|
||||
): Promise<ChatWootMessagePartial> {
|
||||
let converter: MessageToChatWootConverter;
|
||||
let msg: ChatWootMessagePartial;
|
||||
const protoMessage = resolveProtoMessage(payload);
|
||||
const protoMessage = resolveProtoMessage(payload._data);
|
||||
|
||||
// Check for Facebook Ad first - but let it use the normal flow later
|
||||
converter = new FacebookAdMessage(this.l, this.logger);
|
||||
|
||||
@@ -65,7 +65,7 @@ class MessageEditedHandler extends MessageBaseHandler<WAMessageEditedBody> {
|
||||
protected async getMessage(
|
||||
payload: WAMessageEditedBody,
|
||||
): Promise<ChatWootMessagePartial> {
|
||||
const protoMessage = resolveProtoMessage(payload);
|
||||
const protoMessage = resolveProtoMessage(payload._data);
|
||||
const converter: MessageToChatWootConverter = new MessageEdited(this.l);
|
||||
return converter.convert(payload, protoMessage);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
|
||||
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
|
||||
import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base';
|
||||
import { isEmptyString } from './utils/proto';
|
||||
import { isEmptyString } from './utils/text';
|
||||
import * as lodash from 'lodash';
|
||||
import type { proto } from '@adiwajshing/baileys';
|
||||
import { WAMessage } from '@waha/structures/responses.dto';
|
||||
|
||||
@@ -2,7 +2,7 @@ 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 { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chatwoot';
|
||||
import { isEmptyString } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/proto';
|
||||
import { isEmptyString } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/text';
|
||||
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/markdown';
|
||||
import type { proto } from '@adiwajshing/baileys';
|
||||
import { WAMessage } from '@waha/structures/responses.dto';
|
||||
|
||||
@@ -2,7 +2,7 @@ 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 { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chatwoot';
|
||||
import { isEmptyString } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/proto';
|
||||
import { isEmptyString } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/text';
|
||||
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/markdown';
|
||||
import * as lodash from 'lodash';
|
||||
import type { proto } from '@adiwajshing/baileys';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
|
||||
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
|
||||
import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base';
|
||||
import { isEmptyString } from './utils/proto';
|
||||
import { isEmptyString } from './utils/text';
|
||||
import * as lodash from 'lodash';
|
||||
import type { proto } from '@adiwajshing/baileys';
|
||||
import { WAMessage } from '@waha/structures/responses.dto';
|
||||
|
||||
@@ -103,7 +103,7 @@ describe('PixMessage - pt-BR', () => {
|
||||
|
||||
// Test
|
||||
const logger = JestLogger();
|
||||
const protoMessage = resolveProtoMessage(payload);
|
||||
const protoMessage = resolveProtoMessage(payload._data);
|
||||
const converter = new PixMessage(locale, logger);
|
||||
const result = converter.convert(payload, protoMessage);
|
||||
|
||||
@@ -222,7 +222,7 @@ describe('PixMessage - pt-BR', () => {
|
||||
|
||||
// Test
|
||||
const logger = JestLogger();
|
||||
const protoMessage = resolveProtoMessage(payload);
|
||||
const protoMessage = resolveProtoMessage(payload._data);
|
||||
const converter = new PixMessage(locale, logger);
|
||||
const result = converter.convert(payload, protoMessage);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base'
|
||||
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
|
||||
import { PixTemplatePayload, TKey } from '@waha/apps/chatwoot/i18n/templates';
|
||||
import { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chatwoot';
|
||||
import { isEmptyString } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/proto';
|
||||
import { isEmptyString } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/text';
|
||||
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/markdown';
|
||||
import * as lodash from 'lodash';
|
||||
import type { proto } from '@adiwajshing/baileys';
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base'
|
||||
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
|
||||
import { PollCreationMessage, TKey } from '@waha/apps/chatwoot/i18n/templates';
|
||||
import { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chatwoot';
|
||||
import { isEmptyString } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/proto';
|
||||
import { isEmptyString } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/text';
|
||||
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/markdown';
|
||||
import type { proto } from '@adiwajshing/baileys';
|
||||
import { WAMessage } from '@waha/structures/responses.dto';
|
||||
|
||||
@@ -4,7 +4,7 @@ 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 { WAHASelf } from '@waha/apps/app_sdk/waha/WAHASelf';
|
||||
import { isEmptyString } from './utils/proto';
|
||||
import { isEmptyString } from './utils/text';
|
||||
import type { proto } from '@adiwajshing/baileys';
|
||||
import { WAMessage } from '@waha/structures/responses.dto';
|
||||
import { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chatwoot';
|
||||
|
||||
@@ -2,7 +2,6 @@ import { WAMessage } from '@waha/structures/responses.dto';
|
||||
import type { proto } from '@adiwajshing/baileys';
|
||||
import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base';
|
||||
|
||||
export { resolveProtoMessage } from './utils/proto';
|
||||
export { TextMessage } from './TextMessage';
|
||||
export { LocationMessage } from './LocationMessage';
|
||||
export { ShareContactMessage } from './ShareContactMessage';
|
||||
@@ -23,3 +22,4 @@ export interface MessageToChatWootConverter {
|
||||
protoMessage: proto.Message | null,
|
||||
): Awaitable<ChatWootMessagePartial | null>;
|
||||
}
|
||||
export { resolveProtoMessage } from '@waha/core/engines/gows/waproto';
|
||||
@@ -0,0 +1,6 @@
|
||||
export function isEmptyString(content: string) {
|
||||
if (!content) {
|
||||
return true;
|
||||
}
|
||||
return content === '' || content === '\n';
|
||||
}
|
||||
@@ -148,7 +148,12 @@ export class WhatsappConfigService implements OnApplicationBootstrap {
|
||||
if (!value) {
|
||||
return [];
|
||||
}
|
||||
return value.split(',');
|
||||
return value.split(',').filter(Boolean);
|
||||
}
|
||||
|
||||
getExcludedFullPaths(): string[] {
|
||||
const paths = this.getExcludedPaths();
|
||||
return paths.map((path) => (path.startsWith('/') ? path : `/${path}`));
|
||||
}
|
||||
|
||||
getHealthMediaFilesThreshold(): number {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import * as lodash from 'lodash';
|
||||
import { DocumentBuilder, OpenAPIObject, SwaggerModule } from '@nestjs/swagger';
|
||||
import { DECORATORS } from '@nestjs/swagger/dist/constants';
|
||||
import { BasicAuthFunction } from '@waha/core/auth/basicAuth';
|
||||
@@ -185,13 +186,17 @@ export class SwaggerConfiguratorCore {
|
||||
setUpAuth(credentials: [string, string]): void {
|
||||
const [username, password] = credentials;
|
||||
const dashboardConfig = this.app.get(DashboardConfigServiceCore);
|
||||
const exclude = [
|
||||
const config = this.app.get(WhatsappConfigService);
|
||||
const exclude = lodash.uniq([
|
||||
'/api/',
|
||||
dashboardConfig.dashboardUri,
|
||||
'/health',
|
||||
'/ping',
|
||||
'/ws',
|
||||
'/webhooks/',
|
||||
];
|
||||
...config.getExcludedFullPaths(),
|
||||
]);
|
||||
|
||||
const authFunction = BasicAuthFunction(username, password, exclude);
|
||||
this.app.use(authFunction);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { WhatsappSession } from '@waha/core/abc/session.abc';
|
||||
|
||||
/**
|
||||
* Decorator to mark a method as an activity that
|
||||
* keeps the WhatsApp session online.
|
||||
* @constructor
|
||||
*/
|
||||
export function Activity() {
|
||||
return function <T extends (...args: any[]) => Promise<any>>(
|
||||
target: WhatsappSession,
|
||||
propertyKey: string,
|
||||
descriptor: TypedPropertyDescriptor<T>,
|
||||
) {
|
||||
const original = descriptor.value!;
|
||||
descriptor.value = async function (
|
||||
this: WhatsappSession,
|
||||
...args: Parameters<T>
|
||||
): Promise<ReturnType<T>> {
|
||||
await this.maintainPresenceOnline();
|
||||
return await original.apply(this, args);
|
||||
} as T;
|
||||
|
||||
return descriptor;
|
||||
};
|
||||
}
|
||||
+129
-2
@@ -6,6 +6,7 @@ import { MessagesForRead } from '@waha/core/utils/convertors';
|
||||
import {
|
||||
IgnoreJidConfig,
|
||||
isJidBroadcast,
|
||||
isJidGroup,
|
||||
isJidNewsletter,
|
||||
isNullJid,
|
||||
JidFilter,
|
||||
@@ -101,6 +102,7 @@ import { EventMessageRequest } from '../../structures/events.dto';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
GroupField,
|
||||
GroupParticipant,
|
||||
GroupsListFields,
|
||||
ParticipantsRequest,
|
||||
SettingsSecurityChangeInfo,
|
||||
@@ -130,6 +132,11 @@ import { IMediaManager } from '../media/IMediaManager';
|
||||
import { QR } from '../QR';
|
||||
import { DataStore } from './DataStore';
|
||||
import { fetchBuffer } from '@waha/utils/fetch';
|
||||
import {
|
||||
PRESENCE_AUTO_ONLINE,
|
||||
PRESENCE_AUTO_ONLINE_DURATION_SECONDS,
|
||||
} from '@waha/core/env';
|
||||
import { Activity } from '@waha/core/abc/activity';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const qrcode = require('qrcode-terminal');
|
||||
@@ -183,6 +190,16 @@ export abstract class WhatsappSession {
|
||||
protected jids: JidFilter;
|
||||
|
||||
private _status: WAHASessionStatus;
|
||||
private _presence:
|
||||
| WAHAPresenceStatus.ONLINE
|
||||
| WAHAPresenceStatus.OFFLINE
|
||||
| null = null;
|
||||
private lastActivityTimestamp?: number;
|
||||
protected presenceAutoOnlineConfig = {
|
||||
enabled: PRESENCE_AUTO_ONLINE,
|
||||
duration: PRESENCE_AUTO_ONLINE_DURATION_SECONDS * 1000,
|
||||
};
|
||||
|
||||
private shouldPrintQR: boolean;
|
||||
protected events2: DefaultMap<WAHAEvents, SwitchObservable<any>>;
|
||||
private status$: Subject<WAHASessionStatus>;
|
||||
@@ -196,6 +213,8 @@ export abstract class WhatsappSession {
|
||||
stdTTL: 10 * 60, // 10 minutes
|
||||
});
|
||||
|
||||
private presenceOfflineTimeout?: ReturnType<typeof setTimeout>;
|
||||
|
||||
public mediaConverter: IMediaConverter = new CoreMediaConverter();
|
||||
|
||||
public constructor({
|
||||
@@ -316,6 +335,30 @@ export abstract class WhatsappSession {
|
||||
return this._status;
|
||||
}
|
||||
|
||||
protected set presence(value: WAHAPresenceStatus) {
|
||||
switch (value) {
|
||||
case null:
|
||||
this._presence = null;
|
||||
break;
|
||||
case WAHAPresenceStatus.ONLINE:
|
||||
this._presence = WAHAPresenceStatus.ONLINE;
|
||||
break;
|
||||
case WAHAPresenceStatus.OFFLINE:
|
||||
this._presence = WAHAPresenceStatus.OFFLINE;
|
||||
break;
|
||||
default:
|
||||
// Ignore chat relates presence
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public get presence():
|
||||
| WAHAPresenceStatus.ONLINE
|
||||
| WAHAPresenceStatus.OFFLINE
|
||||
| null {
|
||||
return this._presence;
|
||||
}
|
||||
|
||||
getBrowserExecutablePath() {
|
||||
return getBrowserExecutablePath();
|
||||
}
|
||||
@@ -534,10 +577,79 @@ export abstract class WhatsappSession {
|
||||
|
||||
abstract sendSeen(chat: SendSeenRequest);
|
||||
|
||||
abstract startTyping(chat: ChatRequest);
|
||||
abstract startTyping(chat: ChatRequest): Promise<void>;
|
||||
|
||||
abstract stopTyping(chat: ChatRequest);
|
||||
|
||||
/**
|
||||
* Activity tracking and presence management
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the timestamp of the last "activity" in the session
|
||||
* @returns Timestamp in milliseconds or undefined if there was never any activity
|
||||
*/
|
||||
public getLastActivityTimestamp(): number | undefined {
|
||||
return this.lastActivityTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maintains ONLINE presence active while there is activity
|
||||
* Resets the timer on each activity, only goes OFFLINE after Xs without activity
|
||||
*/
|
||||
async maintainPresenceOnline(): Promise<void> {
|
||||
if (!this.presenceAutoOnlineConfig.enabled) {
|
||||
return;
|
||||
}
|
||||
if (this.status !== WAHASessionStatus.WORKING) {
|
||||
return;
|
||||
}
|
||||
this.lastActivityTimestamp = Date.now();
|
||||
// If not ONLINE yet, send ONLINE
|
||||
if (this._presence !== WAHAPresenceStatus.ONLINE) {
|
||||
try {
|
||||
// Force set ONLINE in case of many requests comes at the same time
|
||||
// So we'll set ONLINE exactly once
|
||||
this.presence = WAHAPresenceStatus.ONLINE;
|
||||
await this.setPresence(WAHAPresenceStatus.ONLINE);
|
||||
this.logger.debug('Set presence to ONLINE due to activity');
|
||||
} catch (error) {
|
||||
this.logger.debug('Failed to set presence ONLINE', error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Cancel the previous timeout (if exists)
|
||||
this.cleanupPresenceTimeout();
|
||||
|
||||
// Schedule to go back OFFLINE after timeout without activity
|
||||
this.presenceOfflineTimeout = setTimeout(async () => {
|
||||
try {
|
||||
const working = this.status === WAHASessionStatus.WORKING;
|
||||
const online = this.presence === WAHAPresenceStatus.ONLINE;
|
||||
if (!working || !online) {
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
await this.setPresence(WAHAPresenceStatus.OFFLINE);
|
||||
this.logger.debug(
|
||||
'Auto-set presence to OFFLINE after time without activity',
|
||||
);
|
||||
} catch (error) {
|
||||
this.presence = WAHAPresenceStatus.OFFLINE;
|
||||
this.logger.debug('Failed to set presence OFFLINE', error);
|
||||
}
|
||||
this.cleanupPresenceTimeout();
|
||||
}, this.presenceAutoOnlineConfig.duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the timeout when the session stops
|
||||
*/
|
||||
protected cleanupPresenceTimeout() {
|
||||
clearTimeout(this.presenceOfflineTimeout);
|
||||
this.presenceOfflineTimeout = null;
|
||||
}
|
||||
|
||||
abstract setReaction(request: MessageReactionRequest);
|
||||
|
||||
setStar(request: MessageStarRequest): Promise<void> {
|
||||
@@ -821,6 +933,10 @@ export abstract class WhatsappSession {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getGroupParticipants(id: string): Promise<GroupParticipant[]> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getInfoAdminsOnly(id): Promise<SettingsSecurityChangeInfo> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
@@ -918,7 +1034,10 @@ export abstract class WhatsappSession {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public setPresence(presence: WAHAPresenceStatus, chatId?: string) {
|
||||
public setPresence(
|
||||
presence: WAHAPresenceStatus,
|
||||
chatId?: string,
|
||||
): Promise<void> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
@@ -1080,6 +1199,14 @@ export abstract class WhatsappSession {
|
||||
public fetch(url: string): Promise<Buffer> {
|
||||
return fetchBuffer(url);
|
||||
}
|
||||
|
||||
public async resolveMentionsAll(chatId: string): Promise<string[]> {
|
||||
const participants = await this.getGroupParticipants(chatId);
|
||||
let mentions = participants.map((p) => p.id);
|
||||
// Exclude my ids
|
||||
const me = this.getSessionMeInfo();
|
||||
return mentions.filter((id) => id !== me.id && id !== me.lid);
|
||||
}
|
||||
}
|
||||
|
||||
export function getGroupInviteLink(code: string) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
GroupInfoFull,
|
||||
JoinedGroupEvent,
|
||||
} from '@waha/core/engines/gows/types.group';
|
||||
import { toJID } from '@waha/core/utils/jids';
|
||||
import { toCusFormat, toJID } from '@waha/core/utils/jids';
|
||||
import {
|
||||
GroupId,
|
||||
GroupInfo,
|
||||
@@ -116,7 +116,7 @@ function getParticipants(jids: string[] | null, type: GroupParticipantType) {
|
||||
const participants: GroupParticipant[] = [];
|
||||
for (const jid of jids) {
|
||||
participants.push({
|
||||
id: jid,
|
||||
id: toCusFormat(jid),
|
||||
role: role,
|
||||
});
|
||||
}
|
||||
@@ -124,7 +124,7 @@ function getParticipants(jids: string[] | null, type: GroupParticipantType) {
|
||||
}
|
||||
|
||||
function ToGroupInfo(group: GroupInfoFull): GroupInfo {
|
||||
const participants: GroupParticipant[] = extractParticipants(
|
||||
const participants: GroupParticipant[] = ToGroupParticipants(
|
||||
group.Participants,
|
||||
);
|
||||
return {
|
||||
@@ -169,7 +169,7 @@ function ToGroupInfoPartial(group: GroupInfoEvent): GroupInfo | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
function extractParticipants(
|
||||
export function ToGroupParticipants(
|
||||
participants: GOWSGroupParticipant[],
|
||||
): GroupParticipant[] {
|
||||
const result: GroupParticipant[] = [];
|
||||
@@ -183,7 +183,8 @@ function extractParticipants(
|
||||
role = GroupParticipantRole.PARTICIPANT;
|
||||
}
|
||||
result.push({
|
||||
id: participant.JID,
|
||||
id: toCusFormat(participant.JID),
|
||||
pn: toCusFormat(participant.PhoneNumber),
|
||||
role: role,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3564,9 +3564,10 @@ export namespace messages {
|
||||
list?: ListMessage;
|
||||
location?: Location;
|
||||
pollVote?: PollVoteMessage;
|
||||
mentions?: string[];
|
||||
}) {
|
||||
super();
|
||||
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [11, 13], this.#one_of_decls);
|
||||
pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [11, 13, 19], this.#one_of_decls);
|
||||
if (!Array.isArray(data) && typeof data == "object") {
|
||||
if ("session" in data && data.session != undefined) {
|
||||
this.session = data.session;
|
||||
@@ -3622,6 +3623,9 @@ export namespace messages {
|
||||
if ("pollVote" in data && data.pollVote != undefined) {
|
||||
this.pollVote = data.pollVote;
|
||||
}
|
||||
if ("mentions" in data && data.mentions != undefined) {
|
||||
this.mentions = data.mentions;
|
||||
}
|
||||
}
|
||||
}
|
||||
get session() {
|
||||
@@ -3762,6 +3766,12 @@ export namespace messages {
|
||||
get has_pollVote() {
|
||||
return pb_1.Message.getField(this, 18) != null;
|
||||
}
|
||||
get mentions() {
|
||||
return pb_1.Message.getFieldWithDefault(this, 19, []) as string[];
|
||||
}
|
||||
set mentions(value: string[]) {
|
||||
pb_1.Message.setField(this, 19, value);
|
||||
}
|
||||
static fromObject(data: {
|
||||
session?: ReturnType<typeof Session.prototype.toObject>;
|
||||
jid?: string;
|
||||
@@ -3781,6 +3791,7 @@ export namespace messages {
|
||||
list?: ReturnType<typeof ListMessage.prototype.toObject>;
|
||||
location?: ReturnType<typeof Location.prototype.toObject>;
|
||||
pollVote?: ReturnType<typeof PollVoteMessage.prototype.toObject>;
|
||||
mentions?: string[];
|
||||
}): MessageRequest {
|
||||
const message = new MessageRequest({});
|
||||
if (data.session != null) {
|
||||
@@ -3837,6 +3848,9 @@ export namespace messages {
|
||||
if (data.pollVote != null) {
|
||||
message.pollVote = PollVoteMessage.fromObject(data.pollVote);
|
||||
}
|
||||
if (data.mentions != null) {
|
||||
message.mentions = data.mentions;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
toObject() {
|
||||
@@ -3859,6 +3873,7 @@ export namespace messages {
|
||||
list?: ReturnType<typeof ListMessage.prototype.toObject>;
|
||||
location?: ReturnType<typeof Location.prototype.toObject>;
|
||||
pollVote?: ReturnType<typeof PollVoteMessage.prototype.toObject>;
|
||||
mentions?: string[];
|
||||
} = {};
|
||||
if (this.session != null) {
|
||||
data.session = this.session.toObject();
|
||||
@@ -3914,6 +3929,9 @@ export namespace messages {
|
||||
if (this.pollVote != null) {
|
||||
data.pollVote = this.pollVote.toObject();
|
||||
}
|
||||
if (this.mentions != null) {
|
||||
data.mentions = this.mentions;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
serialize(): Uint8Array;
|
||||
@@ -3956,6 +3974,8 @@ export namespace messages {
|
||||
writer.writeMessage(17, this.location, () => this.location.serialize(writer));
|
||||
if (this.has_pollVote)
|
||||
writer.writeMessage(18, this.pollVote, () => this.pollVote.serialize(writer));
|
||||
if (this.mentions.length)
|
||||
writer.writeRepeatedString(19, this.mentions);
|
||||
if (!w)
|
||||
return writer.getResultBuffer();
|
||||
}
|
||||
@@ -4019,6 +4039,9 @@ export namespace messages {
|
||||
case 18:
|
||||
reader.readMessage(message.pollVote, () => message.pollVote = PollVoteMessage.deserialize(reader));
|
||||
break;
|
||||
case 19:
|
||||
pb_1.Message.addToRepeatedField(message, 19, reader.readString());
|
||||
break;
|
||||
default: reader.skipField();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8555,7 +8555,7 @@ proto.messages.EventMessage.prototype.hasLocation = function() {
|
||||
* @private {!Array<number>}
|
||||
* @const
|
||||
*/
|
||||
proto.messages.MessageRequest.repeatedFields_ = [11,13];
|
||||
proto.messages.MessageRequest.repeatedFields_ = [11,13,19];
|
||||
|
||||
|
||||
|
||||
@@ -8606,7 +8606,8 @@ proto.messages.MessageRequest.toObject = function(includeInstance, msg) {
|
||||
poll: (f = msg.getPoll()) && proto.messages.PollMessage.toObject(includeInstance, f),
|
||||
list: (f = msg.getList()) && proto.messages.ListMessage.toObject(includeInstance, f),
|
||||
location: (f = msg.getLocation()) && proto.messages.Location.toObject(includeInstance, f),
|
||||
pollvote: (f = msg.getPollvote()) && proto.messages.PollVoteMessage.toObject(includeInstance, f)
|
||||
pollvote: (f = msg.getPollvote()) && proto.messages.PollVoteMessage.toObject(includeInstance, f),
|
||||
mentionsList: (f = jspb.Message.getRepeatedField(msg, 19)) == null ? undefined : f
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -8726,6 +8727,10 @@ proto.messages.MessageRequest.deserializeBinaryFromReader = function(msg, reader
|
||||
reader.readMessage(value,proto.messages.PollVoteMessage.deserializeBinaryFromReader);
|
||||
msg.setPollvote(value);
|
||||
break;
|
||||
case 19:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.addMentions(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -8892,6 +8897,13 @@ proto.messages.MessageRequest.serializeBinaryToWriter = function(message, writer
|
||||
proto.messages.PollVoteMessage.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = message.getMentionsList();
|
||||
if (f.length > 0) {
|
||||
writer.writeRepeatedString(
|
||||
19,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -9448,6 +9460,43 @@ proto.messages.MessageRequest.prototype.hasPollvote = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* repeated string mentions = 19;
|
||||
* @return {!Array<string>}
|
||||
*/
|
||||
proto.messages.MessageRequest.prototype.getMentionsList = function() {
|
||||
return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 19));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {!Array<string>} value
|
||||
* @return {!proto.messages.MessageRequest} returns this
|
||||
*/
|
||||
proto.messages.MessageRequest.prototype.setMentionsList = function(value) {
|
||||
return jspb.Message.setField(this, 19, value || []);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @param {number=} opt_index
|
||||
* @return {!proto.messages.MessageRequest} returns this
|
||||
*/
|
||||
proto.messages.MessageRequest.prototype.addMentions = function(value, opt_index) {
|
||||
return jspb.Message.addToRepeatedField(this, 19, value, opt_index);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the list making it empty but non-null.
|
||||
* @return {!proto.messages.MessageRequest} returns this
|
||||
*/
|
||||
proto.messages.MessageRequest.prototype.clearMentionsList = function() {
|
||||
return this.setMentionsList([]);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import { Jid } from '@waha/core/engines/const';
|
||||
import { EventsFromObservable } from '@waha/core/engines/gows/EventsFromObservable';
|
||||
import { GowsEventStreamObservable } from '@waha/core/engines/gows/GowsEventStreamObservable';
|
||||
import {
|
||||
ToGroupParticipants,
|
||||
ToGroupV2JoinEvent,
|
||||
ToGroupV2LeaveEvent,
|
||||
ToGroupV2ParticipantsEvents,
|
||||
@@ -105,6 +106,7 @@ import {
|
||||
import { BinaryFile, RemoteFile } from '@waha/structures/files.dto';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
GroupParticipant,
|
||||
GroupSortField,
|
||||
Participant,
|
||||
ParticipantsRequest,
|
||||
@@ -172,6 +174,10 @@ import {
|
||||
import esm from '@waha/vendor/esm';
|
||||
import { IsEditedMessage } from '@waha/core/utils/pwa';
|
||||
import MessageServiceClient = messages.MessageServiceClient;
|
||||
import { GoToJSWAProto } from '@waha/core/engines/gows/waproto';
|
||||
import { extractWALocation } from '@waha/core/engines/waproto/locaiton';
|
||||
import { extractVCards } from '@waha/core/engines/waproto/vcards';
|
||||
import { Activity } from '@waha/core/abc/activity';
|
||||
|
||||
enum WhatsMeowEvent {
|
||||
CONNECTED = 'gows.ConnectedEventData',
|
||||
@@ -198,8 +204,8 @@ enum WhatsMeowEvent {
|
||||
}
|
||||
|
||||
const gRPCClientConfig = {
|
||||
'grpc.max_send_message_length': 128 * 1024 * 1024,
|
||||
'grpc.max_receive_message_length': 128 * 1024 * 1024,
|
||||
'grpc.max_send_message_length': 512 * 1024 * 1024,
|
||||
'grpc.max_receive_message_length': 512 * 1024 * 1024,
|
||||
};
|
||||
|
||||
export interface GowsConfig {
|
||||
@@ -331,11 +337,15 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
|
||||
events.on(WhatsMeowEvent.DISCONNECTED, () => {
|
||||
if (this.status != WAHASessionStatus.STARTING) {
|
||||
this.cleanupPresenceTimeout();
|
||||
this.presence = null;
|
||||
this.status = WAHASessionStatus.STARTING;
|
||||
}
|
||||
});
|
||||
events.on(WhatsMeowEvent.KEEP_ALIVE_TIMEOUT, () => {
|
||||
if (this.status != WAHASessionStatus.STARTING) {
|
||||
this.cleanupPresenceTimeout();
|
||||
this.presence = null;
|
||||
this.status = WAHASessionStatus.STARTING;
|
||||
}
|
||||
});
|
||||
@@ -664,6 +674,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
this.events2.get(WAHAEvents.LABEL_CHAT_DELETED).switch(labelChatDeleted$);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async fetchContactProfilePicture(id: string): Promise<string> {
|
||||
const jid = toJID(this.ensureSuffix(id));
|
||||
const request = new messages.ProfilePictureRequest({
|
||||
@@ -682,6 +693,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
this.cleanupPresenceTimeout();
|
||||
if (this.client) {
|
||||
const response = await promisify(this.client.StopSession)(this.session);
|
||||
response.toObject();
|
||||
@@ -756,6 +768,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
/**
|
||||
* Profile methods
|
||||
*/
|
||||
@Activity()
|
||||
public async setProfileName(name: string): Promise<boolean> {
|
||||
const request = new messages.ProfileNameRequest({
|
||||
session: this.session,
|
||||
@@ -766,6 +779,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setProfileStatus(status: string): Promise<boolean> {
|
||||
const request = new messages.ProfileStatusRequest({
|
||||
session: this.session,
|
||||
@@ -795,6 +809,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return data.id;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async sendText(request: MessageTextRequest) {
|
||||
const jid = toJID(this.ensureSuffix(request.chatId));
|
||||
const message = new messages.MessageRequest({
|
||||
@@ -804,12 +819,14 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
linkPreview: request.linkPreview ?? true,
|
||||
linkPreviewHighQuality: request.linkPreviewHighQuality,
|
||||
replyTo: getMessageIdFromSerialized(request.reply_to),
|
||||
mentions: request.mentions?.map((mention) => toJID(mention)),
|
||||
});
|
||||
const response = await promisify(this.client.SendMessage)(message);
|
||||
const data = response.toObject();
|
||||
return this.messageResponse(jid, data);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async editMessage(
|
||||
chatId: string,
|
||||
messageId: string,
|
||||
@@ -830,6 +847,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return this.messageResponse(jid, data);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async sendContactVCard(request: MessageContactVcardRequest) {
|
||||
const jid = toJID(this.ensureSuffix(request.chatId));
|
||||
const contacts = request.contacts.map((el) => ({ vcard: toVcardV3(el) }));
|
||||
@@ -844,6 +862,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return this.messageResponse(jid, data);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async sendPoll(request: MessagePollRequest) {
|
||||
const jid = toJID(request.chatId);
|
||||
const message = new messages.MessageRequest({
|
||||
@@ -869,6 +888,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
throw new AvailableInPlusVersion();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async deleteMessage(chatId: string, messageId: string) {
|
||||
const jid = toJID(this.ensureSuffix(chatId));
|
||||
const key = parseMessageIdSerialized(messageId);
|
||||
@@ -890,6 +910,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return contacts.map(toJID);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async sendTextStatus(status: TextStatus) {
|
||||
const participants = await this.prepareJidsForStatus(status.contacts);
|
||||
const message = new messages.MessageRequest({
|
||||
@@ -959,6 +980,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
};
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async sendLocation(request: MessageLocationRequest) {
|
||||
const jid = toJID(this.ensureSuffix(request.chatId));
|
||||
const message = new messages.MessageRequest({
|
||||
@@ -977,7 +999,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
}
|
||||
|
||||
forwardMessage(request: MessageForwardRequest): Promise<WAMessage> {
|
||||
throw new Error('Method not implemented.');
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
sendImage(request: MessageImageRequest) {
|
||||
@@ -998,10 +1020,12 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
throw new AvailableInPlusVersion();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
reply(request: MessageReplyRequest) {
|
||||
throw new Error('Method not implemented.');
|
||||
return this.sendText(request);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async sendSeen(request: SendSeenRequest) {
|
||||
const keys = ExtractMessageKeysForRead(request);
|
||||
if (keys.length === 0) {
|
||||
@@ -1024,10 +1048,12 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return;
|
||||
}
|
||||
|
||||
startTyping(chat: ChatRequest) {
|
||||
return this.setPresence(WAHAPresenceStatus.TYPING, chat.chatId);
|
||||
@Activity()
|
||||
async startTyping(chat: ChatRequest): Promise<void> {
|
||||
await this.setPresence(WAHAPresenceStatus.TYPING, chat.chatId);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
stopTyping(chat: ChatRequest) {
|
||||
return this.setPresence(WAHAPresenceStatus.PAUSED, chat.chatId);
|
||||
}
|
||||
@@ -1035,6 +1061,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
/**
|
||||
* Group methods
|
||||
*/
|
||||
@Activity()
|
||||
public async createGroup(request: CreateGroupRequest) {
|
||||
const req = new messages.CreateGroupRequest({
|
||||
session: this.session,
|
||||
@@ -1046,6 +1073,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async joinInfoGroup(code: string): Promise<any> {
|
||||
const req = new messages.GroupCodeRequest({
|
||||
session: this.session,
|
||||
@@ -1056,6 +1084,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async joinGroup(code: string): Promise<string> {
|
||||
const req = new messages.GroupCodeRequest({
|
||||
session: this.session,
|
||||
@@ -1086,6 +1115,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
delete group.Participants;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async refreshGroups(): Promise<boolean> {
|
||||
const req = this.session;
|
||||
await promisify(this.client.FetchGroups)(req);
|
||||
@@ -1102,6 +1132,14 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return data;
|
||||
}
|
||||
|
||||
public async getGroupParticipants(id: string): Promise<GroupParticipant[]> {
|
||||
const group = await this.getGroup(id);
|
||||
if (!group?.Participants?.length) {
|
||||
return [];
|
||||
}
|
||||
return ToGroupParticipants(group.Participants);
|
||||
}
|
||||
|
||||
public async getInfoAdminsOnly(id): Promise<SettingsSecurityChangeInfo> {
|
||||
const group = await this.getGroup(id);
|
||||
return {
|
||||
@@ -1109,6 +1147,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
};
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setInfoAdminsOnly(id, value) {
|
||||
const req = new messages.JidBoolRequest({
|
||||
session: this.session,
|
||||
@@ -1126,6 +1165,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
};
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setMessagesAdminsOnly(id, value) {
|
||||
const req = new messages.JidBoolRequest({
|
||||
session: this.session,
|
||||
@@ -1140,6 +1180,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async leaveGroup(id) {
|
||||
const req = new messages.JidRequest({
|
||||
session: this.session,
|
||||
@@ -1148,6 +1189,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
await promisify(this.client.LeaveGroup)(req);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setDescription(id, description) {
|
||||
const req = new messages.JidStringRequest({
|
||||
session: this.session,
|
||||
@@ -1157,6 +1199,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
await promisify(this.client.SetGroupDescription)(req);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setSubject(id, description) {
|
||||
const req = new messages.JidStringRequest({
|
||||
session: this.session,
|
||||
@@ -1166,6 +1209,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
await promisify(this.client.SetGroupName)(req);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async getInviteCode(id): Promise<string> {
|
||||
const req = new messages.JidRequest({
|
||||
session: this.session,
|
||||
@@ -1176,6 +1220,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return data.value;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async revokeInviteCode(id): Promise<string> {
|
||||
const req = new messages.JidRequest({
|
||||
session: this.session,
|
||||
@@ -1208,26 +1253,31 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public addParticipants(id: string, request: ParticipantsRequest) {
|
||||
const action = messages.ParticipantAction.ADD;
|
||||
return this.updateParticipants(id, request.participants, action);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public removeParticipants(id, request: ParticipantsRequest) {
|
||||
const action = messages.ParticipantAction.REMOVE;
|
||||
return this.updateParticipants(id, request.participants, action);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public promoteParticipantsToAdmin(id, request: ParticipantsRequest) {
|
||||
const action = messages.ParticipantAction.PROMOTE;
|
||||
return this.updateParticipants(id, request.participants, action);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public demoteParticipantsToUser(id, request: ParticipantsRequest) {
|
||||
const action = messages.ParticipantAction.DEMOTE;
|
||||
return this.updateParticipants(id, request.participants, action);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async setReaction(request: MessageReactionRequest) {
|
||||
const key = parseMessageIdSerialized(request.messageId);
|
||||
const message = new messages.MessageReaction({
|
||||
@@ -1242,6 +1292,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return this.messageResponse(key.remoteJid, data);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async sendEvent(request: EventMessageRequest): Promise<WAMessage> {
|
||||
const jid = toJID(this.ensureSuffix(request.chatId));
|
||||
const event = request.event;
|
||||
@@ -1280,6 +1331,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return this.messageResponse(jid, data) as any;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async cancelEvent(eventId: string): Promise<WAMessage> {
|
||||
throw new Error('Method not implemented.');
|
||||
|
||||
@@ -1315,6 +1367,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
method = this.client.SendPresence;
|
||||
break;
|
||||
case WAHAPresenceStatus.TYPING:
|
||||
await this.maintainPresenceOnline();
|
||||
request = new messages.ChatPresenceRequest({
|
||||
session: this.session,
|
||||
jid: jid,
|
||||
@@ -1323,6 +1376,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
method = this.client.SendChatPresence;
|
||||
break;
|
||||
case WAHAPresenceStatus.RECORDING:
|
||||
await this.maintainPresenceOnline();
|
||||
request = new messages.ChatPresenceRequest({
|
||||
session: this.session,
|
||||
jid: jid,
|
||||
@@ -1331,6 +1385,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
method = this.client.SendChatPresence;
|
||||
break;
|
||||
case WAHAPresenceStatus.PAUSED:
|
||||
await this.maintainPresenceOnline();
|
||||
request = new messages.ChatPresenceRequest({
|
||||
session: this.session,
|
||||
jid: jid,
|
||||
@@ -1343,6 +1398,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
throw new Error('Invalid presence status');
|
||||
}
|
||||
await promisify(method)(request);
|
||||
this.presence = presence;
|
||||
}
|
||||
|
||||
public async getPresences(): Promise<WAHAChatPresences[]> {
|
||||
@@ -1364,6 +1420,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return this.toWahaPresences(jid, result);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async subscribePresence(chatId: string) {
|
||||
const jid = toJID(chatId);
|
||||
const req = new messages.SubscribePresenceRequest({
|
||||
@@ -1485,6 +1542,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return channels;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async channelsCreateChannel(
|
||||
request: CreateChannelRequest,
|
||||
): Promise<Channel> {
|
||||
@@ -1498,10 +1556,12 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return this.toChannel(newsletter);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async channelsGetChannel(id: string): Promise<Channel> {
|
||||
return await this.channelsGetChannelByInviteCode(id);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async channelsGetChannelByInviteCode(
|
||||
inviteCode: string,
|
||||
): Promise<Channel> {
|
||||
@@ -1514,10 +1574,12 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return this.toChannel(newsletter);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public channelsFollowChannel(id: string): Promise<any> {
|
||||
return this.channelsToggleFollow(id, true);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public channelsUnfollowChannel(id: string): Promise<any> {
|
||||
return this.channelsToggleFollow(id, false);
|
||||
}
|
||||
@@ -1534,10 +1596,12 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return response.toObject();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public channelsMuteChannel(id: string): Promise<void> {
|
||||
return this.channelsToggleMute(id, true);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public channelsUnmuteChannel(id: string): Promise<void> {
|
||||
return this.channelsToggleMute(id, false);
|
||||
}
|
||||
@@ -1555,7 +1619,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
/**
|
||||
* Contacts methods
|
||||
*/
|
||||
|
||||
@Activity()
|
||||
public async upsertContact(chatId: string, body: ContactUpdateBody) {
|
||||
const jid = toJID(chatId);
|
||||
const request = new messages.UpdateContactRequest({
|
||||
@@ -1803,6 +1867,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public readChatMessages(
|
||||
chatId: string,
|
||||
request: ReadChatMessagesQuery,
|
||||
@@ -1838,6 +1903,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return labels.map(this.toLabel);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async createLabel(labelDto: LabelDTO): Promise<Label> {
|
||||
const labels = await this.getLabels();
|
||||
const highestLabelId = lodash.max(
|
||||
@@ -1863,6 +1929,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
};
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async updateLabel(label: Label): Promise<Label> {
|
||||
const request = new messages.UpsertLabelRequest({
|
||||
session: this.session,
|
||||
@@ -1876,6 +1943,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async deleteLabel(label: Label): Promise<void> {
|
||||
const request = new messages.DeleteLabelRequest({
|
||||
session: this.session,
|
||||
@@ -1913,6 +1981,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return labels.map(this.toLabel);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async chatsUnreadChat(chatId: string): Promise<any> {
|
||||
const jid = toJID(this.ensureSuffix(chatId));
|
||||
const request = new messages.ChatUnreadRequest({
|
||||
@@ -1924,6 +1993,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async putLabelsToChat(chatId: string, labels: LabelID[]) {
|
||||
const jid = toJID(chatId);
|
||||
const labelsIds = labels.map((label) => label.id);
|
||||
@@ -2030,6 +2100,16 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
const mediaContent = extractMediaContent(message.Message);
|
||||
const source = this.getSourceDeviceByMsg(message);
|
||||
|
||||
let waproto: proto.Message | null = null;
|
||||
try {
|
||||
waproto = GoToJSWAProto(message.Message);
|
||||
} catch (e) {
|
||||
this.logger.error(
|
||||
'Failed to resolve proto message from GOWS to JS format',
|
||||
);
|
||||
this.logger.error(e, e.stack);
|
||||
}
|
||||
|
||||
return {
|
||||
id: id,
|
||||
timestamp: parseTimestampToSeconds(message.Info.Timestamp),
|
||||
@@ -2045,7 +2125,8 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
|
||||
mediaUrl: message.media?.url,
|
||||
// @ts-ignore
|
||||
ack: ack,
|
||||
// @ts-ignore
|
||||
location: extractWALocation(waproto),
|
||||
vCards: extractVCards(waproto),
|
||||
ackName: WAMessageAck[ack] || ACK_UNKNOWN,
|
||||
replyTo: replyTo,
|
||||
_data: message,
|
||||
|
||||
@@ -86,6 +86,7 @@ export interface GroupInfoEvent {
|
||||
|
||||
export interface GOWSGroupParticipant {
|
||||
JID: JID;
|
||||
PhoneNumber: JID;
|
||||
IsAdmin: boolean;
|
||||
IsSuperAdmin: boolean;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { camelCaseKeysDeep } from './proto';
|
||||
import { camelCaseKeysDeep } from '@waha/core/engines/gows/waproto';
|
||||
|
||||
describe('camelCaseKeysDeep', () => {
|
||||
it('URL to url', () => {
|
||||
+16
-14
@@ -1,6 +1,5 @@
|
||||
import { WAMessage } from '@waha/structures/responses.dto';
|
||||
import type { proto } from '@adiwajshing/baileys';
|
||||
import * as lodash from 'lodash';
|
||||
import type { proto } from '@adiwajshing/baileys';
|
||||
|
||||
export function camelCaseKeysDeep<T = any>(input: unknown): T {
|
||||
if (Array.isArray(input)) return input.map(camelCaseKeysDeep) as unknown as T;
|
||||
@@ -13,26 +12,29 @@ export function camelCaseKeysDeep<T = any>(input: unknown): T {
|
||||
return input as T;
|
||||
}
|
||||
|
||||
export function resolveProtoMessage(payload: WAMessage): proto.Message | null {
|
||||
/**
|
||||
* Converts GoToJS WA Proto to Baileys Proto Message
|
||||
*/
|
||||
export function GoToJSWAProto(data: any): proto.Message | null {
|
||||
if (!data) {
|
||||
return data;
|
||||
}
|
||||
return camelCaseKeysDeep(data) as proto.Message;
|
||||
}
|
||||
|
||||
export function resolveProtoMessage(data: any): proto.Message | null {
|
||||
// GOWS
|
||||
if (payload._data.Message) {
|
||||
const protoMessage = payload._data.Message;
|
||||
if (data.Message) {
|
||||
const protoMessage = data.Message;
|
||||
// mediaURL => mediaUrl
|
||||
// otherAttributes => otherAttributes
|
||||
return camelCaseKeysDeep(protoMessage);
|
||||
}
|
||||
|
||||
// NOWEB
|
||||
if (payload._data.message) {
|
||||
return payload._data.message;
|
||||
if (data.message) {
|
||||
return data.message;
|
||||
}
|
||||
// WEBJS - not available
|
||||
return null;
|
||||
}
|
||||
|
||||
export function isEmptyString(content: string) {
|
||||
if (!content) {
|
||||
return true;
|
||||
}
|
||||
return content === '' || content === '\n';
|
||||
}
|
||||
@@ -56,6 +56,7 @@ export function ToGroupParticipant(
|
||||
}
|
||||
return {
|
||||
id: toCusFormat(participant.id),
|
||||
pn: toCusFormat(participant.phoneNumber),
|
||||
role: role,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { WABrowserDescription } from '@adiwajshing/baileys';
|
||||
import makeWASocket, {
|
||||
Browsers,
|
||||
Chat,
|
||||
Contact,
|
||||
decryptPollVote,
|
||||
@@ -10,7 +10,6 @@ import makeWASocket, {
|
||||
getAggregateVotesInPollMessage,
|
||||
getContentType,
|
||||
getKeyAuthor,
|
||||
isJidGroup,
|
||||
isPnUser,
|
||||
isRealMessage,
|
||||
jidNormalizedUser,
|
||||
@@ -24,7 +23,6 @@ import makeWASocket, {
|
||||
WAMessageKey,
|
||||
WAMessageUpdate,
|
||||
} from '@adiwajshing/baileys';
|
||||
import type { WABrowserDescription } from '@adiwajshing/baileys';
|
||||
import { WACallEvent } from '@adiwajshing/baileys/lib/Types/Call';
|
||||
import { BaileysEventMap } from '@adiwajshing/baileys/lib/Types/Events';
|
||||
import { GroupMetadata } from '@adiwajshing/baileys/lib/Types/GroupMetadata';
|
||||
@@ -38,18 +36,15 @@ import {
|
||||
} from '@adiwajshing/baileys/lib/Types/LabelAssociation';
|
||||
import { MessageUserReceiptUpdate } from '@adiwajshing/baileys/lib/Types/Message';
|
||||
import { ILogger } from '@adiwajshing/baileys/lib/Utils/logger';
|
||||
import {
|
||||
isJidBroadcast,
|
||||
isLidUser,
|
||||
} from '@adiwajshing/baileys/lib/WABinary/jid-utils';
|
||||
import { isLidUser } from '@adiwajshing/baileys/lib/WABinary/jid-utils';
|
||||
import { UnprocessableEntityException } from '@nestjs/common';
|
||||
import {
|
||||
ensureSuffix,
|
||||
getChannelInviteLink,
|
||||
getPublicUrlFromDirectPath,
|
||||
WhatsappSession,
|
||||
} from '@waha/core/abc/session.abc';
|
||||
import {
|
||||
ToGroupParticipant,
|
||||
ToGroupV2JoinEvent,
|
||||
ToGroupV2LeaveEvent,
|
||||
ToGroupV2Participants,
|
||||
@@ -140,6 +135,7 @@ import {
|
||||
import { BinaryFile, RemoteFile } from '@waha/structures/files.dto';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
GroupParticipant,
|
||||
ParticipantsRequest,
|
||||
SettingsSecurityChangeInfo,
|
||||
} from '@waha/structures/groups.dto';
|
||||
@@ -203,6 +199,9 @@ import {
|
||||
IsEditedMessage,
|
||||
IsHistorySyncNotification,
|
||||
} from '@waha/core/utils/pwa';
|
||||
import { extractWALocation } from '@waha/core/engines/waproto/locaiton';
|
||||
import { extractVCards } from '@waha/core/engines/waproto/vcards';
|
||||
import { Activity } from '@waha/core/abc/activity';
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const promiseRetry = require('promise-retry');
|
||||
|
||||
@@ -725,6 +724,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
}
|
||||
|
||||
private async end() {
|
||||
this.cleanupPresenceTimeout();
|
||||
this.presence = null;
|
||||
this.autoRestartJob.stop();
|
||||
// @ts-ignore
|
||||
this.sock?.ev?.removeAllListeners();
|
||||
@@ -807,11 +808,13 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
/**
|
||||
* Profile methods
|
||||
*/
|
||||
@Activity()
|
||||
public async setProfileName(name: string): Promise<boolean> {
|
||||
await this.sock.updateProfileName(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setProfileStatus(status: string): Promise<boolean> {
|
||||
await this.sock.updateProfileStatus(status);
|
||||
return true;
|
||||
@@ -847,6 +850,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return this.generateMessageID();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async sendText(request: MessageTextRequest) {
|
||||
const chatId = toJID(this.ensureSuffix(request.chatId));
|
||||
const message = {
|
||||
@@ -859,6 +863,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return this.sock.sendMessage(chatId, message, options);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public deleteMessage(chatId: string, messageId: string) {
|
||||
const jid = toJID(this.ensureSuffix(chatId));
|
||||
const key = parseMessageIdSerialized(messageId);
|
||||
@@ -868,6 +873,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return this.sock.sendMessage(jid, { delete: key }, options);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public editMessage(
|
||||
chatId: string,
|
||||
messageId: string,
|
||||
@@ -888,6 +894,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return this.sock.sendMessage(jid, message, options);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async sendContactVCard(request: MessageContactVcardRequest) {
|
||||
const chatId = toJID(this.ensureSuffix(request.chatId));
|
||||
const contacts = request.contacts.map((el) => ({ vcard: toVcardV3(el) }));
|
||||
@@ -896,6 +903,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return await this.sock.sendMessage(chatId, msg, options);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async sendPoll(request: MessagePollRequest) {
|
||||
const requestPoll = request.poll;
|
||||
const poll = {
|
||||
@@ -912,6 +920,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return this.toWAMessage(result);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async reply(request: MessageReplyRequest) {
|
||||
const options = await this.getMessageOptions(request);
|
||||
const message = {
|
||||
@@ -949,6 +958,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async sendButtons(request: SendButtonsRequest) {
|
||||
const chatId = toJID(this.ensureSuffix(request.chatId));
|
||||
const headerImage = await this.uploadMedia(request.headerImage, 'image');
|
||||
@@ -967,6 +977,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
throw new AvailableInPlusVersion();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async sendLocation(request: MessageLocationRequest) {
|
||||
const chatId = toJID(this.ensureSuffix(request.chatId));
|
||||
const msg = {
|
||||
@@ -980,6 +991,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return await this.sock.sendMessage(chatId, msg, options);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async forwardMessage(request: MessageForwardRequest): Promise<WAMessage> {
|
||||
const key = parseMessageIdSerialized(request.messageId);
|
||||
const forwardMessage = await this.store.loadMessage(key.remoteJid, key.id);
|
||||
@@ -998,6 +1010,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return this.toWAMessage(result);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async sendLinkPreview(request: MessageLinkPreviewRequest) {
|
||||
const text = `${request.title}\n${request.url}`;
|
||||
const chatId = toJID(this.ensureSuffix(request.chatId));
|
||||
@@ -1006,6 +1019,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return this.sock.sendMessage(chatId, msg, options);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async sendSeen(request: SendSeenRequest) {
|
||||
const keys = ExtractMessageKeysForRead(request);
|
||||
if (keys.length === 0) {
|
||||
@@ -1023,11 +1037,13 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
this.sock?.ev.emit('messages.update', updates);
|
||||
}
|
||||
|
||||
async startTyping(request: ChatRequest) {
|
||||
@Activity()
|
||||
async startTyping(request: ChatRequest): Promise<void> {
|
||||
const chatId = toJID(this.ensureSuffix(request.chatId));
|
||||
return this.sock.sendPresenceUpdate('composing', chatId);
|
||||
await this.sock.sendPresenceUpdate('composing', chatId);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async stopTyping(request: ChatRequest) {
|
||||
const chatId = toJID(this.ensureSuffix(request.chatId));
|
||||
return this.sock.sendPresenceUpdate('paused', chatId);
|
||||
@@ -1055,6 +1071,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public readChatMessages(
|
||||
chatId: string,
|
||||
request: ReadChatMessagesQuery,
|
||||
@@ -1073,6 +1090,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return await this.processIncomingMessage(message, query.downloadMedia);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async pinMessage(
|
||||
chatId: string,
|
||||
messageId: string,
|
||||
@@ -1088,6 +1106,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async unpinMessage(
|
||||
chatId: string,
|
||||
messageId: string,
|
||||
@@ -1101,6 +1120,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async setReaction(request: MessageReactionRequest) {
|
||||
const key = parseMessageIdSerialized(request.messageId);
|
||||
if (isJidNewsletter(key.remoteJid)) {
|
||||
@@ -1133,6 +1153,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
}
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async setStar(request: MessageStarRequest) {
|
||||
const key = parseMessageIdSerialized(request.messageId);
|
||||
await this.sock.chatModify(
|
||||
@@ -1206,6 +1227,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
};
|
||||
}
|
||||
|
||||
@Activity()
|
||||
protected async chatsPutArchive(
|
||||
chatId: string,
|
||||
archive: boolean,
|
||||
@@ -1218,14 +1240,17 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public chatsArchiveChat(chatId: string): Promise<any> {
|
||||
return this.chatsPutArchive(chatId, true);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public chatsUnarchiveChat(chatId: string): Promise<any> {
|
||||
return this.chatsPutArchive(chatId, false);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async chatsUnreadChat(chatId: string): Promise<any> {
|
||||
const jid = toJID(chatId);
|
||||
const messages = await this.store.getMessagesByJid(jid, {}, { limit: 1 });
|
||||
@@ -1244,6 +1269,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return labels.map(this.toLabel);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async createLabel(label: LabelDTO): Promise<Label> {
|
||||
const labels = await this.store.getLabels();
|
||||
const highestLabelId = lodash.max(
|
||||
@@ -1267,6 +1293,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
};
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async updateLabel(label: Label): Promise<Label> {
|
||||
const labelAction: LabelActionBody = {
|
||||
id: label.id,
|
||||
@@ -1279,6 +1306,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async deleteLabel(label: Label): Promise<void> {
|
||||
const labelAction: LabelActionBody = {
|
||||
id: label.id,
|
||||
@@ -1303,6 +1331,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return labels.map(this.toLabel);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async putLabelsToChat(chatId: string, labels: LabelID[]) {
|
||||
const jid = toJID(chatId);
|
||||
const labelsIds = labels.map((label) => label.id);
|
||||
@@ -1344,6 +1373,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
* Contacts methods
|
||||
*/
|
||||
|
||||
@Activity()
|
||||
public async upsertContact(chatId: string, body: ContactUpdateBody) {
|
||||
const jid = toJID(chatId);
|
||||
let fullName = body.firstName;
|
||||
@@ -1379,6 +1409,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return contacts.map(this.toWAContact);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async fetchContactProfilePicture(id: string) {
|
||||
const contact = this.ensureSuffix(id);
|
||||
try {
|
||||
@@ -1444,15 +1475,18 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
/**
|
||||
* Group methods
|
||||
*/
|
||||
@Activity()
|
||||
public createGroup(request: CreateGroupRequest) {
|
||||
const participants = request.participants.map(getId);
|
||||
return this.sock.groupCreate(request.name, participants);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public joinGroup(code: string) {
|
||||
return this.sock.groupAcceptInvite(code);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public joinInfoGroup(code: string) {
|
||||
return this.sock.groupGetInviteInfo(code);
|
||||
}
|
||||
@@ -1467,6 +1501,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
delete group.participants;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async refreshGroups(): Promise<boolean> {
|
||||
this.store.resetGroupsCache();
|
||||
await this.store.getGroups({});
|
||||
@@ -1482,6 +1517,14 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return group;
|
||||
}
|
||||
|
||||
public async getGroupParticipants(id: string): Promise<GroupParticipant[]> {
|
||||
const group = (await this.getGroup(id)) as GroupMetadata;
|
||||
if (!group?.participants?.length) {
|
||||
return [];
|
||||
}
|
||||
return group.participants.map(ToGroupParticipant);
|
||||
}
|
||||
|
||||
public async deleteGroup(id) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
@@ -1491,6 +1534,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return { adminsOnly: group.restrict };
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setInfoAdminsOnly(id, value) {
|
||||
const setting = value ? 'locked' : 'unlocked';
|
||||
return await this.sock.groupSettingUpdate(id, setting);
|
||||
@@ -1501,27 +1545,33 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return { adminsOnly: group.announce };
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setMessagesAdminsOnly(id, value) {
|
||||
const setting = value ? 'announcement' : 'not_announcement';
|
||||
return await this.sock.groupSettingUpdate(id, setting);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async leaveGroup(id) {
|
||||
return this.sock.groupLeave(id);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setDescription(id, description) {
|
||||
return this.sock.groupUpdateDescription(id, description);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setSubject(id, subject) {
|
||||
return this.sock.groupUpdateSubject(id, subject);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async getInviteCode(id): Promise<string> {
|
||||
return this.sock.groupInviteCode(id);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async revokeInviteCode(id): Promise<string> {
|
||||
await this.sock.groupRevokeInvite(id);
|
||||
return this.sock.groupInviteCode(id);
|
||||
@@ -1532,27 +1582,37 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return groups[id].participants;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async addParticipants(id, request: ParticipantsRequest) {
|
||||
const participants = request.participants.map(getId);
|
||||
return this.sock.groupParticipantsUpdate(id, participants, 'add');
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async removeParticipants(id, request: ParticipantsRequest) {
|
||||
const participants = request.participants.map(getId);
|
||||
return this.sock.groupParticipantsUpdate(id, participants, 'remove');
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async promoteParticipantsToAdmin(id, request: ParticipantsRequest) {
|
||||
const participants = request.participants.map(getId);
|
||||
return this.sock.groupParticipantsUpdate(id, participants, 'promote');
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async demoteParticipantsToUser(id, request: ParticipantsRequest) {
|
||||
const participants = request.participants.map(getId);
|
||||
return this.sock.groupParticipantsUpdate(id, participants, 'demote');
|
||||
}
|
||||
|
||||
public async setPresence(presence: WAHAPresenceStatus, chatId?: string) {
|
||||
switch (presence) {
|
||||
case WAHAPresenceStatus.TYPING:
|
||||
case WAHAPresenceStatus.RECORDING:
|
||||
case WAHAPresenceStatus.PAUSED:
|
||||
await this.maintainPresenceOnline();
|
||||
}
|
||||
const enginePresence = ToEnginePresenceStatus[presence];
|
||||
if (!enginePresence) {
|
||||
throw new NotImplementedByEngineError(
|
||||
@@ -1563,6 +1623,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
chatId = toJID(this.ensureSuffix(chatId));
|
||||
}
|
||||
await this.sock.sendPresenceUpdate(enginePresence, chatId);
|
||||
this.presence = presence;
|
||||
}
|
||||
|
||||
public async getPresences(): Promise<WAHAChatPresences[]> {
|
||||
@@ -1585,6 +1646,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return this.toWahaPresences(jid, result);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public subscribePresence(id: string): Promise<void> {
|
||||
const jid = toJID(id);
|
||||
return this.sock.presenceSubscribe(jid);
|
||||
@@ -1593,6 +1655,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
/**
|
||||
* Status methods
|
||||
*/
|
||||
@Activity()
|
||||
public async sendStatusMessage(
|
||||
message: any,
|
||||
options: any,
|
||||
@@ -1658,6 +1721,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
}
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async sendTextStatus(status: TextStatus) {
|
||||
const message = {
|
||||
text: status.text,
|
||||
@@ -1706,6 +1770,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return jids.filter((jid) => jid.endsWith('@s.whatsapp.net'));
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async deleteStatus(request: DeleteStatusRequest) {
|
||||
const messageId = request.id;
|
||||
const key = parseMessageIdSerialized(messageId, true);
|
||||
@@ -1796,6 +1861,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return channels;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async channelsCreateChannel(request: CreateChannelRequest) {
|
||||
const newsletter = await this.sock.newsletterCreate(
|
||||
request.name,
|
||||
@@ -1809,27 +1875,33 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return this.toChannel(toNewsletterMetadata(newsletter));
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async channelsGetChannelByInviteCode(inviteCode: string) {
|
||||
const newsletter = await this.sock.newsletterMetadata('invite', inviteCode);
|
||||
return this.toChannel(toNewsletterMetadata(newsletter));
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async channelsDeleteChannel(id: string) {
|
||||
return await this.sock.newsletterDelete(id);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async channelsFollowChannel(id: string): Promise<any> {
|
||||
return await this.sock.newsletterFollow(id);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async channelsUnfollowChannel(id: string): Promise<any> {
|
||||
return await this.sock.newsletterUnfollow(id);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async channelsMuteChannel(id: string): Promise<any> {
|
||||
return await this.sock.newsletterMute(id);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async channelsUnmuteChannel(id: string): Promise<any> {
|
||||
return await this.sock.newsletterUnmute(id);
|
||||
}
|
||||
@@ -2270,6 +2342,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
const ack = message.ack || StatusToAck(message.status);
|
||||
const mediaContent = extractMediaContent(message.message);
|
||||
const source = this.getMessageSource(message.key.id);
|
||||
const waproto = message.message;
|
||||
return {
|
||||
id: id,
|
||||
timestamp: ensureNumber(message.messageTimestamp),
|
||||
@@ -2287,8 +2360,8 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
ack: ack,
|
||||
// @ts-ignore
|
||||
ackName: WAMessageAck[ack] || ACK_UNKNOWN,
|
||||
location: message.location,
|
||||
vCards: message.vCards,
|
||||
location: extractWALocation(waproto),
|
||||
vCards: extractVCards(waproto),
|
||||
replyTo: replyTo,
|
||||
_data: message,
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@ import { Sqlite3ChatRepository } from './Sqlite3ChatRepository';
|
||||
import { Sqlite3ContactRepository } from './Sqlite3ContactRepository';
|
||||
import { Sqlite3MessagesRepository } from './Sqlite3MessagesRepository';
|
||||
import { Sqlite3SchemaValidation } from './Sqlite3SchemaValidation';
|
||||
import { KNEX_SQLITE_CLIENT } from '@waha/core/env';
|
||||
|
||||
export class Sqlite3Storage extends INowebStorage {
|
||||
private readonly tables: Schema[];
|
||||
@@ -22,9 +23,16 @@ export class Sqlite3Storage extends INowebStorage {
|
||||
constructor(filePath: string) {
|
||||
super();
|
||||
this.knex = Knex({
|
||||
client: 'sqlite3',
|
||||
client: KNEX_SQLITE_CLIENT,
|
||||
connection: { filename: filePath },
|
||||
useNullAsDefault: true,
|
||||
pool: {
|
||||
min: 1,
|
||||
max: 10,
|
||||
idleTimeoutMillis: 60_000,
|
||||
createTimeoutMillis: 120_000,
|
||||
acquireTimeoutMillis: 120_000,
|
||||
},
|
||||
});
|
||||
this.tables = NOWEB_STORE_SCHEMA;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { proto } from '@adiwajshing/baileys';
|
||||
import { WALocation } from '@waha/structures/responses.dto';
|
||||
import * as lodash from 'lodash';
|
||||
import { ensureBase64 } from '@waha/utils/bytes';
|
||||
|
||||
export function extractWALocation(waproto: proto.Message): WALocation | null {
|
||||
if (!waproto) {
|
||||
return null;
|
||||
}
|
||||
if (!lodash.isEmpty(waproto.locationMessage)) {
|
||||
const location = waproto.locationMessage;
|
||||
return {
|
||||
live: false,
|
||||
latitude: location.degreesLatitude?.toString(),
|
||||
longitude: location.degreesLongitude?.toString(),
|
||||
name: location.name,
|
||||
address: location.address,
|
||||
url: location.url,
|
||||
description: location.comment,
|
||||
thumbnail: ensureBase64(location.jpegThumbnail),
|
||||
};
|
||||
}
|
||||
if (!lodash.isEmpty(waproto.liveLocationMessage)) {
|
||||
const location = waproto.liveLocationMessage;
|
||||
return {
|
||||
live: true,
|
||||
latitude: location.degreesLatitude?.toString(),
|
||||
longitude: location.degreesLongitude?.toString(),
|
||||
description: location.caption,
|
||||
thumbnail: ensureBase64(location.jpegThumbnail),
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { proto } from '@adiwajshing/baileys';
|
||||
import * as lodash from 'lodash';
|
||||
|
||||
export function extractVCards(waproto: proto.Message): string[] | null {
|
||||
if (!waproto) {
|
||||
return null;
|
||||
}
|
||||
if (!lodash.isEmpty(waproto.contactMessage)) {
|
||||
return [waproto.contactMessage.vcard];
|
||||
}
|
||||
|
||||
if (!lodash.isEmpty(waproto.contactsArrayMessage)) {
|
||||
return waproto.contactsArrayMessage.contacts.map(
|
||||
(contact) => contact.vcard,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
GroupNotificationTypes,
|
||||
GroupParticipant as WEBJSGroupParticipant,
|
||||
} from 'whatsapp-web.js';
|
||||
import { isPnUser } from '@waha/core/utils/jids';
|
||||
|
||||
function ToGroupInfo(
|
||||
group: GroupChat,
|
||||
@@ -60,7 +61,7 @@ export async function ToGroupV2JoinEvent(
|
||||
};
|
||||
}
|
||||
|
||||
function getParticipants(
|
||||
export function getParticipants(
|
||||
participants: WEBJSGroupParticipant[],
|
||||
): GroupParticipant[] {
|
||||
return participants.map((participant) => {
|
||||
@@ -73,6 +74,9 @@ function getParticipants(
|
||||
|
||||
return {
|
||||
id: participant.id._serialized,
|
||||
pn: isPnUser(participant.id._serialized)
|
||||
? participant.id._serialized
|
||||
: null,
|
||||
role: role,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
TagReceiptNodeToReceiptEvent,
|
||||
} from '@waha/core/engines/webjs/ack.webjs';
|
||||
import {
|
||||
getParticipants,
|
||||
ToGroupV2JoinEvent,
|
||||
ToGroupV2LeaveEvent,
|
||||
ToGroupV2ParticipantsEvent,
|
||||
@@ -95,6 +96,7 @@ import {
|
||||
import { BinaryFile, RemoteFile } from '@waha/structures/files.dto';
|
||||
import {
|
||||
CreateGroupRequest,
|
||||
GroupParticipant,
|
||||
GroupSortField,
|
||||
ParticipantsRequest,
|
||||
SettingsSecurityChangeInfo,
|
||||
@@ -108,7 +110,11 @@ import {
|
||||
WAHAChatPresences,
|
||||
WAHAPresenceData,
|
||||
} from '@waha/structures/presence.dto';
|
||||
import { WAMessage, WAMessageReaction } from '@waha/structures/responses.dto';
|
||||
import {
|
||||
WALocation,
|
||||
WAMessage,
|
||||
WAMessageReaction,
|
||||
} from '@waha/structures/responses.dto';
|
||||
import { BrowserTraceQuery } from '@waha/structures/server.debug.dto';
|
||||
import { MeInfo } from '@waha/structures/sessions.dto';
|
||||
import { StatusRequest, TextStatus } from '@waha/structures/status.dto';
|
||||
@@ -153,6 +159,7 @@ import {
|
||||
normalizeJid,
|
||||
toCusFormat,
|
||||
} from '@waha/core/utils/jids';
|
||||
import { Activity } from '@waha/core/abc/activity';
|
||||
|
||||
export interface WebJSConfig {
|
||||
webVersion?: string;
|
||||
@@ -364,6 +371,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
}
|
||||
|
||||
async stop() {
|
||||
this.cleanupPresenceTimeout();
|
||||
this.shouldRestart = false;
|
||||
this.status = WAHASessionStatus.STOPPED;
|
||||
this.stopEvents();
|
||||
@@ -387,6 +395,8 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
}
|
||||
|
||||
private async end() {
|
||||
this.cleanupPresenceTimeout();
|
||||
this.presence = null;
|
||||
this.engineStateCheckDelayedJob.cancel();
|
||||
this.whatsapp?.removeAllListeners();
|
||||
this.whatsapp?.pupBrowser?.removeAllListeners();
|
||||
@@ -618,11 +628,13 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
/**
|
||||
* Profile methods
|
||||
*/
|
||||
@Activity()
|
||||
public async setProfileName(name: string): Promise<boolean> {
|
||||
await this.whatsapp.setPushName(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setProfileStatus(status: string): Promise<boolean> {
|
||||
await this.whatsapp.setStatus(status);
|
||||
return true;
|
||||
@@ -639,6 +651,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
/**
|
||||
* Other methods
|
||||
*/
|
||||
@Activity()
|
||||
sendText(request: MessageTextRequest) {
|
||||
const options = this.getMessageOptions(request);
|
||||
return this.whatsapp.sendMessage(
|
||||
@@ -648,11 +661,13 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public deleteMessage(chatId: string, messageId: string) {
|
||||
const message = this.recreateMessage(messageId);
|
||||
return message.delete(true);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public editMessage(
|
||||
chatId: string,
|
||||
messageId: string,
|
||||
@@ -667,6 +682,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return message.edit(request.text, options);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async sendContactVCard(request: MessageContactVcardRequest) {
|
||||
const chatId = this.ensureSuffix(request.chatId);
|
||||
const vcards = request.contacts.map((el) => toVcardV3(el as any));
|
||||
@@ -689,7 +705,8 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return this.whatsapp.sendMessage(chatId, '', { ...options, extra });
|
||||
}
|
||||
|
||||
reply(request: MessageReplyRequest) {
|
||||
@Activity()
|
||||
async reply(request: MessageReplyRequest) {
|
||||
const options = this.getMessageOptions(request);
|
||||
return this.whatsapp.sendMessage(
|
||||
this.ensureSuffix(request.chatId),
|
||||
@@ -714,6 +731,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
throw new AvailableInPlusVersion();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async sendLocation(request: MessageLocationRequest) {
|
||||
const location = new Location(request.latitude, request.longitude, {
|
||||
name: request.title,
|
||||
@@ -726,6 +744,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async forwardMessage(request: MessageForwardRequest): Promise<WAMessage> {
|
||||
const forwardMessage = this.recreateMessage(request.messageId);
|
||||
const msg = await forwardMessage.forward(this.ensureSuffix(request.chatId));
|
||||
@@ -735,6 +754,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return { sent: msg || false };
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async sendSeen(request: SendSeenRequest) {
|
||||
const chat: Chat = await this.whatsapp.getChatById(
|
||||
this.ensureSuffix(request.chatId),
|
||||
@@ -742,13 +762,15 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
await chat.sendSeen();
|
||||
}
|
||||
|
||||
async startTyping(request: ChatRequest) {
|
||||
@Activity()
|
||||
async startTyping(request: ChatRequest): Promise<void> {
|
||||
const chat: Chat = await this.whatsapp.getChatById(
|
||||
this.ensureSuffix(request.chatId),
|
||||
);
|
||||
await chat.sendStateTyping();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async stopTyping(request: ChatRequest) {
|
||||
const chat: Chat = await this.whatsapp.getChatById(
|
||||
this.ensureSuffix(request.chatId),
|
||||
@@ -756,6 +778,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
await chat.clearState();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async setReaction(request: MessageReactionRequest) {
|
||||
const message = this.recreateMessage(request.messageId);
|
||||
return message.react(request.reaction);
|
||||
@@ -772,6 +795,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return new MessageInstance(this.whatsapp, data);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async setStar(request: MessageStarRequest) {
|
||||
const message = this.recreateMessage(request.messageId);
|
||||
if (request.star) {
|
||||
@@ -861,6 +885,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async readChatMessages(
|
||||
chatId: string,
|
||||
request: ReadChatMessagesQuery,
|
||||
@@ -895,6 +920,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return await this.processIncomingMessage(message, query.downloadMedia);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async pinMessage(
|
||||
chatId: string,
|
||||
messageId: string,
|
||||
@@ -904,6 +930,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return message.pin(duration);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async unpinMessage(
|
||||
chatId: string,
|
||||
messageId: string,
|
||||
@@ -912,26 +939,31 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return message.unpin();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async deleteChat(chatId) {
|
||||
const chat = await this.whatsapp.getChatById(this.ensureSuffix(chatId));
|
||||
return chat.delete();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
async clearMessages(chatId) {
|
||||
const chat = await this.whatsapp.getChatById(chatId);
|
||||
return chat.clearMessages();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public chatsArchiveChat(chatId: string): Promise<any> {
|
||||
const id = this.ensureSuffix(chatId);
|
||||
return this.whatsapp.archiveChat(id);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public chatsUnarchiveChat(chatId: string): Promise<any> {
|
||||
const id = this.ensureSuffix(chatId);
|
||||
return this.whatsapp.unarchiveChat(id);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public chatsUnreadChat(chatId: string): Promise<any> {
|
||||
const id = this.ensureSuffix(chatId);
|
||||
return this.whatsapp.markChatUnread(id);
|
||||
@@ -947,6 +979,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return labels.map(this.toLabel);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async createLabel(label: LabelDTO): Promise<Label> {
|
||||
const labelId = await this.whatsapp.createLabel(label.name, label.color);
|
||||
return {
|
||||
@@ -957,14 +990,17 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
};
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async updateLabel(label: Label): Promise<Label> {
|
||||
return await this.whatsapp.updateLabel(label);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public deleteLabel(label: Label): Promise<void> {
|
||||
return this.whatsapp.deleteLabel(label);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public getChatsByLabelId(labelId: string) {
|
||||
return this.whatsapp.getChatsByLabelId(labelId);
|
||||
}
|
||||
@@ -975,6 +1011,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return labels.map(this.toLabel);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async putLabelsToChat(chatId: string, labels: LabelID[]) {
|
||||
const labelIds = labels.map((label) => label.id);
|
||||
const chatIds = [this.ensureSuffix(chatId)];
|
||||
@@ -994,6 +1031,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
/**
|
||||
* Contacts methods
|
||||
*/
|
||||
@Activity()
|
||||
public async upsertContact(chatId: string, body: ContactUpdateBody) {
|
||||
const phoneNumber = chatId.split('@')[0];
|
||||
await this.whatsapp.saveOrEditAddressbookContact(
|
||||
@@ -1024,12 +1062,14 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return { about: await contact.getAbout() };
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async fetchContactProfilePicture(id: string) {
|
||||
const contact = await this.whatsapp.getContactById(this.ensureSuffix(id));
|
||||
const url = await contact.getProfilePicUrl();
|
||||
return url;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async blockContact(request: ContactRequest) {
|
||||
const contact = await this.whatsapp.getContactById(
|
||||
this.ensureSuffix(request.contactId),
|
||||
@@ -1037,6 +1077,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
await contact.block();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async unblockContact(request: ContactRequest) {
|
||||
const contact = await this.whatsapp.getContactById(
|
||||
this.ensureSuffix(request.contactId),
|
||||
@@ -1079,6 +1120,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
/**
|
||||
* Group methods
|
||||
*/
|
||||
@Activity()
|
||||
public createGroup(request: CreateGroupRequest) {
|
||||
const participantIds = request.participants.map(
|
||||
(participant) => participant.id,
|
||||
@@ -1086,10 +1128,12 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return this.whatsapp.createGroup(request.name, participantIds);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public joinGroup(code: string) {
|
||||
return this.whatsapp.acceptInvite(code);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public joinInfoGroup(code: string) {
|
||||
return this.whatsapp.getInviteInfo(code);
|
||||
}
|
||||
@@ -1103,6 +1147,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
};
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setInfoAdminsOnly(id, value) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
return groupChat.setInfoAdminsOnly(value);
|
||||
@@ -1118,6 +1163,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
};
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setMessagesAdminsOnly(id, value) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
return groupChat.setMessagesAdminsOnly(value);
|
||||
@@ -1155,31 +1201,42 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return this.whatsapp.getChatById(id);
|
||||
}
|
||||
|
||||
public async getGroupParticipants(id: string): Promise<GroupParticipant[]> {
|
||||
const group = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
return getParticipants(group.participants);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async deleteGroup(id) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
return groupChat.delete();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async leaveGroup(id) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
return groupChat.leave();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setDescription(id, description) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
return groupChat.setDescription(description);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async setSubject(id, subject) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
return groupChat.setSubject(subject);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async getInviteCode(id): Promise<string> {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
return groupChat.getInviteCode();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async revokeInviteCode(id): Promise<string> {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
await groupChat.revokeInvite();
|
||||
@@ -1191,6 +1248,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return groupChat.participants;
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async addParticipants(id, request: ParticipantsRequest) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
const participantIds = request.participants.map(
|
||||
@@ -1199,6 +1257,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return groupChat.addParticipants(participantIds);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async removeParticipants(id, request: ParticipantsRequest) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
const participantIds = request.participants.map(
|
||||
@@ -1207,6 +1266,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return groupChat.removeParticipants(participantIds);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async promoteParticipantsToAdmin(id, request: ParticipantsRequest) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
const participantIds = request.participants.map(
|
||||
@@ -1215,6 +1275,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return groupChat.promoteParticipants(participantIds);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async demoteParticipantsToUser(id, request: ParticipantsRequest) {
|
||||
const groupChat = (await this.whatsapp.getChatById(id)) as GroupChat;
|
||||
const participantIds = request.participants.map(
|
||||
@@ -1285,6 +1346,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
};
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async channelsList(query: ListChannelsQuery): Promise<Channel[]> {
|
||||
const data = await this.whatsapp.getChannels();
|
||||
let channels = data.map(this.ChatToChannel);
|
||||
@@ -1319,6 +1381,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return await this.channelsGetChannelByInviteCode(id);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async channelsGetChannelByInviteCode(
|
||||
inviteCode: string,
|
||||
): Promise<Channel> {
|
||||
@@ -1363,14 +1426,17 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
await this.whatsapp.sendPresenceUnavailable();
|
||||
break;
|
||||
case WAHAPresenceStatus.TYPING:
|
||||
await this.maintainPresenceOnline();
|
||||
chat = await this.whatsapp.getChatById(chatId);
|
||||
await chat.sendStateTyping();
|
||||
break;
|
||||
case WAHAPresenceStatus.RECORDING:
|
||||
await this.maintainPresenceOnline();
|
||||
chat = await this.whatsapp.getChatById(chatId);
|
||||
await chat.sendStateRecording();
|
||||
break;
|
||||
case WAHAPresenceStatus.PAUSED:
|
||||
await this.maintainPresenceOnline();
|
||||
chat = await this.whatsapp.getChatById(chatId);
|
||||
await chat.clearState();
|
||||
break;
|
||||
@@ -1379,18 +1445,21 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
`WEBJS engine doesn't support '${presence}' presence.`,
|
||||
);
|
||||
}
|
||||
this.presence = presence;
|
||||
}
|
||||
|
||||
public getPresences(): Promise<WAHAChatPresences[]> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async getPresence(id: string): Promise<WAHAChatPresences> {
|
||||
const chatId = toCusFormat(id);
|
||||
const presences = await this.whatsapp.getPresence(chatId);
|
||||
return this.toWahaPresences(chatId, presences);
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public async subscribePresence(id: string): Promise<any> {
|
||||
const chatId = toCusFormat(id);
|
||||
await this.whatsapp.subscribePresence(chatId);
|
||||
@@ -1438,6 +1507,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
}
|
||||
}
|
||||
|
||||
@Activity()
|
||||
public sendTextStatus(status: TextStatus) {
|
||||
this.checkStatusRequest(status);
|
||||
return this.whatsapp.sendTextStatus(status);
|
||||
@@ -1795,7 +1865,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
// @ts-ignore
|
||||
ack: message.ack,
|
||||
ackName: WAMessageAck[message.ack] || ACK_UNKNOWN,
|
||||
location: message.location,
|
||||
location: this.extractLocation(message),
|
||||
vCards: message.vCards,
|
||||
replyTo: replyTo,
|
||||
_data: message.rawData,
|
||||
@@ -1816,6 +1886,24 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
};
|
||||
}
|
||||
|
||||
protected extractLocation(message: Message): WALocation | null {
|
||||
const location = message.location;
|
||||
if (lodash.isEmpty(location)) {
|
||||
return null;
|
||||
}
|
||||
const rawData: any = message.rawData;
|
||||
return {
|
||||
live: Boolean(rawData.isLive),
|
||||
latitude: location.latitude,
|
||||
longitude: location.longitude,
|
||||
name: location.name,
|
||||
address: location.address,
|
||||
description: rawData.comment || location.description,
|
||||
url: location.url,
|
||||
thumbnail: message.body,
|
||||
};
|
||||
}
|
||||
|
||||
public async getEngineInfo() {
|
||||
if (!this.whatsapp || !this.whatsapp.pupPage) {
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { parseBool } from '@waha/helpers';
|
||||
|
||||
//
|
||||
// Presence
|
||||
//
|
||||
|
||||
// Automatically mark session as ONLINE on any messages activity
|
||||
export const PRESENCE_AUTO_ONLINE = process.env.WAHA_PRESENCE_AUTO_ONLINE
|
||||
? parseBool(process.env.WAHA_PRESENCE_AUTO_ONLINE)
|
||||
: true;
|
||||
|
||||
// Duration (in seconds) to keep session ONLINE after activity
|
||||
// 25 seconds is default web timeout with no activity
|
||||
export const PRESENCE_AUTO_ONLINE_DURATION_SECONDS =
|
||||
parseInt(process.env.WAHA_PRESENCE_AUTO_ONLINE_DURATION_SECONDS) || 25;
|
||||
|
||||
//
|
||||
// Local - sqlite3 engine
|
||||
//
|
||||
let KNEX_SQLITE_CLIENT = process.env.WAHA_SQLITE_ENGINE;
|
||||
if (KNEX_SQLITE_CLIENT != 'sqlite3' && KNEX_SQLITE_CLIENT != 'better-sqlite3') {
|
||||
KNEX_SQLITE_CLIENT = 'sqlite3';
|
||||
}
|
||||
export { KNEX_SQLITE_CLIENT };
|
||||
@@ -349,6 +349,10 @@ export class SessionManagerCore extends SessionManager implements OnModuleInit {
|
||||
status: WAHASessionStatus.STOPPED,
|
||||
config: this.sessionConfig,
|
||||
me: null,
|
||||
presence: null,
|
||||
timestamps: {
|
||||
activity: null,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -367,6 +371,10 @@ export class SessionManagerCore extends SessionManager implements OnModuleInit {
|
||||
status: session.status,
|
||||
config: session.sessionConfig,
|
||||
me: me,
|
||||
presence: session.presence,
|
||||
timestamps: {
|
||||
activity: session?.getLastActivityTimestamp(),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -400,7 +408,10 @@ export class SessionManagerCore extends SessionManager implements OnModuleInit {
|
||||
}
|
||||
const session = sessions[0];
|
||||
const engine = await this.fetchEngineInfo();
|
||||
return { ...session, engine: engine };
|
||||
return {
|
||||
...session,
|
||||
engine: engine,
|
||||
};
|
||||
}
|
||||
|
||||
protected stopEvents() {
|
||||
|
||||
@@ -4,6 +4,7 @@ import Knex from 'knex';
|
||||
import * as path from 'path';
|
||||
|
||||
import { LocalStore } from './LocalStore';
|
||||
import { KNEX_SQLITE_CLIENT } from '@waha/core/env';
|
||||
|
||||
export class LocalStoreCore extends LocalStore {
|
||||
protected readonly baseDirectory: string =
|
||||
@@ -69,7 +70,7 @@ export class LocalStoreCore extends LocalStore {
|
||||
const engineDir = this.getEngineDirectory();
|
||||
const database = path.join(engineDir, 'waha.sqlite3');
|
||||
return Knex({
|
||||
client: 'sqlite3',
|
||||
client: KNEX_SQLITE_CLIENT,
|
||||
connection: { filename: database },
|
||||
useNullAsDefault: true,
|
||||
acquireConnectionTimeout: 120_000,
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { isJidGroup } from '@waha/core/utils/jids';
|
||||
import { UnprocessableEntityException } from '@nestjs/common';
|
||||
|
||||
const ALL = 'all';
|
||||
|
||||
export function mentionsAll(request: { mentions?: string[] }) {
|
||||
return request.mentions && request.mentions.includes(ALL);
|
||||
}
|
||||
|
||||
export function validateRequestMentions(request: {
|
||||
chatId: string;
|
||||
mentions?: string[];
|
||||
}) {
|
||||
if (!isJidGroup(request.chatId)) {
|
||||
throw new UnprocessableEntityException(
|
||||
`"mentions":["all"] can be used only in group chats, not in '${request.chatId}'`,
|
||||
);
|
||||
}
|
||||
if (request.mentions.length > 1) {
|
||||
throw new UnprocessableEntityException(
|
||||
`"mentions":["all"] cannot be used with other mentions`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -94,6 +94,7 @@ export class ChatPictureResponse {
|
||||
|
||||
export enum MessageSortField {
|
||||
TIMESTAMP = 'timestamp',
|
||||
MESSAGE_TIMESTAMP = 'messageTimestamp',
|
||||
}
|
||||
|
||||
export class GetChatMessagesQuery extends PaginationParams {
|
||||
|
||||
@@ -303,6 +303,9 @@ class FileRequest extends ChatRequest {
|
||||
export class MessageImageRequest extends FileRequest {
|
||||
caption?: string;
|
||||
|
||||
@ApiHideProperty()
|
||||
mentions?: string[];
|
||||
|
||||
@ReplyToProperty()
|
||||
reply_to?: string;
|
||||
}
|
||||
@@ -310,6 +313,9 @@ export class MessageImageRequest extends FileRequest {
|
||||
export class MessageFileRequest extends FileRequest {
|
||||
caption?: string;
|
||||
|
||||
@ApiHideProperty()
|
||||
mentions?: string[];
|
||||
|
||||
@ReplyToProperty()
|
||||
reply_to?: string;
|
||||
}
|
||||
@@ -343,6 +349,9 @@ export class MessageVideoRequest extends ChatRequest {
|
||||
|
||||
caption?: string = 'Just watch at this!';
|
||||
|
||||
@ApiHideProperty()
|
||||
mentions?: string[];
|
||||
|
||||
@ApiProperty({
|
||||
description:
|
||||
'The ID of the message to reply to - false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA',
|
||||
|
||||
@@ -115,10 +115,17 @@ export enum GroupParticipantRole {
|
||||
|
||||
export class GroupParticipant {
|
||||
@ApiProperty({
|
||||
example: '123456789@c.us',
|
||||
description: 'Member ID in @c.us or @lid format',
|
||||
example: '123456789@lid',
|
||||
})
|
||||
id: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Member ID in @c.us format',
|
||||
example: '123456789@c.us',
|
||||
})
|
||||
pn?: string;
|
||||
|
||||
@ApiProperty({
|
||||
example: GroupParticipantRole.PARTICIPANT,
|
||||
})
|
||||
|
||||
@@ -6,9 +6,14 @@ import { WAMessageAck } from './enums.dto';
|
||||
import { ChatIdProperty, MessageIdProperty } from './properties.dto';
|
||||
|
||||
export class WALocation {
|
||||
description?: string;
|
||||
latitude: string;
|
||||
longitude: string;
|
||||
live: boolean;
|
||||
name?: string;
|
||||
address?: string;
|
||||
url?: string;
|
||||
description?: string; // "comment" in proto
|
||||
thumbnail?: string;
|
||||
}
|
||||
|
||||
export enum MessageSource {
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
|
||||
import { WAHASessionStatus } from './enums.dto';
|
||||
import { WAHAPresenceStatus, WAHASessionStatus } from './enums.dto';
|
||||
import { ChatIdProperty } from './properties.dto';
|
||||
import { WebhookConfig } from './webhooks.config.dto';
|
||||
|
||||
@@ -237,6 +237,11 @@ export class MeInfo {
|
||||
export class SessionInfo extends SessionDTO {
|
||||
me?: MeInfo;
|
||||
assignedWorker?: string;
|
||||
// Timestamp of the last activity in milliseconds
|
||||
presence: WAHAPresenceStatus.ONLINE | WAHAPresenceStatus.OFFLINE | null;
|
||||
timestamps: {
|
||||
activity: number | null;
|
||||
};
|
||||
}
|
||||
|
||||
export class SessionDetailedInfo extends SessionInfo {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
export function ensureBase64(
|
||||
data: string | Uint8Array | ArrayBuffer | null | undefined,
|
||||
): string | null | undefined {
|
||||
// Preserves null or undefined
|
||||
if (!data) {
|
||||
return data as any;
|
||||
}
|
||||
// Already base64
|
||||
if (typeof data === 'string') {
|
||||
return data.trim();
|
||||
}
|
||||
|
||||
if (data instanceof Uint8Array) {
|
||||
return Buffer.from(data).toString('base64');
|
||||
}
|
||||
|
||||
if (data instanceof ArrayBuffer) {
|
||||
return Buffer.from(new Uint8Array(data)).toString('base64');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
+1
-1
@@ -30,7 +30,7 @@ export function getWAHAVersion(): WAHAVersion {
|
||||
}
|
||||
|
||||
export const VERSION: WAHAEnvironment = {
|
||||
version: '2025.10.5',
|
||||
version: '2025.11.2',
|
||||
engine: getEngineName(),
|
||||
tier: getWAHAVersion(),
|
||||
browser:
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"waha": {
|
||||
"gows": {
|
||||
"repo": "devlikeapro/gows",
|
||||
"ref": "v1.0.19"
|
||||
"ref": "v1.0.21"
|
||||
},
|
||||
"dashboard": {
|
||||
"repo": "devlikeapro/dashboard",
|
||||
|
||||
@@ -5,9 +5,9 @@ __metadata:
|
||||
version: 6
|
||||
cacheKey: 8
|
||||
|
||||
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-10-19":
|
||||
version: 7.0.0-rc.5
|
||||
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=df07435e41a5b6062c51a4a74393558cecf8fc50"
|
||||
"@adiwajshing/baileys@github:devlikeapro/Baileys#fork-master-2025-11-07":
|
||||
version: 7.0.0-rc.6
|
||||
resolution: "@adiwajshing/baileys@https://github.com/devlikeapro/Baileys.git#commit=e828866d7936785b054e16f360c32115f118f291"
|
||||
dependencies:
|
||||
"@cacheable/node-cache": ^1.4.0
|
||||
"@hapi/boom": ^9.1.3
|
||||
@@ -31,7 +31,7 @@ __metadata:
|
||||
optional: true
|
||||
link-preview-js:
|
||||
optional: true
|
||||
checksum: 065a0a3e086e307ef805321355914c97282d279e7cbe2350118562373c0d36aaf877b0789d7e339f45f9c24e31a425f699b2f905411500522d0dc95a31c8986a
|
||||
checksum: 0a02bc71fe13e998047f10e205126cca978ffde85a50fabc8ccf25c79a3ff6baecc857a395956e6c37b10abbeb9e390e4fcbc870ea11e3a22224cbc8c562c41e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -4867,14 +4867,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"better-sqlite3@npm:11.3.0":
|
||||
version: 11.3.0
|
||||
resolution: "better-sqlite3@npm:11.3.0"
|
||||
"better-sqlite3@npm:^12.4.1":
|
||||
version: 12.4.1
|
||||
resolution: "better-sqlite3@npm:12.4.1"
|
||||
dependencies:
|
||||
bindings: ^1.5.0
|
||||
node-gyp: latest
|
||||
prebuild-install: ^7.1.1
|
||||
checksum: 364058b4cc739abd5774f511521298eac101d1e6f0362da65b56d9135e6e0728af38c2fa182ec2b56b4c76d7f7ff8efefd753df0c1b8f0192051b06d21e5d1f7
|
||||
checksum: 39141f425a661fcb6ad413c791132c58463026f30d652a672ed9e0fa9fd286021826c0d2c3700762d6bafcccef8de5af1f0fb79eb8c2f31f005f7f0b47069292
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -6174,6 +6174,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dotenv@npm:^17.2.3":
|
||||
version: 17.2.3
|
||||
resolution: "dotenv@npm:17.2.3"
|
||||
checksum: fde23eb88649041ec7a0f6a47bbe59cac3c454fc2007cf2e40b9c984aaf0636347218c56cfbbf067034b0a73f530a2698a19b4058695787eb650ec69fe234624
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dunder-proto@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "dunder-proto@npm:1.0.1"
|
||||
@@ -12525,7 +12532,7 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "waha@workspace:."
|
||||
dependencies:
|
||||
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-10-19"
|
||||
"@adiwajshing/baileys": "github:devlikeapro/Baileys#fork-master-2025-11-07"
|
||||
"@adiwajshing/keyed-db": ^0.2.4
|
||||
"@aws-sdk/client-s3": ^3.633.0
|
||||
"@aws-sdk/s3-request-presigner": ^3.633.0
|
||||
@@ -12569,7 +12576,7 @@ __metadata:
|
||||
audio-decode: ^2.2.2
|
||||
axios: ^1.9.0
|
||||
axios-retry: ^4.5.0
|
||||
better-sqlite3: 11.3.0
|
||||
better-sqlite3: ^12.4.1
|
||||
bufferutil: ^4.0.8
|
||||
bullmq: ^5.48.1
|
||||
check-disk-space: ^3.4.0
|
||||
@@ -12578,6 +12585,7 @@ __metadata:
|
||||
class-validator: 0.14.0
|
||||
commander: ^14.0.1
|
||||
del: ^8.0.0
|
||||
dotenv: ^17.2.3
|
||||
express-basic-auth: ^1.2.1
|
||||
file-type: 16.5.4
|
||||
fs-extra: ^11.2.0
|
||||
@@ -12628,7 +12636,7 @@ __metadata:
|
||||
typescript: 5.9.2
|
||||
ulid: ^2.3.0
|
||||
undici: ^7.16.0
|
||||
whatsapp-web.js: "github:devlikeapro/whatsapp-web.js#fork-main-2025-09-10"
|
||||
whatsapp-web.js: "github:devlikeapro/whatsapp-web.js#fork-main-2025-11-05"
|
||||
write-file-atomic: ^6.0.0
|
||||
yaml: ^2.7.1
|
||||
dependenciesMeta:
|
||||
@@ -12729,9 +12737,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-2025-09-10":
|
||||
version: 1.34.1
|
||||
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=130dbdb5608d9d6add1fe8712dc9e8be6a4bab34"
|
||||
"whatsapp-web.js@github:devlikeapro/whatsapp-web.js#fork-main-2025-11-05":
|
||||
version: 1.34.2
|
||||
resolution: "whatsapp-web.js@https://github.com/devlikeapro/whatsapp-web.js.git#commit=c84b4af4dfd01f326d37f4de6acd7bebc06496d0"
|
||||
dependencies:
|
||||
"@pedroslopez/moduleraid": ^5.0.2
|
||||
archiver: ^5.3.1
|
||||
@@ -12749,7 +12757,7 @@ __metadata:
|
||||
optional: true
|
||||
unzipper:
|
||||
optional: true
|
||||
checksum: fd1fa94beab523b7b0becec27bd626e350805e1cdcefe81b4648e4a1c865fab8eeee9ab5a7c2d72d83969647c108df446ee2ffa2a475a0024acb75007fd96a53
|
||||
checksum: d9d615d29ba14f6113080a19d8f7e1396ea7dde3cbe4983db04e8df512483198c3f73236e9e8c366afb035ee700bd5718d72bc18ee01caeb7cd86bcadfdbb056
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in new issue
Block a user