Compare commits

...
74 Commits
Author SHA1 Message Date
devlikepro 317fab4dfe [core] 2025.9.3
Release / WEBJS - chrome - amd64 - chrome (push) Waiting to run
Release / WEBJS - chromium - amd64 - latest (push) Waiting to run
Release / WEBJS - chromium - linux/arm64 - arm (push) Waiting to run
Release / GOWS - none - amd64 - gows (push) Waiting to run
Release / GOWS - none - linux/arm64 - gows-arm (push) Waiting to run
Release / NOWEB - none - amd64 - noweb (push) Waiting to run
Release / NOWEB - none - linux/arm64 - noweb-arm (push) Waiting to run
2025-09-14 17:48:58 +07:00
devlikepro 6c83ab68b4 [core] Up Dashboard 2025-09-14 17:48:57 +07:00
devlikepro 2194465757 [core] Up GOWS 2025-09-14 17:48:57 +07:00
devlikepro a0e70f88ad [core] NOWEB - update proto to v1025985905 2025-09-14 17:48:56 +07:00
devlikepro 9102a3d852 [core] WEBJS - Add WAHA_WEBJS_PUPPETER_ARGS to test --single-process
fix #1348
2025-09-14 17:48:55 +07:00
devlikepro b05cba896e [core] WEBJS - Update Chrome, Puppeter
fix #1348
2025-09-14 17:48:55 +07:00
devlikepro 6716a31b6c [core] WEBJS - --disable-blink-features=AutomationControlled
fix #1348
2025-09-14 17:48:54 +07:00
devlikepro 2e42d0e7eb [core] ChatWoot - receive messages from broadcast list
fix #1321
2025-09-14 17:48:54 +07:00
devlikepro dee9864157 [core] ChatWoot API Error 2025-09-14 17:48:53 +07:00
devlikepro 0293fc8c6f [core] Fix cases when message.media constains _data when no media attached 2025-09-14 17:48:52 +07:00
devlikepro 725fdc03a6 [core] ChatWoot - i18n 2025-09-14 17:48:49 +07:00
devlikepro 4557cb695f [core] ChatWoot - refactoring 2025-09-14 17:48:49 +07:00
devlikepro 172403a061 [core] ChatWoot - share contacts
fix #1175
2025-09-14 17:48:48 +07:00
devlikepro d03c3c185e [core] ChatWoot - share location (current and live)
fix #1205
2025-09-14 17:48:47 +07:00
devlikepro 50a111343c [core] ChatWoot - fix audio and pdf preview
fix #1330
fix #1340
2025-09-14 17:48:46 +07:00
devlikepro f01693adc3 [core] ChatWoot - Unsupported messages from WhatsApp
fix #1267
2025-09-14 17:48:46 +07:00
devlikepro d2bbbb4448 [core] Apps - JobLink 2025-09-14 17:48:45 +07:00
devlikepro 38c0aadff2 [core] ChatWoot - ignore not set WAHA_DASHBOARD_ENABLED - treat as true 2025-09-14 17:48:44 +07:00
devlikepro 79d593b155 [core] ChatWoot - customize templates via API
fix #1160
fix #1161
2025-09-14 17:48:43 +07:00
devlikepro ddc84cfecc [core] ChatWoot - customize messages
fix #1161
2025-09-14 17:48:42 +07:00
devlikepro b620383d94 [core] ChatWoot - do not send empty text messages 2025-09-14 17:48:42 +07:00
devlikepro 2319dba68d [core] ChatWoot - load languages WAHA_APPS_CHATWOOT_LANGUAGES_FOLDER
fix #1160
2025-09-14 17:48:41 +07:00
devlikepro 0c388ee3a7 [core] ChatWoot - set link preview
fix #1162
fix #1274
2025-09-14 17:48:40 +07:00
devlikepro fb5b3e2964 [core] Use ChatWootConfig 2025-09-14 17:48:39 +07:00
devlikepro d44eaa5b41 [core] Disable commands.server
fix #1182
fix #1176
2025-09-14 17:48:38 +07:00
devlikepro 2eaf05df27 [core] Update i18n 2025-09-14 17:48:37 +07:00
devlikepro 9e5431e205 [core] Update typescript 2025-09-14 17:48:35 +07:00
devlikepro 26c3102af8 [core] ChatWoot - disable app
fix #1260
2025-09-14 17:48:34 +07:00
devlikepro 104f06ef2b [core] ChatWoot - docker compose extra_hosts 2025-09-14 17:48:34 +07:00
devlikepro 93c8654e84 [core] Up chatwoot 4.5.2 2025-09-14 17:48:34 +07:00
devlikepro a7261790bd [core] Add ignore.broadcast
fix #1346
2025-09-14 17:48:34 +07:00
devlikepro d8d6e521f6 [core] Add excel to examples 2025-09-14 16:57:51 +07:00
devlikepro 02497bcd40 [core] 2025.9.2
Release / WEBJS - chromium - linux/arm64 - arm (push) Waiting to run
Release / GOWS - none - amd64 - gows (push) Waiting to run
Release / GOWS - none - linux/arm64 - gows-arm (push) Waiting to run
Release / NOWEB - none - amd64 - noweb (push) Waiting to run
Release / NOWEB - none - linux/arm64 - noweb-arm (push) Waiting to run
Release / WEBJS - chrome - amd64 - chrome (push) Waiting to run
Release / WEBJS - chromium - amd64 - latest (push) Waiting to run
2025-09-05 11:19:04 +07:00
devlikepro b498e15620 [core] NOWEB - Fix message.any fromMe: 1
fix #1318
fix #1338
2025-09-05 11:19:04 +07:00
devlikepro 1288d312c0 [core] Update Dashboard - WAHA_API_KEY warning 2025-09-05 11:19:04 +07:00
devlikepro 84cd350c42 [core] 2025.9.1
Release / WEBJS - chrome - amd64 - chrome (push) Waiting to run
Release / WEBJS - chromium - amd64 - latest (push) Waiting to run
Release / WEBJS - chromium - linux/arm64 - arm (push) Waiting to run
Release / GOWS - none - amd64 - gows (push) Waiting to run
Release / GOWS - none - linux/arm64 - gows-arm (push) Waiting to run
Release / NOWEB - none - amd64 - noweb (push) Waiting to run
Release / NOWEB - none - linux/arm64 - noweb-arm (push) Waiting to run
2025-09-02 18:14:14 +07:00
devlikepro 100bf89074 [core] WEBJS - fix STARTING session
fix #1301
fix #1315
fix #1314
fix #1312
2025-09-02 18:14:14 +07:00
devlikepro d66c80f95a [core] 2025.8.3
Release / WEBJS - chrome - amd64 - chrome (push) Waiting to run
Release / WEBJS - chromium - amd64 - latest (push) Waiting to run
Release / WEBJS - chromium - linux/arm64 - arm (push) Waiting to run
Release / GOWS - none - amd64 - gows (push) Waiting to run
Release / GOWS - none - linux/arm64 - gows-arm (push) Waiting to run
Release / NOWEB - none - amd64 - noweb (push) Waiting to run
Release / NOWEB - none - linux/arm64 - noweb-arm (push) Waiting to run
2025-08-30 18:30:07 +07:00
devlikepro 26f04bbd94 [core] Up Dashboard 2025-08-30 18:30:06 +07:00
devlikepro 6582c03823 [core] Up GOWS 2025-08-30 18:30:06 +07:00
devlikepro abacaf2e02 [core] WEBJS - fix STARTING session
fix #1298
fix #1299
fix #1300
2025-08-30 18:30:05 +07:00
devlikepro c65d8b9856 [core] GOWS - chat /unread 2025-08-30 18:30:05 +07:00
devlikepro 8f94cf06de [core] WEBJS - add webjs.tagsEventsOn config 2025-08-30 18:30:04 +07:00
devlikepro 94c1c16de8 [core] Use oxlint instead of eslint 2025-08-30 18:30:03 +07:00
devlikepro e69d992725 [core] Ignore - Status, Groups, Broadcast
fix #1142
fix #1259
fix #1190
2025-08-30 18:30:03 +07:00
devlikepro e6c5dd6e22 [core] eslint - no project 2025-08-30 18:30:02 +07:00
devlikepro 5486d4874a [core] Refactor media and processIncomingMessage 2025-08-30 18:30:02 +07:00
devlikepro 9d9d8e03fe [core] Add @types 2025-08-30 18:30:02 +07:00
devlikepro 6ee50243a2 [core] 2025.8.2
Release / GOWS - none - linux/arm64 - gows-arm (push) Waiting to run
Release / NOWEB - none - amd64 - noweb (push) Waiting to run
Release / NOWEB - none - linux/arm64 - noweb-arm (push) Waiting to run
Release / WEBJS - chrome - amd64 - chrome (push) Waiting to run
Release / WEBJS - chromium - amd64 - latest (push) Waiting to run
Release / WEBJS - chromium - linux/arm64 - arm (push) Waiting to run
Release / GOWS - none - amd64 - gows (push) Waiting to run
2025-08-25 10:31:26 +07:00
devlikepro 3ec078f9c1 [core] Update Dashboard 2025-08-25 10:31:26 +07:00
devlikepro 4c3209be6d [core] GOWS v1.0.12 2025-08-25 10:31:26 +07:00
devlikepro 7fe8f9a8dc [core] put "stack" last in errors 2025-08-25 10:31:25 +07:00
devlikepro 7fa2b29418 [core] GOWS - /api/sendPollVote
fix #1077
2025-08-25 10:31:25 +07:00
devlikepro dd4edd5a85 [core] show the engine name in errors 2025-08-25 10:31:24 +07:00
devlikepro b85df5a0d3 [core] GOWS - /api/sendLocation
fix #1250
2025-08-25 10:31:24 +07:00
devlikepro 1a7bec3d4e [core] NOWEB - add title for location, convert to right jid 2025-08-25 10:31:23 +07:00
devlikepro 727c4b454f [core] GOWS - Fix poll key
fix #1028
2025-08-25 10:31:23 +07:00
devlikepro 4ba86b34a9 [core] Add me.lid - GOWS, NOWEB 2025-08-25 10:31:22 +07:00
devlikepro 277a35aed9 [core] Add /api/sendList - NOWEB, GOWS
fix #393
fix #482
fix #715
fix #938
2025-08-25 10:31:21 +07:00
devlikepro 28c1ffa09f [core] ChatWoot - fix link preview for big files issue
fix #1162
2025-08-25 10:31:21 +07:00
devlikepro 10595680a9 [core] Dashboard - add zh - Simplified Chinese
fix #1193
2025-08-25 10:31:21 +07:00
devlikepro 577298dbc5 [core] ChatWoot - add zh-CN - Simplified Chinese
fix #1193
2025-08-25 10:31:21 +07:00
devlikepro 49ca4dfbf7 [core] rm LocaleCode 2025-08-25 10:31:21 +07:00
devlikepro 89ec47deec [core] 2025.8.1
Release / WEBJS - chrome - amd64 - chrome (push) Waiting to run
Release / WEBJS - chromium - amd64 - latest (push) Waiting to run
Release / WEBJS - chromium - linux/arm64 - arm (push) Waiting to run
Release / GOWS - none - amd64 - gows (push) Waiting to run
Release / GOWS - none - linux/arm64 - gows-arm (push) Waiting to run
Release / NOWEB - none - amd64 - noweb (push) Waiting to run
Release / NOWEB - none - linux/arm64 - noweb-arm (push) Waiting to run
2025-08-05 15:53:42 +07:00
devlikepro a043a46457 [core] API - limited characters in the session name
fix #1163
2025-08-05 15:53:42 +07:00
devlikepro eb8e3a0fd1 [core] WEBJS - fix create group
fix #1145
2025-08-05 15:53:41 +07:00
devlikepro fc25df8104 [core] NOWEB - fix channel list
fix #1177
2025-08-05 15:53:41 +07:00
devlikepro d8ffd3e543 [core] 2025.7.11
Release / WEBJS - chrome - amd64 - chrome (push) Waiting to run
Release / WEBJS - chromium - amd64 - latest (push) Waiting to run
Release / WEBJS - chromium - linux/arm64 - arm (push) Waiting to run
Release / GOWS - none - amd64 - gows (push) Waiting to run
Release / GOWS - none - linux/arm64 - gows-arm (push) Waiting to run
Release / NOWEB - none - amd64 - noweb (push) Waiting to run
Release / NOWEB - none - linux/arm64 - noweb-arm (push) Waiting to run
2025-08-03 13:59:23 +07:00
devlikepro ea4ec9310d [core] WEBJS - fix Caught error, dropping value from, event: 'message.ack'
fix #1178
2025-08-03 13:59:22 +07:00
devlikepro 479ec85e0d [core] Dashboard - i18n 2025-08-03 13:59:22 +07:00
devlikepro 3e4b450260 [core] ChatWoot - find chat id - jid, lid, chat id 2025-08-03 13:59:22 +07:00
devlikepro f6a81949d0 [core] ChatWoot - Urdu 2025-08-03 13:59:21 +07:00
devlikepro 6896962606 [core] Always show list of chatwoot languages
fix #1138
2025-08-03 13:59:21 +07:00
devlikepro 9b6d6ab4b0 [core] Inline LOCALE_NAME in yaml 2025-08-03 13:59:21 +07:00
102 changed files with 6939 additions and 2670 deletions

No files matched your search

+1
View File
@@ -1,2 +1,3 @@
src/core/engines/gows/grpc/*
src/core/engines/gows/proto/*
src/core/engines/webjs/_lodash.js
-36
View File
@@ -1,36 +0,0 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin', 'simple-import-sort'],
extends: [
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'prettier/@typescript-eslint',
],
root: true,
env: {
node: true,
jest: true,
},
rules: {
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-inferrable-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'lines-between-class-members': [
'error',
'always',
{ exceptAfterSingleLine: true },
],
},
};
+15
View File
@@ -0,0 +1,15 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["typescript"],
"env": {
"builtin": true
},
"rules": {
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/ban-ts-comment": "off",
"no-unused-vars": "off"
}
}
+5 -11
View File
@@ -17,18 +17,12 @@ repos:
language: python
stages: [commit-msg]
- repo: local
hooks:
- id: lint
stages: [pre-commit]
name: lint
- id: oxlint
name: oxlint
entry: npx oxlint --fix --deny-warnings
language: system
entry: bash -c '$HOME/.nvm/nvm-exec npm run lint-cache'
pass_filenames: false
files: |
(?x)^(
src/.*|
)$
pass_filenames: true
files: \.(ts|tsx|js|jsx)$
- repo: https://github.com/pre-commit/mirrors-prettier
rev: 'v3.1.0'
+1 -1
View File
@@ -162,7 +162,7 @@ RUN if [ "$USE_BROWSER" = "chromium" ]; then \
# Install Chrome
# Available versions:
# https://www.ubuntuupdates.org/package/google_chrome/stable/main/base/google-chrome-stable
ARG CHROME_VERSION="137.0.7151.103-1"
ARG CHROME_VERSION="140.0.7339.80-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 \
+3 -1
View File
@@ -24,10 +24,12 @@ services:
- 8.8.8.8
base: &base
image: chatwoot/chatwoot:v4.3.0
image: chatwoot/chatwoot:v4.5.2
env_file: .chatwoot.env ## Change this file for customized env variables
volumes:
- chatwoot_storage:/app/storage
extra_hosts:
- 'host.docker.internal:host-gateway'
logging:
driver: 'json-file'
options:
Binary file not shown.
+2 -1
View File
@@ -7,7 +7,8 @@
"dashboard/**",
"core/engines/webjs/*",
"plus/engines/webjs/*",
"apps/chatwoot/i18n/*.yaml"
"apps/chatwoot/i18n/locales/*.yaml",
"apps/chatwoot/i18n/locales/*.yml"
],
"watchAssets": true
}
+10 -12
View File
@@ -14,8 +14,8 @@
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"start:prod-exit": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix --max-warnings=0",
"lint-cache": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix --cache --max-warnings=0",
"lint": "oxlint --deny-warnings",
"lint-fix": "oxlint --fix --deny-warnings",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
@@ -71,6 +71,7 @@
"fs-extra": "^11.2.0",
"google-protobuf": "^3.21.4",
"https-proxy-agent": "^7.0.0",
"ical.js": "^2.2.1",
"ioredis": "^5",
"joi": "^17.13.3",
"knex": "^3.1.0",
@@ -90,7 +91,7 @@
"pino-pretty": "^11.2.1",
"pretty-bytes": "5.6.0",
"promise-retry": "^2.0.1",
"puppeteer": "^24.10.0",
"puppeteer": "^24.19.0",
"qrcode": "^1.5.1",
"qrcode-terminal": "^0.12.0",
"reflect-metadata": "^0.1.13",
@@ -110,7 +111,7 @@
"resolutions": {
"bufferutil": "^4.0.8",
"ws": "^8.18.0",
"puppeteer": "^24.10.0",
"puppeteer": "^24.19.0",
"whatwg-url": "13.0.0",
"libsignal": "github:devlikeapro/libsignal-node#fork-master",
"axios": "^1.9.0"
@@ -124,23 +125,20 @@
"@types/express": "^4.17.3",
"@types/jest": "26.0.10",
"@types/node": "^18.14.2",
"@types/node-fetch": "^2.6.13",
"@types/semver": "^7.7.0",
"@types/supertest": "^2.0.8",
"@typescript-eslint/eslint-plugin": "3.9.1",
"@typescript-eslint/parser": "3.9.1",
"eslint": "7.7.0",
"eslint-config-prettier": "^6.10.0",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-simple-import-sort": "^10.0.0",
"grpc-tools": "^1.13.0",
"jest": "^29.7.0",
"oxlint": "^1.13.0",
"prettier": "^1.19.1",
"protoc-gen-ts": "^0.8.7",
"supertest": "^4.0.2",
"ts-jest": "^29.1.3",
"ts-loader": "^6.2.1",
"ts-node": "^10.9.1",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.1.0",
"typescript": "^4.8.4",
"typescript": "5.9.2",
"yargs": "^17.7.2"
},
"jest": {
+26 -1
View File
@@ -16,6 +16,7 @@ import {
transformAck,
} from '@waha/structures/chats.dto';
import { SendButtonsRequest } from '@waha/structures/chatting.buttons.dto';
import { SendListRequest } from '@waha/structures/chatting.list.dto';
import { SessionManager } from '../core/abc/manager.abc';
import {
@@ -31,6 +32,7 @@ import {
MessageLinkPreviewRequest,
MessageLocationRequest,
MessagePollRequest,
MessagePollVoteRequest,
MessageReactionRequest,
MessageReplyRequest,
MessageStarRequest,
@@ -121,8 +123,9 @@ export class ChattingController {
@Post('/sendButtons')
@ApiOperation({
summary: 'Send buttons (interactive message)',
summary: 'Send buttons message (interactive)',
description: 'Send Buttons',
deprecated: true,
})
@UsePipes(new WAHAValidationPipe())
async sendButtons(@Body() request: SendButtonsRequest) {
@@ -130,6 +133,17 @@ export class ChattingController {
return whatsapp.sendButtons(request);
}
@Post('/sendList')
@ApiOperation({
summary: 'Send a list message (interactive)',
description: 'Send a List message with sections and rows',
})
@UsePipes(new WAHAValidationPipe())
async sendList(@Body() request: SendListRequest) {
const whatsapp = await this.manager.getWorkingSession(request.session);
return whatsapp.sendList(request);
}
@Post('/forwardMessage')
async forwardMessage(
@Body() request: MessageForwardRequest,
@@ -193,6 +207,17 @@ export class ChattingController {
return whatsapp.sendPoll(request);
}
@Post('/sendPollVote')
@ApiOperation({
summary: 'Vote on a poll',
description: 'Cast vote(s) on an existing poll message',
})
@UsePipes(new WAHAValidationPipe())
async sendPollVote(@Body() request: MessagePollVoteRequest) {
const whatsapp = await this.manager.getWorkingSession(request.session);
return whatsapp.sendPollVote(request);
}
@Post('/sendLocation')
async sendLocation(@Body() request: MessageLocationRequest) {
const whatsapp = await this.manager.getWorkingSession(request.session);
+8
View File
@@ -50,3 +50,11 @@ export function HasBeenRetried(job: Job): boolean {
const attemptsMade = job.attemptsMade + 1;
return attemptsMade > 1;
}
export function JobLink(job: Job): { text: string; url: string } {
const text = `${job.queueName} => ${job.id}`;
const url = `http://localhost:3000/jobs/queue/${encodeURIComponent(
job.queueName,
)}/${job.id}`;
return { text: text, url: url };
}
+9 -2
View File
@@ -48,10 +48,17 @@ export class AppsController {
return await this.appsService.create(this.manager, app);
}
@Get('/:id')
@ApiOperation({ summary: 'Get app by ID' })
@UsePipes(new WAHAValidationPipe())
async get(@Param('id') id: string): Promise<App> {
return await this.appsService.get(this.manager, id);
}
@Put('/:id')
@ApiOperation({ summary: 'Update an existing app' })
@UsePipes(new WAHAValidationPipe())
async update(@Param('id') id: string, @Body() app: App): Promise<void> {
async update(@Param('id') id: string, @Body() app: App): Promise<App> {
if (!app.id) {
app.id = id;
} else if (app.id !== id) {
@@ -60,7 +67,7 @@ export class AppsController {
);
}
await this.appsService.update(this.manager, app);
return await this.appsService.update(this.manager, app);
}
@Delete('/:id')
+2 -2
View File
@@ -6,8 +6,8 @@ import * as basicAuth from 'express-basic-auth';
export function BullAuthMiddleware() {
let username = process.env.WAHA_DASHBOARD_USERNAME;
let password = process.env.WAHA_DASHBOARD_PASSWORD;
const enabled = parseBool(process.env.WAHA_DASHBOARD_ENABLED);
if (!enabled) {
if (process.env.WAHA_DASHBOARD_ENABLED) {
const enabled = parseBool(process.env.WAHA_DASHBOARD_ENABLED);
// Generate a random uuid4 username /password to prevent access
username = 'admin';
password = crypto.randomUUID();
+18 -1
View File
@@ -1,6 +1,13 @@
import { ChatWootAppConfig } from '@waha/apps/chatwoot/dto/config.dto';
import { Type } from 'class-transformer';
import { IsEnum, IsString, ValidateNested } from 'class-validator';
import {
IsBoolean,
IsEnum,
IsOptional,
IsString,
ValidateNested,
} from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export type AllowedAppConfig = ChatWootAppConfig;
@@ -19,6 +26,16 @@ export class App<T extends AllowedAppConfig = any> {
@IsEnum(AppName)
app: AppName;
@ApiProperty({
description:
'Enable or disable this app without deleting it. If omitted, treated as enabled (true).',
required: false,
default: true,
})
@IsOptional()
@IsBoolean()
enabled?: boolean = true;
@ValidateNested()
@Type((options) => {
if (options && options.object && options.object.app) {
@@ -0,0 +1,26 @@
import { Knex } from 'knex';
exports.up = async function (knex: Knex) {
// Add a boolean column 'enabled' defaulting to true and not nullable.
const hasColumn = await knex.schema.hasColumn('apps', 'enabled');
if (!hasColumn) {
await knex.schema.alterTable('apps', (table) => {
table.boolean('enabled').defaultTo(true);
});
// Ensure existing rows have enabled=true
await knex('apps').update({ enabled: true });
// Make the column NOT NULL
await knex.schema.alterTable('apps', (table) => {
table.boolean('enabled').notNullable().defaultTo(true).alter();
});
}
};
exports.down = async function (knex: Knex) {
const hasColumn = await knex.schema.hasColumn('apps', 'enabled');
if (hasColumn) {
await knex.schema.alterTable('apps', (table) => {
table.dropColumn('enabled');
});
}
};
@@ -21,11 +21,15 @@ export class AppsDisabledService implements IAppsService {
throw new AppsIsDisabledError();
}
async get(manager: SessionManager, appId: string): Promise<App> {
throw new AppsIsDisabledError();
}
async create(manager: SessionManager, app: App): Promise<App> {
throw new AppsIsDisabledError();
}
async update(manager: SessionManager, app: App) {
async update(manager: SessionManager, app: App): Promise<App> {
throw new AppsIsDisabledError();
}
@@ -58,15 +58,32 @@ export class AppsEnabledService implements IAppsService {
}
const service = this.getAppService(app);
await service.beforeCreated(app);
service.validate(app);
// Only run beforeCreated when app is enabled (default true if omitted)
if (app.enabled !== false) {
await service.beforeCreated(app);
}
const result = await repo.save(app);
await this.restartIfRunning(manager, app.session);
if (app.enabled) {
await this.restartIfRunning(manager, app.session);
}
delete result.pk;
return result;
}
async update(manager: SessionManager, app: App) {
async get(manager: SessionManager, appId: string): Promise<App> {
const knex = manager.store.getWAHADatabase();
const repo = new AppRepository(knex);
const app = await repo.getById(appId);
if (!app) {
throw new NotFoundException(`App '${appId}' not found`);
}
delete (app as any).pk;
return app;
}
async update(manager: SessionManager, app: App): Promise<App> {
await this.checkSessionExists(manager, app.session);
const knex = manager.store.getWAHADatabase();
const repo = new AppRepository(knex);
@@ -86,11 +103,26 @@ export class AppsEnabledService implements IAppsService {
}
const service = this.getAppService(app);
await service.beforeUpdated(savedApp, app);
service.validate(app);
const hasEnabledChange = savedApp.enabled !== app.enabled;
if (hasEnabledChange) {
if (app.enabled) {
await service.beforeEnabled(savedApp, app);
} else {
await service.beforeDisabled(savedApp, app);
}
} else {
await service.beforeUpdated(savedApp, app);
}
await repo.update(app.id, app);
await this.restartIfRunning(manager, app.session);
return;
const updated = await repo.getById(app.id);
delete (updated as any)?.pk;
return updated!;
}
async delete(manager: SessionManager, appId: string) {
@@ -110,7 +142,7 @@ export class AppsEnabledService implements IAppsService {
async beforeSessionStart(session: WhatsappSession, store: DataStore) {
const knex = store.getWAHADatabase();
const repo = new AppRepository(knex);
const apps = await repo.getAllBySession(session.name);
const apps = await repo.getEnabledBySession(session.name);
for (const app of apps) {
const service = this.getAppService(app);
await service.beforeSessionStart(app, session);
+12
View File
@@ -5,8 +5,20 @@ import { WhatsappSession } from '@waha/core/abc/session.abc';
* Exact App service
*/
export interface IAppService {
validate(app: App): void;
beforeCreated(app: App): Promise<void>;
/**
* Called only when the app transitions from disabled -> enabled.
*/
beforeEnabled(savedApp: App, newApp: App): Promise<void>;
/**
* Called only when the app transitions from enabled -> disabled.
*/
beforeDisabled(savedApp: App, newApp: App): Promise<void>;
beforeUpdated(savedApp: App, newApp: App): Promise<void>;
beforeDeleted(app: App): Promise<void>;
+3 -1
View File
@@ -7,9 +7,11 @@ import { Knex } from 'knex';
export interface IAppsService {
list(manager: SessionManager, session: string): Promise<App[]>;
get(manager: SessionManager, appId: string): Promise<App>;
create(manager: SessionManager, app: App): Promise<App>;
update(manager: SessionManager, app: App): Promise<void>;
update(manager: SessionManager, app: App): Promise<App>;
delete(manager: SessionManager, appId: string): Promise<void>;
+50 -3
View File
@@ -16,7 +16,7 @@ export class AppRepository {
* Saves an app to the database
*/
async save(app: Omit<App, 'pk'>): Promise<AppDB> {
const appToSave = { ...app };
const appToSave: any = { ...app };
if (appToSave.config && typeof appToSave.config === 'object') {
appToSave.config = JSON.stringify(appToSave.config);
}
@@ -29,7 +29,25 @@ export class AppRepository {
private deserialize(app: AppDB): AppDB {
const parsedConfig =
typeof app.config === 'string' ? JSON.parse(app.config) : app.config;
return { ...app, config: parsedConfig };
// Ensure 'enabled' is boolean (SQLite may return 0/1)
let enabled: boolean;
switch (app.enabled as any) {
case undefined:
case true:
case 1:
case '1':
enabled = true;
break;
case false:
case 0:
enabled = false;
break;
default:
enabled = true;
}
return { ...(app as any), enabled: enabled, config: parsedConfig } as AppDB;
}
private serialize<T extends Partial<App>>(app: T): T {
@@ -51,6 +69,20 @@ export class AppRepository {
return this.deserialize(app);
}
/**
* Gets an enabled app by id, or null if not found or disabled.
*/
async findEnabledAppById(id: string): Promise<AppDB | null> {
const app = await this.knex(this.tableName)
.where('id', id)
.andWhere('enabled', true)
.first();
if (!app) {
return null;
}
return this.deserialize(app);
}
/**
* Gets all apps
*/
@@ -62,11 +94,26 @@ export class AppRepository {
.then((apps) => apps.map((app) => this.deserialize(app)));
}
/**
* Gets only enabled apps for a session.
*/
async getEnabledBySession(session: string): Promise<AppDB[]> {
return this.knex(this.tableName)
.select('*')
.where('session', session)
.andWhere('enabled', true)
.orderBy('id', 'asc')
.then((apps) => apps.map((app) => this.deserialize(app)));
}
/**
* Updates an app
*/
async update(id: string, app: Partial<Omit<App, 'id'>>): Promise<void> {
const appToUpdate = this.serialize(app);
const appToUpdate: any = this.serialize(app);
if (appToUpdate.enabled === undefined) {
delete appToUpdate.enabled; // keep current value
}
await this.knex(this.tableName).where('id', id).update(appToUpdate);
}
+2 -1
View File
@@ -9,6 +9,7 @@ import { BullAuthMiddleware } from '@waha/apps/app_sdk/auth';
import { AppsDisabledService } from '@waha/apps/app_sdk/services/AppsDisabledService';
import { AppsEnabledService } from '@waha/apps/app_sdk/services/AppsEnabledService';
import { AppsService } from '@waha/apps/app_sdk/services/IAppsService';
import { ChatwootLocalesController } from '@waha/apps/chatwoot/api/chatwoot.locales.controller';
import { ChatWootExports } from '@waha/apps/chatwoot/chatwoot.module';
import { parseBool } from '@waha/helpers';
import { RMutexModule } from '@waha/modules/rmutex';
@@ -88,7 +89,7 @@ const AppsDisabled = {
},
],
imports: [],
controllers: [AppsController],
controllers: [AppsController, ChatwootLocalesController],
};
function checkApiKey() {
@@ -1,6 +1,7 @@
import { Controller, Get } from '@nestjs/common';
import { sortBy } from 'lodash';
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
import { LOCALE_NAMES } from '@waha/apps/chatwoot/locale';
import { i18n } from '@waha/apps/chatwoot/i18n';
interface LanguageResponse {
name: string;
@@ -17,9 +18,14 @@ export class ChatwootLocalesController {
description: 'Get available languages for Chatwoot app',
})
getLanguages(): LanguageResponse[] {
return Object.entries(LOCALE_NAMES).map(([locale, name]) => ({
name: name,
locale: locale,
}));
const locales = i18n.available();
const priority = ['en-US', 'pt-BR', 'es-ES'];
return sortBy(locales, [
(x) => {
const idx = priority.indexOf(x.locale);
return idx === -1 ? Number.MAX_SAFE_INTEGER : idx;
},
'locale',
]);
}
}
@@ -1,15 +1,26 @@
import { Body, Controller, Param, Post } from '@nestjs/common';
import {
Body,
Controller,
NotFoundException,
Param,
Post,
} from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { GetChatID } from '@waha/apps/chatwoot/client/ids';
import { FindChatID } from '@waha/apps/chatwoot/client/ids';
import { EventName, MessageType } from '@waha/apps/chatwoot/client/types';
import { INBOX_CONTACT_CHAT_ID } from '@waha/apps/chatwoot/const';
import { InboxData } from '@waha/apps/chatwoot/consumers/types';
import { ChatWootQueueService } from '@waha/apps/chatwoot/services/ChatWootQueueService';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { AppRepository } from '@waha/apps/app_sdk/storage/AppRepository';
@Controller('webhooks/chatwoot/')
@ApiTags('🧩 Apps')
export class ChatwootWebhookController {
constructor(private readonly chatWootQueueService: ChatWootQueueService) {}
constructor(
private readonly chatWootQueueService: ChatWootQueueService,
private readonly manager: SessionManager,
) {}
@Post(':session/:id')
@ApiOperation({
@@ -41,9 +52,17 @@ export class ChatwootWebhookController {
body: body,
};
// Skip if app is disabled or does not exist
const knex = this.manager.store.getWAHADatabase();
const repo = new AppRepository(knex);
const app = await repo.findEnabledAppById(id);
if (!app || app.session !== session) {
throw new NotFoundException(`App '${id}' not found`);
}
// Check if it's a command message (sent to the special inbox contact)
const sender = body?.conversation?.meta?.sender;
const chatId = GetChatID(sender);
const chatId = FindChatID(sender);
const isCommandsChat = chatId === INBOX_CONTACT_CHAT_ID;
// Check if it's a deleted message
-1
View File
@@ -1,4 +1,3 @@
import { Module } from '@nestjs/common';
import { RegisterAppQueue } from '@waha/apps/app_sdk/BullUtils';
import {
ExponentialRetriesJobOptions,
@@ -9,7 +9,7 @@ import { Conversation } from '@waha/apps/chatwoot/client/Conversation';
import { ConversationAPI } from '@waha/apps/chatwoot/client/ConversationAPI';
import { ChatWootAPIConfig } from '@waha/apps/chatwoot/client/interfaces';
import { InboxContactInfo } from '@waha/apps/chatwoot/contacts/InboxContactInfo';
import { Locale } from '@waha/apps/chatwoot/locale';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { CacheForConfig } from '../cache/ConversationCache';
import { IConversationCache } from '../cache/IConversationCache';
+13
View File
@@ -15,6 +15,19 @@ export function GetChatID(contact: any): string | null {
return contact?.custom_attributes?.[AttributeKey.WA_CHAT_ID];
}
export function FindChatID(contact: any): string | null {
if (GetJID(contact)) {
return GetJID(contact);
}
if (GetLID(contact)) {
return GetLID(contact);
}
if (GetChatID(contact)) {
return GetChatID(contact);
}
return null;
}
export function SerializeWhatsAppKey(message: WhatsAppMessage): string {
const key = {
id: message.message_id,
+3 -3
View File
@@ -1,7 +1,7 @@
import { AppConsumer } from '@waha/apps/app_sdk/AppConsumer';
import { JobLoggerWrapper } from '@waha/apps/app_sdk/JobLoggerWrapper';
import { HasBeenRetried } from '@waha/apps/app_sdk/JobUtils';
import { GetChatID } from '@waha/apps/chatwoot/client/ids';
import { FindChatID } from '@waha/apps/chatwoot/client/ids';
import { EventName } from '@waha/apps/chatwoot/client/types';
import { ChatWootConversationKey } from '@waha/apps/chatwoot/consumers/mutex';
import { InboxData } from '@waha/apps/chatwoot/consumers/types';
@@ -10,7 +10,6 @@ import {
ChatIDNotFoundForContactError,
PhoneNumberNotFoundInWhatsAppError,
} from '@waha/apps/chatwoot/errors';
import { TKey } from '@waha/apps/chatwoot/locale';
import { WAHASessionAPI } from '@waha/apps/chatwoot/session/WAHASelf';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
@@ -18,6 +17,7 @@ import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { AppRepository } from '../../storage';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
/**
* Base class for ChatWoot inbox consumers
@@ -124,7 +124,7 @@ export async function LookupAndCheckChatId(
body: any,
): Promise<string> {
const sender = body.conversation.meta.sender;
let chatId = GetChatID(sender);
let chatId = FindChatID(sender);
if (!chatId && sender.phone_number) {
const existResult = await session.contactCheckExists(sender.phone_number);
if (!existResult.numberExists) {
+28 -10
View File
@@ -7,13 +7,15 @@ import { MessageType } from '@waha/apps/chatwoot/client/types';
import { ChatWootInboxMessageConsumer } from '@waha/apps/chatwoot/consumers/inbox/base';
import { QueueName } from '@waha/apps/chatwoot/consumers/QueueName';
import { DIContainer } from '@waha/apps/chatwoot/di/DIContainer';
import { Locale, TKey } from '@waha/apps/chatwoot/locale';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { WAHASelf } from '@waha/apps/chatwoot/session/WAHASelf';
import { SessionStatusEmoji } from '@waha/apps/chatwoot/SessionStatusEmoji';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { ChatWootCommandsConfig } from '@waha/apps/chatwoot/dto/config.dto';
@Processor(QueueName.INBOX_COMMANDS, { concurrency: JOB_CONCURRENCY })
export class ChatWootInboxCommandsConsumer extends ChatWootInboxMessageConsumer {
@@ -36,11 +38,14 @@ export class ChatWootInboxCommandsConsumer extends ChatWootInboxMessageConsumer
job.data.session,
container.Locale(),
container.WAHASelf(),
container.ChatWootConfig().commands,
);
return await handler.handle(body);
}
}
const CommandServerPrefix = 'server ';
enum Command {
STATUS = 'status',
RESTART = 'restart',
@@ -99,6 +104,7 @@ class SessionCommandHandler {
private session: string,
private l: Locale,
private waha: WAHASelf,
private commands: ChatWootCommandsConfig,
) {}
async handle(message: any) {
@@ -107,6 +113,13 @@ class SessionCommandHandler {
this.logger.info(
`Executing command ${command} for session ${this.session}`,
);
const warning = this.commandDisabledWarning(command);
const conversation = await this.repo.InboxNotifications();
if (warning) {
const text = this.l.key(warning).render();
await conversation.incoming(text);
return;
}
switch (command) {
case Command.RESTART:
await this.waha.restart(this.session);
@@ -117,7 +130,6 @@ class SessionCommandHandler {
case Command.LOGOUT:
this.logger.info(`Logging out session ${this.session}`);
await this.waha.logout(this.session);
const conversation = await this.repo.InboxNotifications();
const text = this.l.key(TKey.APP_LOGOUT_SUCCESS).render();
await conversation.incoming(text);
break;
@@ -128,7 +140,6 @@ class SessionCommandHandler {
case Command.STATUS: {
this.logger.info(`Getting status for session ${this.session}`);
const session = await this.waha.get(this.session);
const conversation = await this.repo.InboxNotifications();
const emoji = SessionStatusEmoji(session.status);
const text = this.l.key(TKey.APP_SESSION_CURRENT_STATUS).render({
emoji: emoji,
@@ -144,7 +155,6 @@ class SessionCommandHandler {
this.logger.info('Getting server version and status');
const version = await this.waha.serverVersion();
const status = await this.waha.serverStatus();
const conversation = await this.repo.InboxNotifications();
const text = this.l.key(TKey.APP_SERVER_VERSION_AND_STATUS).render({
version: JSON.stringify(version, null, 2),
status: JSON.stringify(status, null, 2),
@@ -154,7 +164,6 @@ class SessionCommandHandler {
}
case Command.SERVER_REBOOT: {
this.logger.info('Rebooting server (graceful)');
const conversation = await this.repo.InboxNotifications();
const text = this.l.key(TKey.APP_SERVER_REBOOT).render();
await conversation.incoming(text);
await this.waha.serverReboot(false);
@@ -162,14 +171,12 @@ class SessionCommandHandler {
}
case Command.SERVER_REBOOT_FORCE: {
this.logger.info('Rebooting server (force)');
const conversation = await this.repo.InboxNotifications();
const text = this.l.key(TKey.APP_SERVER_REBOOT_FORCE).render();
await conversation.incoming(text);
await this.waha.serverReboot(true);
break;
}
case Command.QR: {
const conversation = await this.repo.InboxNotifications();
const content = await this.waha.qr(this.session);
const message = AttachmentFromBuffer(content, 'qr.jpg');
message.message_type = MessageType.INCOMING;
@@ -178,7 +185,6 @@ class SessionCommandHandler {
}
case Command.SCREENSHOT: {
this.logger.info(`Getting screenshot for session ${this.session}`);
const conversation = await this.repo.InboxNotifications();
const content = await this.waha.screenshot(this.session);
const message = AttachmentFromBuffer(content, 'screenshot.jpg');
message.message_type = MessageType.INCOMING;
@@ -186,8 +192,9 @@ class SessionCommandHandler {
break;
}
case Command.HELP: {
const conversation = await this.repo.InboxNotifications();
const text = this.l.key(TKey.APP_COMMANDS_LIST).render();
const text = this.l
.key(TKey.APP_COMMANDS_LIST)
.render({ commands: this.commands });
await conversation.incoming(text);
break;
}
@@ -196,6 +203,17 @@ class SessionCommandHandler {
}
}
/**
* Check if the command allowed for this session
* Returns `null` if allowed or the message key to show as an error
*/
commandDisabledWarning(command: Command): TKey | null {
if (!this.commands.server && command.startsWith(CommandServerPrefix)) {
return TKey.APP_COMMANDS_SERVER_DISABLED;
}
return null;
}
findCommand(text: string): Command {
// Remove empty from end
text = text.trim();
@@ -8,7 +8,6 @@ import {
} from '@waha/apps/chatwoot/consumers/inbox/base';
import { QueueName } from '@waha/apps/chatwoot/consumers/QueueName';
import { DIContainer } from '@waha/apps/chatwoot/di/DIContainer';
import { TKey } from '@waha/apps/chatwoot/locale';
import { EngineHelper } from '@waha/apps/chatwoot/session';
import { WAHASessionAPI } from '@waha/apps/chatwoot/session/WAHASelf';
import {
@@ -30,6 +29,12 @@ import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { SerializeWhatsAppKey } from '../../client/ids';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import {
ChatWootConfig,
LinkPreview,
} from '@waha/apps/chatwoot/dto/config.dto';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const mime = require('mime-types');
@@ -45,7 +50,7 @@ export class ChatWootInboxMessageCreatedConsumer extends ChatWootInboxMessageCon
}
ErrorHeaderKey(): TKey | null {
return TKey.WHATSAPP_ERROR_SENDING_MESSAGE;
return TKey.WHATSAPP_MESSAGE_SENDING_ERROR;
}
protected async Process(container: DIContainer, body, job: Job) {
@@ -55,6 +60,8 @@ export class ChatWootInboxMessageCreatedConsumer extends ChatWootInboxMessageCon
container.MessageMappingService(),
container.Logger(),
session,
container.ChatWootConfig(),
container.Locale(),
);
return await handler.handle(body);
}
@@ -65,6 +72,8 @@ export class MessageHandler {
private mappingService: MessageMappingService,
private logger: ILogger,
private session: WAHASessionAPI,
private config: ChatWootConfig,
private l: Locale,
) {}
async handle(body: any) {
@@ -78,9 +87,7 @@ export class MessageHandler {
}
const content = MarkdownToWhatsApp(message.content);
const attachments = message.attachments || [];
const results = [];
const multipleAttachments = attachments.length > 1;
let part = 1;
const replyTo = await this.getReplyTo(message).catch((err) => {
this.logger.error(`Error getting reply to message ID: ${err}`);
@@ -88,22 +95,28 @@ export class MessageHandler {
});
// Send text
if (attachments.length == 0 || multipleAttachments) {
const msg = await this.sendTextMessage(chatId, content, replyTo);
const attachments = message.attachments || [];
if (content && attachments.length !== 1) {
const textTemplate = this.l.key(TKey.CW_TO_WA_MESSAGE_TEXT);
const text = textTemplate.render({
content: content,
chatwoot: body,
});
const msg = await this.sendTextMessage(chatId, text, replyTo);
results.push(msg);
part = await this.saveMapping(message, msg, part);
this.logger.info(`Text message sent: ${msg.id}`);
}
// Send files
const fileMessageContent = multipleAttachments ? '' : content;
const captionTemplate = this.l.key(TKey.CW_TO_WA_MESSAGE_MEDIA_CAPTION);
for (const file of attachments) {
const msg = await this.sendFile(
chatId,
fileMessageContent,
file,
replyTo,
);
const caption = captionTemplate.render({
content: content,
chatwoot: body,
singleAttachment: attachments.length == 1,
});
const msg = await this.sendFile(chatId, caption, file, replyTo);
this.logger.info(
`File message sent: ${msg.id} - ${file.data_url} - ${file.file_type}`,
);
@@ -154,6 +167,10 @@ export class MessageHandler {
chatId: chatId,
text: content,
reply_to: replyTo,
linkPreview: [LinkPreview.LQ, LinkPreview.HQ].includes(
this.config.linkPreview,
),
linkPreviewHighQuality: this.config.linkPreview == LinkPreview.HQ,
};
const session = this.session;
await session.readMessages(chatId);
@@ -8,13 +8,13 @@ import {
} from '@waha/apps/chatwoot/consumers/inbox/base';
import { QueueName } from '@waha/apps/chatwoot/consumers/QueueName';
import { DIContainer } from '@waha/apps/chatwoot/di/DIContainer';
import { TKey } from '@waha/apps/chatwoot/locale';
import { WAHASessionAPI } from '@waha/apps/chatwoot/session/WAHASelf';
import { MessageMappingService } from '@waha/apps/chatwoot/storage';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
@Processor(QueueName.INBOX_MESSAGE_DELETED, { concurrency: JOB_CONCURRENCY })
export class ChatWootInboxMessageDeletedConsumer extends ChatWootInboxMessageConsumer {
@@ -27,7 +27,7 @@ export class ChatWootInboxMessageDeletedConsumer extends ChatWootInboxMessageCon
}
ErrorHeaderKey(): TKey | null {
return TKey.WHATSAPP_ERROR_REMOVING_MESSAGE;
return TKey.WHATSAPP_MESSAGE_REMOVING_ERROR;
}
protected async Process(container: DIContainer, body, job: Job) {
@@ -4,13 +4,13 @@ import { ChatWootInboxMessageConsumer } from '@waha/apps/chatwoot/consumers/inbo
import { MessageHandler } from '@waha/apps/chatwoot/consumers/inbox/message_created';
import { QueueName } from '@waha/apps/chatwoot/consumers/QueueName';
import { DIContainer } from '@waha/apps/chatwoot/di/DIContainer';
import { TKey } from '@waha/apps/chatwoot/locale';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { WAHASessionAPI } from '../../session/WAHASelf';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
@Processor(QueueName.INBOX_MESSAGE_UPDATED, { concurrency: JOB_CONCURRENCY })
export class ChatWootInboxMessageUpdatedConsumer extends ChatWootInboxMessageConsumer {
@@ -23,7 +23,7 @@ export class ChatWootInboxMessageUpdatedConsumer extends ChatWootInboxMessageCon
}
ErrorHeaderKey(): TKey | null {
return TKey.WHATSAPP_ERROR_SENDING_MESSAGE;
return TKey.WHATSAPP_MESSAGE_SENDING_ERROR;
}
protected async Process(container: DIContainer, body, job: Job) {
@@ -33,6 +33,8 @@ export class ChatWootInboxMessageUpdatedConsumer extends ChatWootInboxMessageCon
container.MessageMappingService(),
container.Logger(),
session,
container.ChatWootConfig(),
container.Locale(),
);
return await handler.handle(body);
}
@@ -5,13 +5,13 @@ import { MessageType } from '@waha/apps/chatwoot/client/types';
import { ScheduledData } from '@waha/apps/chatwoot/consumers/types';
import { DIContainer } from '@waha/apps/chatwoot/di/DIContainer';
import { ChatWootAppConfig } from '@waha/apps/chatwoot/dto/config.dto';
import { TKey } from '@waha/apps/chatwoot/locale';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { AppRepository } from '../../storage';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
/**
* Base class for ChatWoot scheduled consumers
@@ -4,7 +4,6 @@ import { JOB_CONCURRENCY } from '@waha/apps/app_sdk/constants';
import { ILogger } from '@waha/apps/app_sdk/ILogger';
import { QueueName } from '@waha/apps/chatwoot/consumers/QueueName';
import { DIContainer } from '@waha/apps/chatwoot/di/DIContainer';
import { TKey } from '@waha/apps/chatwoot/locale';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { CHANGELOG_URL, SUPPORT_US_URL } from '@waha/core/constants';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
@@ -14,6 +13,7 @@ import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { ChatWootScheduledConsumer } from './base';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
/**
* Scheduled consumer for version checking
@@ -26,7 +26,7 @@ export class CheckVersionConsumer extends ChatWootScheduledConsumer {
}
protected ErrorHeaderKey(): TKey {
return TKey.APP_SCHEDULED_JOB_ERROR;
return TKey.JOB_SCHEDULED_ERROR_HEADER;
}
/**
@@ -50,7 +50,7 @@ export class CheckVersionConsumer extends ChatWootScheduledConsumer {
return;
}
logger.info('WAHA is using the CORE version');
const supportMessage = locale.key(TKey.APP_CORE_VERSION_USED).render({
const supportMessage = locale.key(TKey.WAHA_CORE_VERSION_USED).render({
supportUrl: SUPPORT_US_URL,
});
await conversation.incoming(supportMessage);
@@ -74,7 +74,7 @@ export class CheckVersionConsumer extends ChatWootScheduledConsumer {
// Send a message to Chatwoot conversation about a new version
const locale = container.Locale();
const message = locale.key(TKey.APP_NEW_VERSION_AVAILABLE).render({
const message = locale.key(TKey.WAHA_NEW_VERSION_AVAILABLE).render({
currentVersion: currentVersion,
newVersion: latestVersion,
changelogUrl: CHANGELOG_URL,
@@ -3,13 +3,13 @@ import { Injectable } from '@nestjs/common';
import { JOB_CONCURRENCY } from '@waha/apps/app_sdk/constants';
import { QueueName } from '@waha/apps/chatwoot/consumers/QueueName';
import { DIContainer } from '@waha/apps/chatwoot/di/DIContainer';
import { TKey } from '@waha/apps/chatwoot/locale';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { ChatWootScheduledConsumer } from './base';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
/**
* Scheduled consumer for message cleanup tasks
@@ -26,7 +26,7 @@ export class MessageCleanupConsumer extends ChatWootScheduledConsumer {
}
protected ErrorHeaderKey(): TKey {
return TKey.APP_SCHEDULED_JOB_ERROR;
return TKey.JOB_SCHEDULED_ERROR_HEADER;
}
/**
+19 -15
View File
@@ -13,7 +13,7 @@ import { WhatsAppChatIdKey } from '@waha/apps/chatwoot/consumers/mutex';
import { EventData } from '@waha/apps/chatwoot/consumers/types';
import { WhatsAppContactInfo } from '@waha/apps/chatwoot/contacts/WhatsAppContactInfo';
import { DIContainer } from '@waha/apps/chatwoot/di/DIContainer';
import { Locale, TKey } from '@waha/apps/chatwoot/locale';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { WAHASelf, WAHASessionAPI } from '@waha/apps/chatwoot/session/WAHASelf';
import {
AppRepository,
@@ -35,7 +35,8 @@ import { sleep } from '@waha/utils/promiseTimeout';
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { ChatWootAppConfig } from '../../dto/config.dto';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { isJidBroadcast } from '@adiwajshing/baileys/lib/WABinary/jid-utils';
export function ListenEventsForChatWoot() {
return [
@@ -64,7 +65,7 @@ export abstract class ChatWootWAHABaseConsumer extends AppConsumer {
}
protected ErrorHeaderKey(): TKey | null {
return TKey.WHATSAPP_ERROR_RECEIVING_MESSAGE;
return TKey.WHATSAPP_MESSAGE_RECEIVING_ERROR;
}
private async ProcessAndReportErrors(job) {
@@ -183,8 +184,15 @@ class MessageReportInfo implements IMessageInfo {
}
}
export interface ChatWootMessagePartial {
content: string;
attachments: SendAttachment[];
private?: boolean;
}
export abstract class MessageBaseHandler<Payload extends WAMessageBase> {
constructor(
protected job: Job,
protected mappingService: MessageMappingService,
protected repo: ContactConversationService,
protected logger: ILogger,
@@ -194,12 +202,12 @@ export abstract class MessageBaseHandler<Payload extends WAMessageBase> {
protected waha: WAHASelf,
) {}
abstract getContent(payload: Payload): string;
protected abstract getMessage(
payload: Payload,
): Promise<ChatWootMessagePartial>;
abstract getReplyToWhatsAppID(payload: Payload): string | undefined;
abstract getAttachments(payload: Payload): Promise<SendAttachment[]>;
async handle(data: WAHAWebhook<Payload>) {
const payload = data.payload;
// Find the type as soon as possible for error reporting
@@ -262,7 +270,8 @@ export abstract class MessageBaseHandler<Payload extends WAMessageBase> {
private async buildChatWootMessage(
payload: Payload,
): Promise<conversation_message_create> {
let content: string = this.getContent(payload);
const message = await this.getMessage(payload);
let content = message.content;
// Format the content if the message from me
if (payload.fromMe && payload.source === MessageSource.APP) {
@@ -276,7 +285,7 @@ export abstract class MessageBaseHandler<Payload extends WAMessageBase> {
const chatId = payload.from;
// Add participant name to group messages
const manyParticipants = isJidGroup(chatId) || isJidStatusBroadcast(chatId);
const manyParticipants = isJidGroup(chatId) || isJidBroadcast(chatId);
if (!payload.fromMe && manyParticipants) {
const key = parseMessageIdSerialized(payload.id, true);
let participant = toCusFormat(key.participant);
@@ -290,11 +299,6 @@ export abstract class MessageBaseHandler<Payload extends WAMessageBase> {
participant: participant,
});
}
const attachments: SendAttachment[] = await this.getAttachments(payload);
if (!content && attachments.length > 0) {
// With no content ChatWoot render the message as an ugly big attachment
content = ' ';
}
const replyTo = await this.getReplyToChatWootMessageID(payload).catch(
(err) => {
this.logger.error(`Error getting reply to message ID: ${err}`);
@@ -306,8 +310,8 @@ export abstract class MessageBaseHandler<Payload extends WAMessageBase> {
return {
content: content,
message_type: type,
private: payload.fromMe,
attachments: attachments as any,
private: message.private ?? payload.fromMe,
attachments: message.attachments as any,
content_attributes: {
in_reply_to: replyTo,
},
+139 -4
View File
@@ -4,10 +4,11 @@ import { SendAttachment } from '@waha/apps/chatwoot/client/types';
import { QueueName } from '@waha/apps/chatwoot/consumers/QueueName';
import { EventData } from '@waha/apps/chatwoot/consumers/types';
import {
ChatWootMessagePartial,
ChatWootWAHABaseConsumer,
IMessageInfo,
MessageBaseHandler,
} from '@waha/apps/chatwoot/consumers/waha/base';
import { MessageBaseHandler } from '@waha/apps/chatwoot/consumers/waha/base';
import { WAHASessionAPI } from '@waha/apps/chatwoot/session/WAHASelf';
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/text';
import { SessionManager } from '@waha/core/abc/manager.abc';
@@ -18,6 +19,11 @@ import { WAMessage } from '@waha/structures/responses.dto';
import { WAHAWebhookMessageAny } from '@waha/structures/webhooks.dto';
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { JobLink } from '@waha/apps/app_sdk/JobUtils';
import { proto } from '@adiwajshing/baileys';
import * as lodash from 'lodash';
import { parseVCardV3, SimpleVCardInfo } from '@waha/core/vcard';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const mime = require('mime-types');
@@ -44,6 +50,7 @@ export class WAHAMessageAnyConsumer extends ChatWootWAHABaseConsumer {
const event: WAHAWebhookMessageAny = job.data.event as any;
const session = new WAHASessionAPI(event.session, container.WAHASelf());
const handler = new MessageAnyHandler(
job,
container.MessageMappingService(),
container.ContactConversationService(),
container.Logger(),
@@ -56,10 +63,135 @@ export class WAHAMessageAnyConsumer extends ChatWootWAHABaseConsumer {
}
}
function isEmptyString(content: string) {
if (!content) {
return true;
}
return content == '' || content == '\n';
}
class MessageAnyHandler extends MessageBaseHandler<WAMessage> {
getContent(payload: WAMessage): string {
const body = payload.body;
return WhatsappToMarkdown(body);
protected async getMessage(
payload: WAMessage,
): Promise<ChatWootMessagePartial> {
const protoMessage = this.getProtoMessage(payload);
let msg = await this.getTextMessage(payload, protoMessage);
if (msg) {
return msg;
}
msg = this.getLocationMessage(payload, protoMessage);
if (msg) {
return msg;
}
msg = this.getShareContactMessage(payload, protoMessage);
if (msg) {
return msg;
}
return this.getUnsupportedMessage();
}
protected async getTextMessage(
payload: WAMessage,
_: proto.Message | null,
): Promise<ChatWootMessagePartial | null> {
const attachments = await this.getAttachments(payload);
const content = this.l
.key(TKey.WA_TO_CW_MESSAGE)
.render({ payload: payload });
if (isEmptyString(content) && attachments.length == 0) {
return null;
}
return {
content: WhatsappToMarkdown(content),
attachments: attachments,
private: undefined,
};
}
private getLocationMessage(
payload: WAMessage,
message: proto.Message | null,
): ChatWootMessagePartial | null {
const hasLocation = !lodash.isEmpty(message?.locationMessage);
const hasLiveLocation = !lodash.isEmpty(message?.liveLocationMessage);
if (!hasLocation && !hasLiveLocation) {
return null;
}
const location = this.l.key(TKey.WA_TO_CW_MESSAGE_LOCATION).r({
payload,
message,
});
if (isEmptyString(location)) {
return null;
}
return {
content: location,
attachments: [],
private: undefined,
};
}
private getShareContactMessage(
_payload: WAMessage,
message: proto.Message | null,
): ChatWootMessagePartial | null {
let vcards: string[] = [];
if (!lodash.isEmpty(message?.contactsArrayMessage?.contacts)) {
vcards = message!.contactsArrayMessage!.contacts.map((c) => c.vcard);
} else if (!lodash.isEmpty(message?.contactMessage?.vcard)) {
vcards = [message!.contactMessage!.vcard];
}
if (vcards.length === 0) {
return null;
}
const attachments: SendAttachment[] = vcards.map((v, i) => ({
content: Buffer.from(v, 'utf8').toString('base64'),
encoding: 'base64',
filename: `vcard-${i + 1}.vcf`,
}));
let contacts: SimpleVCardInfo[] = [];
try {
contacts = vcards.map(parseVCardV3);
} catch (err) {
this.logger.error(
`Error parsing some vcards: vcards=${vcards}, err=${err}`,
);
}
const msg = this.l.key(TKey.WA_TO_CW_MESSAGE_CONTACTS).r({ contacts });
if (contacts.length === 0 && attachments.length === 0) {
return null;
}
return {
content: msg,
attachments: attachments,
private: undefined,
};
}
private getUnsupportedMessage(): ChatWootMessagePartial {
const unsupported = this.l
.key(TKey.WA_TO_CW_MESSAGE_UNSUPPORTED)
.render({ details: JobLink(this.job) });
return {
content: unsupported,
attachments: [],
private: true,
};
}
private getProtoMessage(payload: WAMessage): proto.Message | null {
// GOWS
if (payload._data.Message) {
return payload._data.Message;
}
// NOWEB
if (payload._data.message) {
return payload._data.message;
}
// WEBJS - not available
return null;
}
getReplyToWhatsAppID(payload: WAMessage): string {
@@ -72,6 +204,9 @@ class MessageAnyHandler extends MessageBaseHandler<WAMessage> {
}
async getAttachments(payload: WAMessage): Promise<SendAttachment[]> {
//
// WAHA
//
const hasMedia = payload.media?.url;
if (!hasMedia) {
return [];
@@ -3,11 +3,11 @@ import { JOB_CONCURRENCY } from '@waha/apps/app_sdk/constants';
import { QueueName } from '@waha/apps/chatwoot/consumers/QueueName';
import { EventData } from '@waha/apps/chatwoot/consumers/types';
import {
ChatWootMessagePartial,
ChatWootWAHABaseConsumer,
IMessageInfo,
} from '@waha/apps/chatwoot/consumers/waha/base';
import { MessageBaseHandler } from '@waha/apps/chatwoot/consumers/waha/base';
import { TKey } from '@waha/apps/chatwoot/locale';
import { WhatsappToMarkdown } from '@waha/apps/chatwoot/text';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { RMutexService } from '@waha/modules/rmutex/rmutex.service';
@@ -21,6 +21,8 @@ import { PinoLogger } from 'nestjs-pino';
import { SendAttachment } from '../../client/types';
import { WAHASessionAPI } from '../../session/WAHASelf';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { WAMessage } from '@waha/structures/responses.dto';
@Processor(QueueName.WAHA_MESSAGE_EDITED, { concurrency: JOB_CONCURRENCY })
export class WAHAMessageEditedConsumer extends ChatWootWAHABaseConsumer {
@@ -44,6 +46,7 @@ export class WAHAMessageEditedConsumer extends ChatWootWAHABaseConsumer {
const event: WAHAWebhookMessageEdited = job.data.event as any;
const session = new WAHASessionAPI(event.session, container.WAHASelf());
const handler = new MessageEditedHandler(
job,
container.MessageMappingService(),
container.ContactConversationService(),
container.Logger(),
@@ -57,12 +60,19 @@ export class WAHAMessageEditedConsumer extends ChatWootWAHABaseConsumer {
}
class MessageEditedHandler extends MessageBaseHandler<WAMessageEditedBody> {
getContent(payload: WAMessageEditedBody): string {
protected async getMessage(
payload: WAMessageEditedBody,
): Promise<ChatWootMessagePartial> {
const body = payload.body;
const formatted = WhatsappToMarkdown(body);
return this.l
const content = this.l
.key(TKey.MESSAGE_EDITED_IN_WHATSAPP)
.render({ text: formatted });
return {
content: content,
attachments: [],
private: undefined,
};
}
getReplyToWhatsAppID(payload: WAMessageEditedBody): string {
@@ -4,11 +4,11 @@ import { SendAttachment } from '@waha/apps/chatwoot/client/types';
import { QueueName } from '@waha/apps/chatwoot/consumers/QueueName';
import { EventData } from '@waha/apps/chatwoot/consumers/types';
import {
ChatWootMessagePartial,
ChatWootWAHABaseConsumer,
IMessageInfo,
} from '@waha/apps/chatwoot/consumers/waha/base';
import { MessageBaseHandler } from '@waha/apps/chatwoot/consumers/waha/base';
import { TKey } from '@waha/apps/chatwoot/locale';
import { WAHASessionAPI } from '@waha/apps/chatwoot/session/WAHASelf';
import { SessionManager } from '@waha/core/abc/manager.abc';
import { parseMessageIdSerialized } from '@waha/core/utils/ids';
@@ -18,6 +18,7 @@ import { WAMessageReaction, WAReaction } from '@waha/structures/responses.dto';
import { WAHAWebhookMessageReaction } from '@waha/structures/webhooks.dto';
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
@Processor(QueueName.WAHA_MESSAGE_REACTION, { concurrency: JOB_CONCURRENCY })
export class WAHAMessageReactionConsumer extends ChatWootWAHABaseConsumer {
@@ -41,6 +42,7 @@ export class WAHAMessageReactionConsumer extends ChatWootWAHABaseConsumer {
const event: WAHAWebhookMessageReaction = job.data.event as any;
const session = new WAHASessionAPI(event.session, container.WAHASelf());
const handler = new MessageReactionHandler(
job,
container.MessageMappingService(),
container.ContactConversationService(),
container.Logger(),
@@ -54,15 +56,24 @@ export class WAHAMessageReactionConsumer extends ChatWootWAHABaseConsumer {
}
export class MessageReactionHandler extends MessageBaseHandler<WAMessageReaction> {
getContent(payload: WAMessageReaction) {
protected async getMessage(
payload: WAMessageReaction,
): Promise<ChatWootMessagePartial> {
const reaction = payload.reaction as WAReaction;
const emoji = reaction.text;
if (!emoji) {
return this.l.key(TKey.WHATSAPP_REACTION_REMOVED).render();
let content: string;
if (emoji) {
content = this.l.key(TKey.WHATSAPP_REACTION_ADDED).render({
emoji: emoji,
});
} else {
content = this.l.key(TKey.WHATSAPP_REACTION_REMOVED).render();
}
return this.l.key(TKey.WHATSAPP_REACTION_ADDED).render({
emoji: emoji,
});
return {
content: content,
attachments: [],
private: undefined,
};
}
getReplyToWhatsAppID(payload: WAMessageReaction) {
@@ -10,7 +10,7 @@ import {
ChatWootWAHABaseConsumer,
IMessageInfo,
} from '@waha/apps/chatwoot/consumers/waha/base';
import { Locale, TKey } from '@waha/apps/chatwoot/locale';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import {
ChatwootMessage,
MessageMappingService,
@@ -22,6 +22,7 @@ import { WAHAEvents } from '@waha/structures/enums.dto';
import { WAHAWebhookMessageRevoked } from '@waha/structures/webhooks.dto';
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
@Processor(QueueName.WAHA_MESSAGE_REVOKED, { concurrency: JOB_CONCURRENCY })
export class WAHAMessageRevokedConsumer extends ChatWootWAHABaseConsumer {
@@ -10,7 +10,7 @@ import {
ChatWootWAHABaseConsumer,
IMessageInfo,
} from '@waha/apps/chatwoot/consumers/waha/base';
import { Locale, TKey } from '@waha/apps/chatwoot/locale';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { WAHASelf } from '@waha/apps/chatwoot/session/WAHASelf';
import { SessionStatusEmoji } from '@waha/apps/chatwoot/SessionStatusEmoji';
import { SessionManager } from '@waha/core/abc/manager.abc';
@@ -19,6 +19,7 @@ import { WAHAEvents, WAHASessionStatus } from '@waha/structures/enums.dto';
import { WAHAWebhookSessionStatus } from '@waha/structures/webhooks.dto';
import { Job } from 'bullmq';
import { PinoLogger } from 'nestjs-pino';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
@Processor(QueueName.WAHA_SESSION_STATUS, { concurrency: JOB_CONCURRENCY })
export class WAHASessionStatusConsumer extends ChatWootWAHABaseConsumer {
@@ -1,7 +1,8 @@
import { public_contact_create_update_payload } from '@figuro/chatwoot-sdk';
import { ContactInfo } from '@waha/apps/chatwoot/client/ContactConversationService';
import { AttributeKey, INBOX_CONTACT_CHAT_ID } from '@waha/apps/chatwoot/const';
import { Locale, TKey } from '@waha/apps/chatwoot/locale';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
/**
* Inbox Notifications Contact
@@ -3,14 +3,21 @@ import {
isJidNewsletter,
isJidStatusBroadcast,
} from '@adiwajshing/baileys';
import { isLidUser } from '@adiwajshing/baileys/lib/WABinary/jid-utils';
import { public_contact_create_update_payload as Contact } from '@figuro/chatwoot-sdk';
import {
isJidBroadcast,
isLidUser,
} from '@adiwajshing/baileys/lib/WABinary/jid-utils';
import {
public_contact_create_update_payload,
public_contact_create_update_payload as Contact,
} from '@figuro/chatwoot-sdk';
import { ContactInfo } from '@waha/apps/chatwoot/client/ContactConversationService';
import { AttributeKey } from '@waha/apps/chatwoot/const';
import { Locale, TKey } from '@waha/apps/chatwoot/locale';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { WAHASessionAPI } from '@waha/apps/chatwoot/session/WAHASelf';
import { Channel } from '@waha/structures/channels.dto';
import { CacheAsync } from '@waha/utils/Cache';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
/**
* Base WhatsApp contact info class
@@ -220,6 +227,31 @@ class StatusContactInfo extends ChatContactInfo {
}
}
class BroadcastContactInfo extends ChatContactInfo {
async Attributes() {
return {
[AttributeKey.WA_CHAT_ID]: this.chatId,
};
}
@CacheAsync()
async AvatarUrl(): Promise<string | null> {
return null;
}
async PublicContactCreate(): Promise<Contact> {
const name = this.locale
.key(TKey.WHATSAPP_CONTACT_BROADCAST_SUFFIX)
.render();
return {
identifier: this.chatId,
name: `${name} (${this.chatId})`,
custom_attributes: await this.Attributes(),
};
}
}
/**
* Factory function to get the appropriate ContactInfo implementation
* based on the chat ID type
@@ -235,6 +267,8 @@ export function WhatsAppContactInfo(
return new ChannelContactInfo(session, chatId, locale);
} else if (isJidStatusBroadcast(chatId)) {
return new StatusContactInfo(session, chatId, locale);
} else if (isJidBroadcast(chatId)) {
return new BroadcastContactInfo(session, chatId, locale);
} else if (isLidUser(chatId)) {
return new LidContactInfo(session, chatId, locale);
}
+22 -3
View File
@@ -1,4 +1,5 @@
import ChatwootClient from '@figuro/chatwoot-sdk';
import * as lodash from 'lodash';
import { AxiosLogging } from '@waha/apps/app_sdk/AxiosLogging';
import { ILogger } from '@waha/apps/app_sdk/ILogger';
import { ContactAPI } from '@waha/apps/chatwoot/client/ContactAPI';
@@ -6,9 +7,14 @@ import { ContactConversationService } from '@waha/apps/chatwoot/client/ContactCo
import { ConversationAPI } from '@waha/apps/chatwoot/client/ConversationAPI';
import { CustomAttributesAPI } from '@waha/apps/chatwoot/client/CustomAttributesAPI';
import { ChatWootInboxAPI } from '@waha/apps/chatwoot/client/interfaces';
import { ChatWootAppConfig } from '@waha/apps/chatwoot/dto/config.dto';
import {
ChatWootAppConfig,
ChatWootConfig,
DEFAULT_LOCALE,
LinkPreview,
} from '@waha/apps/chatwoot/dto/config.dto';
import { ChatWootErrorReporter } from '@waha/apps/chatwoot/error/ChatWootErrorReporter';
import { Locale } from '@waha/apps/chatwoot/locale';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { WAHASelf } from '@waha/apps/chatwoot/session/WAHASelf';
import {
ChatwootMessageRepository,
@@ -18,6 +24,7 @@ import {
} from '@waha/apps/chatwoot/storage';
import { Job } from 'bullmq';
import { Knex } from 'knex';
import { i18n } from '@waha/apps/chatwoot/i18n';
/**
* Dependency Injection Container for ChatWoot
@@ -63,7 +70,8 @@ export class DIContainer {
public Locale(): Locale {
if (!this.locale) {
this.locale = new Locale(this.config.locale);
this.locale = i18n.locale(this.config.locale || DEFAULT_LOCALE);
this.locale = this.locale.override(this.ChatWootConfig().templates);
}
return this.locale;
}
@@ -198,4 +206,15 @@ export class DIContainer {
public CustomAttributesAPI() {
return new CustomAttributesAPI(this.config, this.AccountAPI());
}
public ChatWootConfig(): ChatWootConfig {
const defaults: ChatWootConfig = {
templates: {},
linkPreview: LinkPreview.OFF,
commands: {
server: true,
},
};
return lodash.defaults({}, this.config, defaults);
}
}
+41 -3
View File
@@ -1,6 +1,33 @@
import { ChatWootAPIConfig } from '@waha/apps/chatwoot/client/interfaces';
import { DEFAULT_LOCALE, LOCALES } from '@waha/apps/chatwoot/locale';
import { IsIn, IsNumber, IsString } from 'class-validator';
import {
IsBoolean,
IsEnum,
IsNumber,
IsOptional,
IsString,
ValidateNested,
} from 'class-validator';
import { Type } from 'class-transformer';
import { IsDynamicObject } from '@waha/nestjs/validation/IsDynamicObject';
export const DEFAULT_LOCALE = 'en-US';
export class ChatWootCommandsConfig {
@IsBoolean()
server: boolean = true;
}
export enum LinkPreview {
OFF = 'OFF',
LQ = 'LG',
HQ = 'HG',
}
export interface ChatWootConfig {
templates: Record<string, string>;
linkPreview: LinkPreview;
commands: ChatWootCommandsConfig;
}
export class ChatWootAppConfig implements ChatWootAPIConfig {
@IsString()
@@ -18,7 +45,18 @@ export class ChatWootAppConfig implements ChatWootAPIConfig {
@IsString()
inboxIdentifier: string;
@IsEnum(LinkPreview)
@IsOptional()
linkPreview?: LinkPreview = LinkPreview.OFF;
@IsString()
@IsIn(LOCALES)
locale: string = DEFAULT_LOCALE;
@IsOptional()
@IsDynamicObject()
templates?: Record<string, string>;
@ValidateNested()
@Type(() => ChatWootCommandsConfig)
commands?: ChatWootCommandsConfig;
}
@@ -1,11 +1,15 @@
import type { conversation_message_create } from '@figuro/chatwoot-sdk/dist/models/conversation_message_create';
import { ILogger } from '@waha/apps/app_sdk/ILogger';
import { NextAttemptDelayInWholeSeconds } from '@waha/apps/app_sdk/JobUtils';
import {
JobLink,
NextAttemptDelayInWholeSeconds,
} from '@waha/apps/app_sdk/JobUtils';
import { Conversation } from '@waha/apps/chatwoot/client/Conversation';
import { MessageType } from '@waha/apps/chatwoot/client/types';
import { ErrorRenderer } from '@waha/apps/chatwoot/error/ErrorRenderer';
import { Locale, TKey } from '@waha/apps/chatwoot/locale';
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { Job } from 'bullmq';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
export class ChatWootErrorReporter {
private errorRenderer: ErrorRenderer = new ErrorRenderer();
@@ -25,10 +29,7 @@ export class ChatWootErrorReporter {
) {
const errorText = this.errorRenderer.renderError(error);
this.logger.error(errorText);
const errorUrl = `http://localhost:3000/jobs/queue/${encodeURIComponent(
this.job.queueName,
)}/${this.job.id}`;
const template = this.l.key(TKey.JOB_ERROR_REPORT);
const template = this.l.key(TKey.JOB_REPORT_ERROR);
const nextDelay = NextAttemptDelayInWholeSeconds(this.job);
const attempts = {
current: this.job.attemptsMade + 1,
@@ -38,10 +39,7 @@ export class ChatWootErrorReporter {
const content = template.render({
header: header,
error: nextDelay != null ? null : errorText,
details: {
text: `${this.job.queueName} => ${this.job.id}`,
url: errorUrl,
},
details: JobLink(this.job),
attempts: attempts,
});
const request: conversation_message_create = {
@@ -74,7 +72,7 @@ export class ChatWootErrorReporter {
const jobUrl = `http://localhost:3000/jobs/queue/${encodeURIComponent(
this.job.queueName,
)}/${this.job.id}`;
const template = this.l.key(TKey.JOB_SUCCEEDED_REPORT);
const template = this.l.key(TKey.JOB_REPORT_SUCCEEDED);
const attempts = {
current: this.job.attemptsMade + 1,
max: this.job.opts?.attempts || 1,
+1 -1
View File
@@ -61,7 +61,7 @@ export class ErrorRenderer {
* @returns Formatted error text
*/
private renderApiError(error: ApiError): string {
let errorText = `API Error: ${error.message}`;
let errorText = `ChatWoot API Error: ${error.message}`;
errorText += `\nStatus: ${error.status}`;
// Add body if it exists
+43
View File
@@ -0,0 +1,43 @@
import { Locale } from '@waha/apps/chatwoot/i18n/locale';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
export class I18N {
private readonly locales: Record<string, Locale>;
constructor() {
this.locales = {};
}
available(): Array<{ name: string; locale: string }> {
const result = [];
for (const [key, template] of Object.entries(this.locales)) {
result.push({ locale: key, name: template.key(TKey.LOCALE_NAME).r() });
}
return result;
}
load(locales: Record<string, Record<string, string>>) {
for (const [locale, strings] of Object.entries(locales)) {
this.add(locale, strings);
}
}
add(locale: string, strings: Record<string, string>) {
if (!this.locales[locale]) {
this.locales[locale] = new Locale(strings);
} else {
this.locales[locale] = this.locales[locale].override(strings);
}
}
locale(locale: string) {
if (!this.has(locale)) {
throw new Error(`Locale ${locale} not found`);
}
return this.locales[locale];
}
has(locale: string) {
return !!this.locales[locale];
}
}
+19
View File
@@ -0,0 +1,19 @@
import * as path from 'path';
import { YamlLocaleLoader } from '@waha/apps/chatwoot/i18n/loader';
import { I18N } from '@waha/apps/chatwoot/i18n/i18n';
const i18n = new I18N();
const localesDir = path.join(__dirname, 'locales');
// Load both .yml and .yaml files from locales directory (non-recursive)
i18n.load(new YamlLocaleLoader(localesDir, 'yml').load());
i18n.load(new YamlLocaleLoader(localesDir, 'yaml').load());
// Resolve env var (supports '.' and '~'); fallback to default
const additional = process.env.WAHA_APPS_CHATWOOT_LANGUAGES_FOLDER?.trim();
if (additional) {
const resolved = path.resolve(additional);
i18n.load(new YamlLocaleLoader(resolved, 'yml').load());
i18n.load(new YamlLocaleLoader(resolved, 'yaml').load());
}
export { i18n };
+33
View File
@@ -0,0 +1,33 @@
import * as fs from 'fs-extra';
import * as path from 'path';
import * as yaml from 'yaml';
export class YamlLocaleLoader {
constructor(
private readonly dir: string,
private readonly ext: string,
) {}
load(): Record<string, Record<string, string>> {
const result: Record<string, Record<string, string>> = {};
const files = fs.readdirSync(this.dir, { withFileTypes: true });
for (const entry of files) {
if (!entry.isFile()) continue;
const filename = entry.name;
if (!filename.endsWith(`.${this.ext}`)) continue;
const locale = path.basename(filename, `.${this.ext}`);
const filePath = path.join(this.dir, filename);
const content = fs.readFileSync(filePath, 'utf-8');
const parsed = yaml.parse(content) as Record<string, string> | undefined;
if (parsed && typeof parsed === 'object') {
result[locale] = parsed as Record<string, string>;
}
}
return result;
}
}
+30
View File
@@ -0,0 +1,30 @@
import * as Mustache from 'mustache';
import { TemplatePayloads, TKey } from '@waha/apps/chatwoot/i18n/templates';
export class Locale {
constructor(private readonly strings: Record<string, string>) {}
key<K extends TKey>(key: K): Template<K> {
return new Template(this.strings[key] || key);
}
/**
* Overrides the existing strings with the provided strings for the locale.
* Merges the new strings with the current strings.
*/
override(strings: Record<string, string>): Locale {
return new Locale({ ...this.strings, ...strings });
}
}
export class Template<K extends TKey> {
constructor(private readonly template: string) {}
render(data: TemplatePayloads[K]): string {
return Mustache.render(this.template, data);
}
r(data: TemplatePayloads[K]): string {
return this.render(data);
}
}
@@ -1,7 +1,9 @@
# Arabic translations for ChatWoot integration
APP_INBOX_CONTACT_NAME: 'تكامل واتساب (WAHA)'
locale.name: '🇦🇪 العربية (Arabic)'
app.inbox.contact.name: 'تكامل واتساب (WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
JOB_ERROR_REPORT: |
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 إعادة المحاولة في {{attempts.nextDelay}} ثانية...
@@ -18,51 +20,52 @@ JOB_ERROR_REPORT: |
```
{{/error}}
JOB_SUCCEEDED_REPORT: |
job.report.succeeded: |
✅ **نجاح**
المحاولة: {{{attempts.current}}} / {{{attempts.max}}}
التفاصيل: [{{{details.text}}}]({{{details.url}}})
WHATSAPP_ERROR_SENDING_MESSAGE: تعذر إرسال رسالة على واتساب.
WHATSAPP_ERROR_RECEIVING_MESSAGE: تعذر استلام رسالة من واتساب
WHATSAPP_ERROR_REMOVING_MESSAGE: تعذر إزالة رسالة من واتساب
APP_SCHEDULED_JOB_ERROR: فشل تنفيذ المهمة المجدولة
whatsapp.message.sending.error: تعذر إرسال رسالة على واتساب.
whatsapp.message.receiving.error: تعذر استلام رسالة من واتساب
whatsapp.message.removing.error: تعذر إزالة رسالة من واتساب
job.scheduled.error.header: فشل تنفيذ المهمة المجدولة
MESSAGE_FROM_WHATSAPP: |
message.from.whatsapp: |
{{{text}}}
📱 *أرسلت من واتساب*
MESSAGE_FROM_API: |
message.from.api: |
{{{text}}}
📱 *أرسلت عبر API*
MESSAGE_REMOVED_IN_WHATSAPP: |
message.removed.in.whatsapp: |
❌ *تم حذف هذه الرسالة في واتساب*
MESSAGE_EDITED_IN_WHATSAPP: |
message.edited.in.whatsapp: |
{{{text}}}
✏️ *تم تعديلها في واتساب*
WHATSAPP_GROUP_MESSAGE: |
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
WHATSAPP_REACTION_ADDED: |
whatsapp.reaction.added: |
*تفاعل* {{{emoji}}}
WHATSAPP_REACTION_REMOVED: |
whatsapp.reaction.removed: |
*تمت إزالة التفاعل*
WHATSAPP_CONTACT_GROUP_SUFFIX: 'مجموعة'
WHATSAPP_CONTACT_CHANNEL_SUFFIX: 'قناة'
whatsapp.contact.group.suffix: 'مجموعة'
whatsapp.contact.channel.suffix: 'قناة'
whatsapp.contact.broadcast.suffix: 'قائمة البث'
# Aka Stories
WHATSAPP_CONTACT_STATUS_NAME: 'الحالة'
whatsapp.contact.status.name: 'الحالة'
APP_CONNECTED_MESSAGE: |
app.connected.message: |
🔗 **متصل: جلسة واتساب إلى صندوق وارد ChatWoot**
✅ تم توصيل جلسة واتساب **{{{name}}}** بنجاح بصندوق وارد ChatWoot هذا.
@@ -80,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
لنبدأ! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **غير متصل: جلسة واتساب من صندوق وارد ChatWoot**
❌ تم قطع اتصال جلسة واتساب **{{{name}}}** من صندوق وارد ChatWoot هذا.
@@ -89,15 +92,15 @@ APP_DISCONNECTED_MESSAGE: |
لإعادة الاتصال، يرجى الاتصال بالمسؤول الخاص بك.
APP_UPDATED_MESSAGE: |
app.updated.message: |
🔄 **تم التحديث: إعدادات جلسة واتساب**
✅ تم تحديث إعدادات جلسة واتساب **{{{name}}}**.
APP_COMMANDS_LIST: |
app.commands.list: |
يمكنك إدارة **تكامل واتساب** عن طريق إرسال أحد الأوامر التالية:
🖥️ الجلسات:
🖥️ **الجلسة**:
1. **status** - الحصول على حالة الجلسة الحالية.
2. **restart** - إعادة تشغيل الجلسة - سيحاول هذا إعادة توصيل حساب واتساب الخاص بك.
3. **start** - بدء الجلسة.
@@ -107,42 +110,47 @@ APP_COMMANDS_LIST: |
7. **screenshot** - الحصول على لقطة شاشة للجلسة (متاح فقط في محرك **WEBJS**).
8. **help** - الأوامر المتاحة.
{{#commands.server}}
⚙️ الخادم:
- **server status** - الحصول على معلومات إصدار الخادم وحالته.
- **server reboot** - إعادة تشغيل الخادم بسلاسة.
- **server reboot force** - إعادة تشغيل الخادم بالقوة.
{{/commands.server}}
أرسل **أمرًا** (غير حساس لحالة الأحرف) أو **رقم أمر** إلى هذه المحادثة.
APP_HELP_REMINDER: |
app.commands.server.disabled: ⛔ **تم تعطيل أوامر الخادم**.
يرجى التواصل مع مسؤول النظام لديك.
أرسل **help** للحصول على قائمة الأوامر المتاحة.
APP_SESSION_STATUS_CHANGE: |
app.help.reminder: |
أرسل **help** للحصول على قائمة الأوامر المتاحة.
app.session.status.change: |
{{{emoji}}} تم تغيير حالة جلسة "{{{session}}}" إلى {{{status}}}
APP_SESSION_CURRENT_STATUS: |
app.session.current.status: |
{{{emoji}}} حالة جلسة **{{{session}}}** هي **{{{status}}}**
**حساب واتساب:**
📱 **{{{name}}} ({{{id}}})**
APP_DASHBOARD_LINK: |
قم بزيارة [لوحة تحكم WAHA]({{{url}}}) لمزيد من التفاصيل.
APP_SESSION_STATUS_WORKING: |
app.session.status.working: |
✅ صندوق وارد ChatWoot هذا مرتبط بـ **حساب واتساب** وجاهز لإرسال واستقبال الرسائل!
📱 **{{{name}}} ({{{id}}})**
APP_SESSION_STATUS_ERROR: |
app.session.status.error: |
⚠️ **صندوق وارد ChatWoot هذا مرتبط بحساب واتساب ولكنه غير جاهز لإرسال واستقبال الرسائل!**
يرجى التحقق من حالة الجلسة ومحاولة إعادة تشغيلها أو تسجيل الخروج ومسح رمز QR مرة أخرى.
APP_SESSION_SCAN_QR_CODE: |
app.session.scan.qr.code: |
⚠️ **امسح رمز QR على هاتفك لربط حساب واتساب بصندوق وارد ChatWoot هذا!**
APP_SERVER_VERSION_AND_STATUS: |
app.server.version.and.status: |
**الإصدار:**
```
{{{version}}}
@@ -153,16 +161,16 @@ APP_SERVER_VERSION_AND_STATUS: |
{{{status}}}
```
APP_SERVER_REBOOT: |
app.server.reboot: |
يتم إعادة تشغيل الخادم بسلاسة...
APP_SERVER_REBOOT_FORCE: |
app.server.reboot.force: |
يتم إعادة تشغيل الخادم بالقوة...
APP_LOGOUT_SUCCESS: |
app.logout.success: |
✅ تم إلغاء تفويض الحساب بنجاح!
APP_NEW_VERSION_AVAILABLE: |
waha.new.version.available: |
🔄 **إصدار جديد متاح!**
الإصدار الحالي: **{{{currentVersion}}}**
@@ -170,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 تحقق مما هو جديد في [**🆕 سجل التغييرات**]({{{changelogUrl}}})
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **ادعم WAHA!**
أنت تستخدم إصدار **WAHA Core** المجاني. إذا وجدت هذا المشروع مفيدًا، فيرجى التفكير في دعم تطويره.
@@ -178,3 +186,51 @@ APP_CORE_VERSION_USED: |
دعمك هو الطريقة الوحيدة التي يمكننا من خلالها الاستمرار في العمل على التحديثات والإصلاحات والميزات الجديدة.
[**🎁 ادعمنا**]({{{supportUrl}}})
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **جهات الاتصال المشتركة**
{{#contacts}}
🪪 الاسم: **{{fullName}}**
{{#phoneNumbers}}
- 📞 الهاتف: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **الموقع المشترك**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **مشاركة الموقع المباشر**
افتح **WhatsApp** لعرض التحديثات اللحظية.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **هذا النوع من الرسائل غير مدعوم في ChatWoot.**
📱 يرجى فتح **WhatsApp** لعرضه.
التفاصيل: [{{{details.text}}}]({{{details.url}}})
@@ -1,7 +1,9 @@
# Bengali translations for ChatWoot integration
APP_INBOX_CONTACT_NAME: 'হোয়াটসঅ্যাপ ইন্টিগ্রেশন (WAHA)'
locale.name: '🇧🇩 বাংলা (Bengali)'
app.inbox.contact.name: 'হোয়াটসঅ্যাপ ইন্টিগ্রেশন (WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
JOB_ERROR_REPORT: |
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 {{attempts.nextDelay}} সেকেন্ডের মধ্যে পুনরায় চেষ্টা করা হচ্ছে...
@@ -18,51 +20,52 @@ JOB_ERROR_REPORT: |
```
{{/error}}
JOB_SUCCEEDED_REPORT: |
job.report.succeeded: |
✅ **সফল হয়েছে**
প্রচেষ্টা: {{{attempts.current}}} / {{{attempts.max}}}
বিবরণ: [{{{details.text}}}]({{{details.url}}})
WHATSAPP_ERROR_SENDING_MESSAGE: হোয়াটসঅ্যাপে বার্তা পাঠাতে পারেনি।
WHATSAPP_ERROR_RECEIVING_MESSAGE: হোয়াটসঅ্যাপ থেকে বার্তা পেতে পারেনি
WHATSAPP_ERROR_REMOVING_MESSAGE: হোয়াটসঅ্যাপ থেকে বার্তা সরাতে পারেনি
APP_SCHEDULED_JOB_ERROR: নির্ধারিত কাজ সম্পাদন করতে ব্যর্থ হয়েছে
whatsapp.message.sending.error: হোয়াটসঅ্যাপে বার্তা পাঠাতে পারেনি।
whatsapp.message.receiving.error: হোয়াটসঅ্যাপ থেকে বার্তা পেতে পারেনি
whatsapp.message.removing.error: হোয়াটসঅ্যাপ থেকে বার্তা সরাতে পারেনি
job.scheduled.error.header: নির্ধারিত কাজ সম্পাদন করতে ব্যর্থ হয়েছে
MESSAGE_FROM_WHATSAPP: |
message.from.whatsapp: |
{{{text}}}
📱 *হোয়াটসঅ্যাপ থেকে পাঠানো হয়েছে*
MESSAGE_FROM_API: |
message.from.api: |
{{{text}}}
📱 *API এর মাধ্যমে পাঠানো হয়েছে*
MESSAGE_REMOVED_IN_WHATSAPP: |
message.removed.in.whatsapp: |
❌ *এই বার্তাটি হোয়াটসঅ্যাপে মুছে ফেলা হয়েছে*
MESSAGE_EDITED_IN_WHATSAPP: |
message.edited.in.whatsapp: |
{{{text}}}
✏️ *হোয়াটসঅ্যাপে সম্পাদিত*
WHATSAPP_GROUP_MESSAGE: |
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
WHATSAPP_REACTION_ADDED: |
whatsapp.reaction.added: |
*প্রতিক্রিয়া দিয়েছেন* {{{emoji}}}
WHATSAPP_REACTION_REMOVED: |
whatsapp.reaction.removed: |
*প্রতিক্রিয়া সরানো হয়েছে*
WHATSAPP_CONTACT_GROUP_SUFFIX: 'গ্রুপ'
WHATSAPP_CONTACT_CHANNEL_SUFFIX: 'চ্যানেল'
whatsapp.contact.group.suffix: 'গ্রুপ'
whatsapp.contact.channel.suffix: 'চ্যানেল'
whatsapp.contact.broadcast.suffix: 'প্রচার তালিকা'
# Aka Stories
WHATSAPP_CONTACT_STATUS_NAME: 'স্ট্যাটাস'
whatsapp.contact.status.name: 'স্ট্যাটাস'
APP_CONNECTED_MESSAGE: |
app.connected.message: |
🔗 **সংযুক্ত: হোয়াটসঅ্যাপ সেশন থেকে ChatWoot ইনবক্স**
✅ হোয়াটসঅ্যাপ সেশন **{{{name}}}** সফলভাবে এই ChatWoot ইনবক্সের সাথে সংযুক্ত করা হয়েছে।
@@ -80,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
চলুন শুরু করা যাক! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **সংযোগ বিচ্ছিন্ন: হোয়াটসঅ্যাপ সেশন থেকে ChatWoot ইনবক্স**
❌ হোয়াটসঅ্যাপ সেশন **{{{name}}}** এই ChatWoot ইনবক্স থেকে সংযোগ বিচ্ছিন্ন করা হয়েছে।
@@ -89,15 +92,15 @@ APP_DISCONNECTED_MESSAGE: |
পুনরায় সংযোগ করতে, অনুগ্রহ করে আপনার প্রশাসকের সাথে যোগাযোগ করুন।
APP_UPDATED_MESSAGE: |
app.updated.message: |
🔄 **আপডেট করা হয়েছে: হোয়াটসঅ্যাপ সেশন সেটিংস**
✅ হোয়াটসঅ্যাপ সেশন **{{{name}}}** এর সেটিংস আপডেট করা হয়েছে।
APP_COMMANDS_LIST: |
app.commands.list: |
আপনি নিম্নলিখিত কমান্ডগুলির একটি পাঠিয়ে **হোয়াটসঅ্যাপ ইন্টিগ্রেশন** পরিচালনা করতে পারেন:
🖥️ সেশন:
🖥️ **সেশন**:
1. **status** - বর্তমান সেশনের স্ট্যাটাস পান।
2. **restart** - সেশন পুনরায় চালু করুন - এটি আপনার হোয়াটসঅ্যাপ অ্যাকাউন্ট পুনরায় সংযোগ করার চেষ্টা করবে।
3. **start** - সেশন শুরু করুন।
@@ -107,42 +110,47 @@ APP_COMMANDS_LIST: |
7. **screenshot** - সেশনের স্ক্রিনশট পান (শুধুমাত্র **WEBJS** ইঞ্জিনে উপলব্ধ)।
8. **help** - উপলব্ধ কমান্ডগুলি।
{{#commands.server}}
⚙️ সার্ভার:
- **server status** - সার্ভারের সংস্করণ এবং স্ট্যাটাস তথ্য পান।
- **server reboot** - সার্ভার সুন্দরভাবে পুনরায় চালু করুন।
- **server reboot force** - সার্ভার জোর করে পুনরায় চালু করুন।
{{/commands.server}}
এই কথোপকথনে একটি **কমান্ড** (কেস সংবেদনশীল নয়) বা **কমান্ড নম্বর** পাঠান।
APP_HELP_REMINDER: |
app.commands.server.disabled: ⛔ **সার্ভার কমান্ড নিষ্ক্রিয় করা হয়েছে**।
দয়া করে আপনার প্রশাসকের সাথে যোগাযোগ করুন।
উপলব্ধ কমান্ডের তালিকা পেতে **help** পাঠান।
app.help.reminder: |
উপলব্ধ কমান্ডগুলির তালিকা পেতে **help** পাঠান।
APP_SESSION_STATUS_CHANGE: |
app.session.status.change: |
{{{emoji}}} "{{{session}}}" সেশন {{{status}}} স্ট্যাটাসে পরিবর্তিত হয়েছে
APP_SESSION_CURRENT_STATUS: |
app.session.current.status: |
{{{emoji}}} **{{{session}}}** সেশনের স্ট্যাটাস **{{{status}}}**
**হোয়াটসঅ্যাপ অ্যাকাউন্ট:**
📱 **{{{name}}} ({{{id}}})**
APP_DASHBOARD_LINK: |
আরও বিবরণের জন্য [WAHA ড্যাশবোর্ড]({{{url}}}) দেখুন।
APP_SESSION_STATUS_WORKING: |
app.session.status.working: |
✅ এই ChatWoot ইনবক্স **হোয়াটসঅ্যাপ অ্যাকাউন্টের** সাথে সংযুক্ত এবং বার্তা পাঠাতে ও গ্রহণ করতে প্রস্তুত!
📱 **{{{name}}} ({{{id}}})**
APP_SESSION_STATUS_ERROR: |
app.session.status.error: |
⚠️ **এই ChatWoot ইনবক্স হোয়াটসঅ্যাপ অ্যাকাউন্টের সাথে সংযুক্ত কিন্তু বার্তা পাঠাতে ও গ্রহণ করতে প্রস্তুত নয়!**
অনুগ্রহ করে সেশনের স্ট্যাটাস পরীক্ষা করুন এবং এটি পুনরায় চালু করার চেষ্টা করুন অথবা লগআউট করুন এবং QR কোড আবার স্ক্যান করুন।
APP_SESSION_SCAN_QR_CODE: |
app.session.scan.qr.code: |
⚠️ **এই ChatWoot ইনবক্সের সাথে হোয়াটসঅ্যাপ অ্যাকাউন্ট লিঙ্ক করতে আপনার ফোনে QR কোড স্ক্যান করুন!**
APP_SERVER_VERSION_AND_STATUS: |
app.server.version.and.status: |
**সংস্করণ:**
```
{{{version}}}
@@ -153,16 +161,16 @@ APP_SERVER_VERSION_AND_STATUS: |
{{{status}}}
```
APP_SERVER_REBOOT: |
app.server.reboot: |
সার্ভার সুন্দরভাবে পুনরায় চালু হচ্ছে...
APP_SERVER_REBOOT_FORCE: |
app.server.reboot.force: |
সার্ভার জোর করে পুনরায় চালু হচ্ছে...
APP_LOGOUT_SUCCESS: |
app.logout.success: |
✅ অ্যাকাউন্ট সফলভাবে অননুমোদিত করা হয়েছে!
APP_NEW_VERSION_AVAILABLE: |
waha.new.version.available: |
🔄 **নতুন সংস্করণ উপলব্ধ!**
বর্তমান সংস্করণ: **{{{currentVersion}}}**
@@ -170,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 [**🆕 পরিবর্তন লগ**]({{{changelogUrl}}}) এ নতুন কি আছে তা দেখুন
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **WAHA সমর্থন করুন!**
আপনি বিনামূল্যের **WAHA Core** সংস্করণ ব্যবহার করছেন। যদি আপনি এই প্রকল্পটি উপযোগী মনে করেন, তবে এর বিকাশকে সমর্থন করার কথা বিবেচনা করুন।
@@ -178,3 +186,51 @@ APP_CORE_VERSION_USED: |
আপনার সমর্থন হল একমাত্র উপায় যা দিয়ে আমরা আপডেট, ফিক্স এবং নতুন বৈশিষ্ট্যগুলিতে কাজ চালিয়ে যেতে পারি।
[**🎁 আমাদের সমর্থন করুন**]({{{supportUrl}}})
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **শেয়ার করা যোগাযোগ**
{{#contacts}}
🪪 নাম: **{{fullName}}**
{{#phoneNumbers}}
- 📞 ফোন: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **শেয়ার করা লোকেশন**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **লাইভ লোকেশন শেয়ার করা হচ্ছে**
রিয়েল-টাইম আপডেট দেখতে **WhatsApp** খুলুন।
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **এই ধরনের বার্তা ChatWoot-এ সমর্থিত নয়।**
📱 এটি দেখতে অনুগ্রহ করে **WhatsApp** খুলুন।
বিস্তারিত: [{{{details.text}}}]({{{details.url}}})
@@ -1,7 +1,9 @@
# German translations for ChatWoot integration
APP_INBOX_CONTACT_NAME: 'WhatsApp Integration (WAHA)'
locale.name: '🇩🇪 Deutsch (German)'
app.inbox.contact.name: 'WhatsApp Integration (WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
JOB_ERROR_REPORT: |
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 Wiederholung in {{attempts.nextDelay}} Sekunden...
@@ -18,54 +20,55 @@ JOB_ERROR_REPORT: |
```
{{/error}}
JOB_SUCCEEDED_REPORT: |
job.report.succeeded: |
✅ **Erfolgreich**
Versuch: {{{attempts.current}}} / {{{attempts.max}}}
Details: [{{{details.text}}}]({{{details.url}}})
WHATSAPP_ERROR_SENDING_MESSAGE:
whatsapp.message.sending.error:
Nachricht konnte nicht über WhatsApp gesendet werden.
WHATSAPP_ERROR_RECEIVING_MESSAGE:
whatsapp.message.receiving.error:
Nachricht konnte nicht von WhatsApp empfangen werden
WHATSAPP_ERROR_REMOVING_MESSAGE:
whatsapp.message.removing.error:
Nachricht konnte nicht von WhatsApp entfernt werden
APP_SCHEDULED_JOB_ERROR: Geplante Aufgabe konnte nicht ausgeführt werden
job.scheduled.error.header: Geplante Aufgabe konnte nicht ausgeführt werden
MESSAGE_FROM_WHATSAPP: |
message.from.whatsapp: |
{{{text}}}
📱 *Von WhatsApp gesendet*
MESSAGE_FROM_API: |
message.from.api: |
{{{text}}}
📱 *Über API gesendet*
MESSAGE_REMOVED_IN_WHATSAPP: |
message.removed.in.whatsapp: |
❌ *Diese Nachricht wurde in WhatsApp gelöscht*
MESSAGE_EDITED_IN_WHATSAPP: |
message.edited.in.whatsapp: |
{{{text}}}
✏️ *In WhatsApp bearbeitet*
WHATSAPP_GROUP_MESSAGE: |
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
WHATSAPP_REACTION_ADDED: |
whatsapp.reaction.added: |
*Reagiert mit* {{{emoji}}}
WHATSAPP_REACTION_REMOVED: |
whatsapp.reaction.removed: |
*Reaktion entfernt*
WHATSAPP_CONTACT_GROUP_SUFFIX: 'Gruppe'
WHATSAPP_CONTACT_CHANNEL_SUFFIX: 'Kanal'
whatsapp.contact.group.suffix: 'Gruppe'
whatsapp.contact.channel.suffix: 'Kanal'
whatsapp.contact.broadcast.suffix: 'Broadcast-Liste'
# Aka Stories
WHATSAPP_CONTACT_STATUS_NAME: 'Status'
whatsapp.contact.status.name: 'Status'
APP_CONNECTED_MESSAGE: |
app.connected.message: |
🔗 **VERBUNDEN: WhatsApp-Sitzung mit ChatWoot Inbox**
✅ WhatsApp-Sitzung **{{{name}}}** wurde erfolgreich mit dieser ChatWoot Inbox verbunden.
@@ -83,7 +86,7 @@ APP_CONNECTED_MESSAGE: |
Lassen Sie uns beginnen! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **GETRENNT: WhatsApp-Sitzung von ChatWoot Inbox**
❌ WhatsApp-Sitzung **{{{name}}}** wurde von dieser ChatWoot Inbox getrennt.
@@ -92,15 +95,15 @@ APP_DISCONNECTED_MESSAGE: |
Um eine erneute Verbindung herzustellen, wenden Sie sich bitte an Ihren Administrator.
APP_UPDATED_MESSAGE: |
app.updated.message: |
🔄 **AKTUALISIERT: WhatsApp-Sitzungseinstellungen**
✅ Einstellungen für WhatsApp-Sitzung **{{{name}}}** wurden aktualisiert.
APP_COMMANDS_LIST: |
app.commands.list: |
Sie können die **WhatsApp Integration** verwalten, indem Sie einen der folgenden Befehle senden:
🖥️ Sitzungen:
🖥️ **Sitzung**:
1. **status** - den aktuellen Sitzungsstatus abrufen.
2. **restart** - die Sitzung neu starten - dies versucht, Ihr WhatsApp-Konto neu zu verbinden.
3. **start** - die Sitzung starten.
@@ -110,42 +113,47 @@ APP_COMMANDS_LIST: |
7. **screenshot** - einen Screenshot der Sitzung erhalten (nur in der **WEBJS**-Engine verfügbar).
8. **help** - verfügbare Befehle.
{{#commands.server}}
⚙️ Server:
- **server status** - Serverversion und Statusinformationen abrufen.
- **server reboot** - Server ordnungsgemäß neu starten.
- **server reboot force** - Server erzwungen neu starten.
{{/commands.server}}
Senden Sie einen **Befehl** (Groß-/Kleinschreibung wird nicht beachtet) oder eine **Befehlsnummer** an diese Konversation.
APP_HELP_REMINDER: |
app.commands.server.disabled: ⛔ **Serverbefehle sind deaktiviert**.
Bitte wenden Sie sich an Ihren Administrator.
Senden Sie **help**, um die Liste der verfügbaren Befehle zu erhalten.
APP_SESSION_STATUS_CHANGE: |
app.help.reminder: |
Senden Sie **help**, um die Liste der verfügbaren Befehle zu erhalten.
app.session.status.change: |
{{{emoji}}} Sitzung "{{{session}}}" hat den Status zu {{{status}}} gewechselt
APP_SESSION_CURRENT_STATUS: |
app.session.current.status: |
{{{emoji}}} **{{{session}}}** Sitzungsstatus ist **{{{status}}}**
**WhatsApp-Konto:**
📱 **{{{name}}} ({{{id}}})**
APP_DASHBOARD_LINK: |
Besuchen Sie das [WAHA Dashboard]({{{url}}}) für weitere Details.
APP_SESSION_STATUS_WORKING: |
app.session.status.working: |
✅ Diese ChatWoot Inbox ist mit dem **WhatsApp-Konto** verknüpft und bereit zum Senden und Empfangen von Nachrichten!
📱 **{{{name}}} ({{{id}}})**
APP_SESSION_STATUS_ERROR: |
app.session.status.error: |
⚠️ **Diese ChatWoot Inbox ist mit dem WhatsApp-Konto verknüpft, aber NICHT bereit zum Senden und Empfangen von Nachrichten!**
Bitte überprüfen Sie den Sitzungsstatus und versuchen Sie, sie neu zu starten oder melden Sie sich ab und scannen Sie den QR-Code erneut.
APP_SESSION_SCAN_QR_CODE: |
app.session.scan.qr.code: |
⚠️ **Scannen Sie den QR-Code auf Ihrem Telefon, um das WhatsApp-Konto mit dieser ChatWoot Inbox zu verknüpfen!**
APP_SERVER_VERSION_AND_STATUS: |
app.server.version.and.status: |
**Version:**
```
{{{version}}}
@@ -156,16 +164,16 @@ APP_SERVER_VERSION_AND_STATUS: |
{{{status}}}
```
APP_SERVER_REBOOT: |
app.server.reboot: |
Server wird ordnungsgemäß neu gestartet...
APP_SERVER_REBOOT_FORCE: |
app.server.reboot.force: |
Server wird erzwungen neu gestartet...
APP_LOGOUT_SUCCESS: |
app.logout.success: |
✅ Konto erfolgreich abgemeldet!
APP_NEW_VERSION_AVAILABLE: |
waha.new.version.available: |
🔄 **Neue Version verfügbar!**
Aktuelle Version: **{{{currentVersion}}}**
@@ -173,7 +181,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 Sehen Sie, was neu ist im [**🆕 Änderungsprotokoll**]({{{changelogUrl}}})
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **Unterstützen Sie WAHA!**
Sie verwenden die kostenlose **WAHA Core** Version. Wenn Sie dieses Projekt nützlich finden, erwägen Sie bitte, seine Entwicklung zu unterstützen.
@@ -181,3 +189,51 @@ APP_CORE_VERSION_USED: |
Ihre Unterstützung ist der einzige Weg, wie wir weiterhin an Updates, Fehlerbehebungen und neuen Funktionen arbeiten können.
[**🎁 Unterstützen Sie uns**]({{{supportUrl}}})
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **Geteilte Kontakte**
{{#contacts}}
🪪 Name: **{{fullName}}**
{{#phoneNumbers}}
- 📞 Telefon: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **Geteilter Standort**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **Live-Standort wird geteilt**
Öffne **WhatsApp**, um Aktualisierungen in Echtzeit zu sehen.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **Dieser Nachrichtentyp wird in ChatWoot nicht unterstützt.**
📱 Bitte öffne **WhatsApp**, um ihn anzusehen.
Details: [{{{details.text}}}]({{{details.url}}})
@@ -1,8 +1,10 @@
# English translations for ChatWoot integration
APP_INBOX_CONTACT_NAME: 'WhatsApp Integration (WAHA)'
APP_INBOX_CONTACT_AVATAR_URL: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
locale.name: '🇺🇸 English (English)'
JOB_ERROR_REPORT: |
app.inbox.contact.name: 'WhatsApp Integration (WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 Retrying in {{attempts.nextDelay}} seconds...
@@ -19,51 +21,100 @@ JOB_ERROR_REPORT: |
```
{{/error}}
JOB_SUCCEEDED_REPORT: |
job.report.succeeded: |
✅ **Succeeded**
Attempt: {{{attempts.current}}} / {{{attempts.max}}}
Details: [{{{details.text}}}]({{{details.url}}})
WHATSAPP_ERROR_SENDING_MESSAGE: Couldn't send a message on WhatsApp.
WHATSAPP_ERROR_RECEIVING_MESSAGE: Couldn't receive a message from WhatsApp
WHATSAPP_ERROR_REMOVING_MESSAGE: Couldn't remove a message from WhatsApp
APP_SCHEDULED_JOB_ERROR: Scheduled job failed to execute
whatsapp.message.sending.error: Couldn't send a message on WhatsApp.
whatsapp.message.receiving.error: Couldn't receive a message from WhatsApp
whatsapp.message.removing.error: Couldn't remove a message from WhatsApp
job.scheduled.error.header: Scheduled job failed to execute
MESSAGE_FROM_WHATSAPP: |
message.from.whatsapp: |
{{{text}}}
📱 *Sent from WhatsApp*
MESSAGE_FROM_API: |
message.from.api: |
{{{text}}}
📱 *Sent via API*
MESSAGE_REMOVED_IN_WHATSAPP: |
message.removed.in.whatsapp: |
❌ *This message was deleted in WhatsApp*
MESSAGE_EDITED_IN_WHATSAPP: |
message.edited.in.whatsapp: |
{{{text}}}
✏️ *Edited in WhatsApp*
WHATSAPP_GROUP_MESSAGE: |
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
WHATSAPP_REACTION_ADDED: |
whatsapp.reaction.added: |
*Reacted* {{{emoji}}}
WHATSAPP_REACTION_REMOVED: |
whatsapp.reaction.removed: |
*Reaction removed*
WHATSAPP_CONTACT_GROUP_SUFFIX: 'Group'
WHATSAPP_CONTACT_CHANNEL_SUFFIX: 'Channel'
whatsapp.contact.group.suffix: 'Group'
whatsapp.contact.channel.suffix: 'Channel'
whatsapp.contact.broadcast.suffix: 'Broadcast List'
# Aka Stories
WHATSAPP_CONTACT_STATUS_NAME: 'Status'
whatsapp.contact.status.name: 'Status'
APP_CONNECTED_MESSAGE: |
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **Shared Contact(s)**
{{#contacts}}
🪪 Name: **{{fullName}}**
{{#phoneNumbers}}
- 📞 Phone: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **Shared Location**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **Sharing Live Location**
Open **WhatsApp** to see real-time updates.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **This message type is not supported in ChatWoot.**
📱 Please open **WhatsApp** to view it.
Details: [{{{details.text}}}]({{{details.url}}})
app.connected.message: |
🔗 **CONNECTED: WhatsApp Session to ChatWoot Inbox**
✅ WhatsApp session **{{{name}}}** has been successfully connected to this ChatWoot Inbox.
@@ -81,7 +132,7 @@ APP_CONNECTED_MESSAGE: |
Let's get started! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **DISCONNECTED: WhatsApp Session from ChatWoot Inbox**
❌ WhatsApp session **{{{name}}}** has been disconnected from this ChatWoot Inbox.
@@ -90,15 +141,15 @@ APP_DISCONNECTED_MESSAGE: |
To reconnect, please contact your administrator.
APP_UPDATED_MESSAGE: |
app.updated.message: |
🔄 **UPDATED: WhatsApp Session Settings**
✅ Settings for WhatsApp session **{{{name}}}** have been updated.
APP_COMMANDS_LIST: |
app.commands.list: |
You can manage **WhatsApp Integration** by sending one of the following commands:
🖥️ Sessions:
🖥️ **Session**:
1. **status** - get the current session status.
2. **restart** - restart the session - this will try to reconnect your WhatsApp account.
3. **start** - start the session.
@@ -108,42 +159,47 @@ APP_COMMANDS_LIST: |
7. **screenshot** - get a screenshot of the session (only available in **WEBJS** engine).
8. **help** - available commands.
⚙️ Server:
{{#commands.server}}
🛠️ **Server**:
- **server status** - get the server version and status information.
- **server reboot** - reboot the server gracefully.
- **server reboot force** - force reboot the server.
{{/commands.server}}
Send a **command** (case insensitive) or a **command number** to this conversation.
APP_HELP_REMINDER: |
app.commands.server.disabled: ⛔ **Server commands are disabled**.
Please contact your administrator.
Send **help** to get the list of available commands.
APP_SESSION_STATUS_CHANGE: |
app.help.reminder: |
Send **help** to get the list of available commands.
app.session.status.change: |
{{{emoji}}} "{{{session}}}" session switched to {{{status}}} status
APP_SESSION_CURRENT_STATUS: |
app.session.current.status: |
{{{emoji}}} **{{{session}}}** session status is in **{{{status}}}**
**WhatsApp Account:**
📱 **{{{name}}} ({{{id}}})**
APP_DASHBOARD_LINK: |
Visit [WAHA Dashboard]({{{url}}}) for more details.
APP_SESSION_STATUS_WORKING: |
app.session.status.working: |
✅ This ChatWoot Inbox is linked to **WhatsApp Account** and ready to send and receive messages!
📱 **{{{name}}} ({{{id}}})**
APP_SESSION_STATUS_ERROR: |
app.session.status.error: |
⚠️ **This ChatWoot Inbox is linked to WhatsApp Account but NOT ready to send and receive messages!**
Please check the session status and try restarting it or logout and scan the QR code again.
APP_SESSION_SCAN_QR_CODE: |
app.session.scan.qr.code: |
⚠️ **Scan the QR code on your phone to link WhatsApp Account with this ChatWoot Inbox!**
APP_SERVER_VERSION_AND_STATUS: |
app.server.version.and.status: |
**Version:**
```
{{{version}}}
@@ -154,16 +210,16 @@ APP_SERVER_VERSION_AND_STATUS: |
{{{status}}}
```
APP_SERVER_REBOOT: |
app.server.reboot: |
Server is rebooting gracefully...
APP_SERVER_REBOOT_FORCE: |
app.server.reboot.force: |
Server is force rebooting...
APP_LOGOUT_SUCCESS: |
app.logout.success: |
✅ Account unauthorized successfully!
APP_NEW_VERSION_AVAILABLE: |
waha.new.version.available: |
🔄 **New Version Available!**
Current version: **{{{currentVersion}}}**
@@ -171,7 +227,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 Check out what's new in the [**🆕 Changelog**]({{{changelogUrl}}})
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **Support WAHA!**
You're using the free **WAHA Core** version. If you find this project useful, please consider supporting its development.
@@ -1,7 +1,9 @@
# Spanish translations for ChatWoot integration
APP_INBOX_CONTACT_NAME: 'Integración de WhatsApp (WAHA)'
locale.name: '🇪🇸 Español (Spanish)'
app.inbox.contact.name: 'Integración de WhatsApp (WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
JOB_ERROR_REPORT: |
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 Reintentando en {{attempts.nextDelay}} segundos...
@@ -18,51 +20,52 @@ JOB_ERROR_REPORT: |
```
{{/error}}
JOB_SUCCEEDED_REPORT: |
job.report.succeeded: |
✅ **Éxito**
Intento: {{{attempts.current}}} / {{{attempts.max}}}
Detalles: [{{{details.text}}}]({{{details.url}}})
WHATSAPP_ERROR_SENDING_MESSAGE: No se pudo enviar un mensaje en WhatsApp.
WHATSAPP_ERROR_RECEIVING_MESSAGE: No se pudo recibir un mensaje de WhatsApp
WHATSAPP_ERROR_REMOVING_MESSAGE: No se pudo eliminar un mensaje de WhatsApp
APP_SCHEDULED_JOB_ERROR: La tarea programada no se pudo ejecutar
whatsapp.message.sending.error: No se pudo enviar un mensaje en WhatsApp.
whatsapp.message.receiving.error: No se pudo recibir un mensaje de WhatsApp
whatsapp.message.removing.error: No se pudo eliminar un mensaje de WhatsApp
job.scheduled.error.header: La tarea programada no se pudo ejecutar
MESSAGE_FROM_WHATSAPP: |
message.from.whatsapp: |
{{{text}}}
📱 *Enviado desde WhatsApp*
MESSAGE_FROM_API: |
message.from.api: |
{{{text}}}
📱 *Enviado a través de API*
MESSAGE_REMOVED_IN_WHATSAPP: |
message.removed.in.whatsapp: |
❌ *Este mensaje fue eliminado en WhatsApp*
MESSAGE_EDITED_IN_WHATSAPP: |
message.edited.in.whatsapp: |
{{{text}}}
✏️ *Editado en WhatsApp*
WHATSAPP_GROUP_MESSAGE: |
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
WHATSAPP_REACTION_ADDED: |
whatsapp.reaction.added: |
*Reaccionó con* {{{emoji}}}
WHATSAPP_REACTION_REMOVED: |
whatsapp.reaction.removed: |
*Reacción eliminada*
WHATSAPP_CONTACT_GROUP_SUFFIX: 'Grupo'
WHATSAPP_CONTACT_CHANNEL_SUFFIX: 'Canal'
whatsapp.contact.group.suffix: 'Grupo'
whatsapp.contact.channel.suffix: 'Canal'
whatsapp.contact.broadcast.suffix: 'Lista de difusión'
# Aka Stories
WHATSAPP_CONTACT_STATUS_NAME: 'Estado'
whatsapp.contact.status.name: 'Estado'
APP_CONNECTED_MESSAGE: |
app.connected.message: |
🔗 **CONECTADO: Sesión de WhatsApp a Bandeja de Entrada de ChatWoot**
✅ La sesión de WhatsApp **{{{name}}}** ha sido conectada exitosamente a esta Bandeja de Entrada de ChatWoot.
@@ -80,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
¡Comencemos! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **DESCONECTADO: Sesión de WhatsApp de Bandeja de Entrada de ChatWoot**
❌ La sesión de WhatsApp **{{{name}}}** ha sido desconectada de esta Bandeja de Entrada de ChatWoot.
@@ -89,15 +92,15 @@ APP_DISCONNECTED_MESSAGE: |
Para reconectar, por favor contacta a tu administrador.
APP_UPDATED_MESSAGE: |
app.updated.message: |
🔄 **ACTUALIZADO: Configuración de Sesión de WhatsApp**
✅ La configuración para la sesión de WhatsApp **{{{name}}}** ha sido actualizada.
APP_COMMANDS_LIST: |
app.commands.list: |
Puedes gestionar la **Integración de WhatsApp** enviando uno de los siguientes comandos:
🖥️ Sesiones:
🖥️ **Sesión**:
1. **status** - obtener el estado actual de la sesión.
2. **restart** - reiniciar la sesión - esto intentará reconectar tu cuenta de WhatsApp.
3. **start** - iniciar la sesión.
@@ -107,42 +110,50 @@ APP_COMMANDS_LIST: |
7. **screenshot** - obtener una captura de pantalla de la sesión (solo disponible en el motor **WEBJS**).
8. **help** - comandos disponibles.
{{#commands.server}}
{{#commands.server}}
⚙️ Servidor:
- **server status** - obtener información de versión y estado del servidor.
- **server reboot** - reiniciar el servidor de forma segura.
- **server reboot force** - forzar el reinicio del servidor.
{{/commands.server}}
{{/commands.server}}
Envía un **comando** (no sensible a mayúsculas/minúsculas) o un **número de comando** a esta conversación.
APP_HELP_REMINDER: |
app.commands.server.disabled: ⛔ **Los comandos del servidor están
deshabilitados**.
Por favor, contacta a tu administrador.
Envía **help** para obtener la lista de comandos disponibles.
APP_SESSION_STATUS_CHANGE: |
app.help.reminder: |
Envía **help** para obtener la lista de comandos disponibles.
app.session.status.change: |
{{{emoji}}} La sesión "{{{session}}}" cambió al estado {{{status}}}
APP_SESSION_CURRENT_STATUS: |
app.session.current.status: |
{{{emoji}}} El estado de la sesión **{{{session}}}** es **{{{status}}}**
**Cuenta de WhatsApp:**
📱 **{{{name}}} ({{{id}}})**
APP_DASHBOARD_LINK: |
Visita el [Panel de WAHA]({{{url}}}) para más detalles.
APP_SESSION_STATUS_WORKING: |
app.session.status.working: |
✅ ¡Esta Bandeja de Entrada de ChatWoot está vinculada a la **Cuenta de WhatsApp** y lista para enviar y recibir mensajes!
📱 **{{{name}}} ({{{id}}})**
APP_SESSION_STATUS_ERROR: |
app.session.status.error: |
⚠️ **¡Esta Bandeja de Entrada de ChatWoot está vinculada a la Cuenta de WhatsApp pero NO está lista para enviar y recibir mensajes!**
Por favor, comprueba el estado de la sesión e intenta reiniciarla o cierra sesión y escanea el código QR de nuevo.
APP_SESSION_SCAN_QR_CODE: |
app.session.scan.qr.code: |
⚠️ **¡Escanea el código QR en tu teléfono para vincular la Cuenta de WhatsApp con esta Bandeja de Entrada de ChatWoot!**
APP_SERVER_VERSION_AND_STATUS: |
app.server.version.and.status: |
**Versión:**
```
{{{version}}}
@@ -153,16 +164,16 @@ APP_SERVER_VERSION_AND_STATUS: |
{{{status}}}
```
APP_SERVER_REBOOT: |
app.server.reboot: |
El servidor se está reiniciando de forma segura...
APP_SERVER_REBOOT_FORCE: |
app.server.reboot.force: |
El servidor se está reiniciando forzadamente...
APP_LOGOUT_SUCCESS: |
app.logout.success: |
✅ ¡Cuenta desautorizada con éxito!
APP_NEW_VERSION_AVAILABLE: |
waha.new.version.available: |
🔄 **¡Nueva Versión Disponible!**
Versión actual: **{{{currentVersion}}}**
@@ -170,7 +181,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 Mira qué hay de nuevo en el [**🆕 Registro de Cambios**]({{{changelogUrl}}})
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **¡Apoya a WAHA!**
Estás usando la versión gratuita **WAHA Core**. Si encuentras útil este proyecto, por favor considera apoyar su desarrollo.
@@ -178,3 +189,51 @@ APP_CORE_VERSION_USED: |
Tu apoyo es la única forma en que podemos seguir trabajando en actualizaciones, correcciones y nuevas funciones.
[**🎁 Apóyanos**]({{{supportUrl}}})
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **Contacto(s) compartido(s)**
{{#contacts}}
🪪 Nombre: **{{fullName}}**
{{#phoneNumbers}}
- 📞 Teléfono: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **Ubicación compartida**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **Compartiendo ubicación en tiempo real**
Abre **WhatsApp** para ver las actualizaciones en tiempo real.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **Este tipo de mensaje no es compatible con ChatWoot.**
📱 Por favor, abre **WhatsApp** para verlo.
Detalles: [{{{details.text}}}]({{{details.url}}})
@@ -1,7 +1,9 @@
# Persian translations for ChatWoot integration
APP_INBOX_CONTACT_NAME: 'ادغام واتساپ (WAHA)'
locale.name: '🇮🇷 فارسی (Persian)'
app.inbox.contact.name: 'ادغام واتساپ (WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
JOB_ERROR_REPORT: |
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 تلاش مجدد در {{attempts.nextDelay}} ثانیه...
@@ -18,51 +20,52 @@ JOB_ERROR_REPORT: |
```
{{/error}}
JOB_SUCCEEDED_REPORT: |
job.report.succeeded: |
✅ **موفقیت‌آمیز**
تلاش: {{{attempts.current}}} / {{{attempts.max}}}
جزئیات: [{{{details.text}}}]({{{details.url}}})
WHATSAPP_ERROR_SENDING_MESSAGE: ارسال پیام در واتساپ امکان‌پذیر نبود.
WHATSAPP_ERROR_RECEIVING_MESSAGE: دریافت پیام از واتساپ امکان‌پذیر نبود
WHATSAPP_ERROR_REMOVING_MESSAGE: حذف پیام از واتساپ امکان‌پذیر نبود
APP_SCHEDULED_JOB_ERROR: اجرای کار زمان‌بندی شده ناموفق بود
whatsapp.message.sending.error: ارسال پیام در واتساپ امکان‌پذیر نبود.
whatsapp.message.receiving.error: دریافت پیام از واتساپ امکان‌پذیر نبود
whatsapp.message.removing.error: حذف پیام از واتساپ امکان‌پذیر نبود
job.scheduled.error.header: اجرای کار زمان‌بندی شده ناموفق بود
MESSAGE_FROM_WHATSAPP: |
message.from.whatsapp: |
{{{text}}}
📱 *ارسال شده از واتساپ*
MESSAGE_FROM_API: |
message.from.api: |
{{{text}}}
📱 *ارسال شده از طریق API*
MESSAGE_REMOVED_IN_WHATSAPP: |
message.removed.in.whatsapp: |
❌ *این پیام در واتساپ حذف شده است*
MESSAGE_EDITED_IN_WHATSAPP: |
message.edited.in.whatsapp: |
{{{text}}}
✏️ *ویرایش شده در واتساپ*
WHATSAPP_GROUP_MESSAGE: |
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
WHATSAPP_REACTION_ADDED: |
whatsapp.reaction.added: |
*واکنش* {{{emoji}}}
WHATSAPP_REACTION_REMOVED: |
whatsapp.reaction.removed: |
*واکنش حذف شد*
WHATSAPP_CONTACT_GROUP_SUFFIX: 'گروه'
WHATSAPP_CONTACT_CHANNEL_SUFFIX: 'کانال'
whatsapp.contact.group.suffix: 'گروه'
whatsapp.contact.channel.suffix: 'کانال'
whatsapp.contact.broadcast.suffix: 'فهرست انتشار'
# Aka Stories
WHATSAPP_CONTACT_STATUS_NAME: 'وضعیت'
whatsapp.contact.status.name: 'وضعیت'
APP_CONNECTED_MESSAGE: |
app.connected.message: |
🔗 **متصل: جلسه واتساپ به صندوق ورودی ChatWoot**
✅ جلسه واتساپ **{{{name}}}** با موفقیت به این صندوق ورودی ChatWoot متصل شده است.
@@ -80,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
بیایید شروع کنیم! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **قطع اتصال: جلسه واتساپ از صندوق ورودی ChatWoot**
❌ جلسه واتساپ **{{{name}}}** از این صندوق ورودی ChatWoot قطع شده است.
@@ -89,15 +92,15 @@ APP_DISCONNECTED_MESSAGE: |
برای اتصال مجدد، لطفاً با مدیر خود تماس بگیرید.
APP_UPDATED_MESSAGE: |
app.updated.message: |
🔄 **به‌روزرسانی: تنظیمات جلسه واتساپ**
✅ تنظیمات جلسه واتساپ **{{{name}}}** به‌روزرسانی شده است.
APP_COMMANDS_LIST: |
app.commands.list: |
شما می‌توانید **ادغام واتساپ** را با ارسال یکی از دستورات زیر مدیریت کنید:
🖥️ جلسات:
🖥️ **جلسه**:
1. **status** - دریافت وضعیت فعلی جلسه.
2. **restart** - راه‌اندازی مجدد جلسه - این تلاش خواهد کرد حساب واتساپ شما را مجدداً متصل کند.
3. **start** - شروع جلسه.
@@ -107,42 +110,47 @@ APP_COMMANDS_LIST: |
7. **screenshot** - دریافت اسکرین‌شات از جلسه (فقط در موتور **WEBJS** در دسترس است).
8. **help** - دستورات موجود.
{{#commands.server}}
⚙️ سرور:
- **server status** - دریافت اطلاعات نسخه و وضعیت سرور.
- **server reboot** - راه‌اندازی مجدد سرور به صورت نرم.
- **server reboot force** - راه‌اندازی مجدد اجباری سرور.
{{/commands.server}}
یک **دستور** (بدون حساسیت به بزرگی و کوچکی حروف) یا **شماره دستور** را به این گفتگو ارسال کنید.
APP_HELP_REMINDER: |
app.commands.server.disabled: ⛔ **دستورات سرور غیرفعال است**.
لطفاً با مدیر سیستم خود تماس بگیرید.
برای دریافت فهرست دستورات، **help** را ارسال کنید.
app.help.reminder: |
برای دریافت لیست دستورات موجود، **help** را ارسال کنید.
APP_SESSION_STATUS_CHANGE: |
app.session.status.change: |
{{{emoji}}} جلسه "{{{session}}}" به وضعیت {{{status}}} تغییر کرد
APP_SESSION_CURRENT_STATUS: |
app.session.current.status: |
{{{emoji}}} وضعیت جلسه **{{{session}}}** در **{{{status}}}** است
**حساب واتساپ:**
📱 **{{{name}}} ({{{id}}})**
APP_DASHBOARD_LINK: |
برای جزئیات بیشتر به [داشبورد WAHA]({{{url}}}) مراجعه کنید.
APP_SESSION_STATUS_WORKING: |
app.session.status.working: |
✅ این صندوق ورودی ChatWoot به **حساب واتساپ** متصل است و آماده ارسال و دریافت پیام است!
📱 **{{{name}}} ({{{id}}})**
APP_SESSION_STATUS_ERROR: |
app.session.status.error: |
⚠️ **این صندوق ورودی ChatWoot به حساب واتساپ متصل است اما آماده ارسال و دریافت پیام نیست!**
لطفاً وضعیت جلسه را بررسی کنید و سعی کنید آن را مجدداً راه‌اندازی کنید یا خارج شوید و کد QR را دوباره اسکن کنید.
APP_SESSION_SCAN_QR_CODE: |
app.session.scan.qr.code: |
⚠️ **کد QR را در تلفن خود اسکن کنید تا حساب واتساپ را با این صندوق ورودی ChatWoot پیوند دهید!**
APP_SERVER_VERSION_AND_STATUS: |
app.server.version.and.status: |
**نسخه:**
```
{{{version}}}
@@ -153,16 +161,16 @@ APP_SERVER_VERSION_AND_STATUS: |
{{{status}}}
```
APP_SERVER_REBOOT: |
app.server.reboot: |
سرور در حال راه‌اندازی مجدد نرم است...
APP_SERVER_REBOOT_FORCE: |
app.server.reboot.force: |
سرور در حال راه‌اندازی مجدد اجباری است...
APP_LOGOUT_SUCCESS: |
app.logout.success: |
✅ حساب با موفقیت غیرمجاز شد!
APP_NEW_VERSION_AVAILABLE: |
waha.new.version.available: |
🔄 **نسخه جدید در دسترس است!**
نسخه فعلی: **{{{currentVersion}}}**
@@ -170,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 موارد جدید را در [**🆕 تغییرات**]({{{changelogUrl}}}) مشاهده کنید
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **از WAHA پشتیبانی کنید!**
شما از نسخه رایگان **WAHA Core** استفاده می‌کنید. اگر این پروژه را مفید می‌دانید، لطفاً حمایت از توسعه آن را در نظر بگیرید.
@@ -178,3 +186,51 @@ APP_CORE_VERSION_USED: |
حمایت شما تنها راهی است که ما می‌توانیم به کار بر روی به‌روزرسانی‌ها، رفع اشکالات و ویژگی‌های جدید ادامه دهیم.
[**🎁 از ما حمایت کنید**]({{{supportUrl}}})
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **مخاطب(ها)ی به‌اشتراک‌گذاشته‌شده**
{{#contacts}}
🪪 نام: **{{fullName}}**
{{#phoneNumbers}}
- 📞 تلفن: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **موقعیت مکانی به‌اشتراک‌گذاشته‌شده**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **اشتراک‌گذاری موقعیت مکانی زنده**
برای دیدن به‌روزرسانی‌های لحظه‌ای **WhatsApp** را باز کنید.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **این نوع پیام در ChatWoot پشتیبانی نمی‌شود.**
📱 لطفاً برای مشاهده آن **WhatsApp** را باز کنید.
جزئیات: [{{{details.text}}}]({{{details.url}}})
@@ -1,7 +1,9 @@
# French translations for ChatWoot integration
APP_INBOX_CONTACT_NAME: 'Intégration WhatsApp (WAHA)'
locale.name: '🇫🇷 Français (French)'
app.inbox.contact.name: 'Intégration WhatsApp (WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
JOB_ERROR_REPORT: |
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 Nouvelle tentative dans {{attempts.nextDelay}} secondes...
@@ -18,51 +20,52 @@ JOB_ERROR_REPORT: |
```
{{/error}}
JOB_SUCCEEDED_REPORT: |
job.report.succeeded: |
✅ **Réussi**
Tentative : {{{attempts.current}}} / {{{attempts.max}}}
Détails : [{{{details.text}}}]({{{details.url}}})
WHATSAPP_ERROR_SENDING_MESSAGE: Impossible d'envoyer un message sur WhatsApp.
WHATSAPP_ERROR_RECEIVING_MESSAGE: Impossible de recevoir un message de WhatsApp
WHATSAPP_ERROR_REMOVING_MESSAGE: Impossible de supprimer un message de WhatsApp
APP_SCHEDULED_JOB_ERROR: La tâche planifiée n'a pas pu être exécutée
whatsapp.message.sending.error: Impossible d'envoyer un message sur WhatsApp.
whatsapp.message.receiving.error: Impossible de recevoir un message de WhatsApp
whatsapp.message.removing.error: Impossible de supprimer un message de WhatsApp
job.scheduled.error.header: La tâche planifiée n'a pas pu être exécutée
MESSAGE_FROM_WHATSAPP: |
message.from.whatsapp: |
{{{text}}}
📱 *Envoyé depuis WhatsApp*
MESSAGE_FROM_API: |
message.from.api: |
{{{text}}}
📱 *Envoyé via API*
MESSAGE_REMOVED_IN_WHATSAPP: |
message.removed.in.whatsapp: |
❌ *Ce message a été supprimé dans WhatsApp*
MESSAGE_EDITED_IN_WHATSAPP: |
message.edited.in.whatsapp: |
{{{text}}}
✏️ *Modifié dans WhatsApp*
WHATSAPP_GROUP_MESSAGE: |
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
WHATSAPP_REACTION_ADDED: |
whatsapp.reaction.added: |
*A réagi avec* {{{emoji}}}
WHATSAPP_REACTION_REMOVED: |
whatsapp.reaction.removed: |
*Réaction supprimée*
WHATSAPP_CONTACT_GROUP_SUFFIX: 'Groupe'
WHATSAPP_CONTACT_CHANNEL_SUFFIX: 'Canal'
whatsapp.contact.group.suffix: 'Groupe'
whatsapp.contact.channel.suffix: 'Canal'
whatsapp.contact.broadcast.suffix: 'Liste de diffusion'
# Aka Stories
WHATSAPP_CONTACT_STATUS_NAME: 'Statut'
whatsapp.contact.status.name: 'Statut'
APP_CONNECTED_MESSAGE: |
app.connected.message: |
🔗 **CONNECTÉ : Session WhatsApp à la Boîte de Réception ChatWoot**
✅ La session WhatsApp **{{{name}}}** a été connectée avec succès à cette Boîte de Réception ChatWoot.
@@ -80,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
Commençons ! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **DÉCONNECTÉ : Session WhatsApp de la Boîte de Réception ChatWoot**
❌ La session WhatsApp **{{{name}}}** a été déconnectée de cette Boîte de Réception ChatWoot.
@@ -89,15 +92,15 @@ APP_DISCONNECTED_MESSAGE: |
Pour vous reconnecter, veuillez contacter votre administrateur.
APP_UPDATED_MESSAGE: |
app.updated.message: |
🔄 **MIS À JOUR : Paramètres de Session WhatsApp**
✅ Les paramètres de la session WhatsApp **{{{name}}}** ont été mis à jour.
APP_COMMANDS_LIST: |
app.commands.list: |
Vous pouvez gérer l'**Intégration WhatsApp** en envoyant l'une des commandes suivantes :
🖥️ Sessions :
🖥️ **Session** :
1. **status** - obtenir l'état actuel de la session.
2. **restart** - redémarrer la session - cela tentera de reconnecter votre compte WhatsApp.
3. **start** - démarrer la session.
@@ -107,42 +110,47 @@ APP_COMMANDS_LIST: |
7. **screenshot** - obtenir une capture d'écran de la session (disponible uniquement dans le moteur **WEBJS**).
8. **help** - commandes disponibles.
{{#commands.server}}
⚙️ Serveur :
- **server status** - obtenir les informations de version et d'état du serveur.
- **server reboot** - redémarrer le serveur en douceur.
- **server reboot force** - forcer le redémarrage du serveur.
{{/commands.server}}
Envoyez une **commande** (insensible à la casse) ou un **numéro de commande** à cette conversation.
APP_HELP_REMINDER: |
app.commands.server.disabled: ⛔ **Les commandes serveur sont désactivées**.
Veuillez contacter votre administrateur.
Envoyez **help** pour obtenir la liste des commandes disponibles.
APP_SESSION_STATUS_CHANGE: |
app.help.reminder: |
Envoyez **help** pour obtenir la liste des commandes disponibles.
app.session.status.change: |
{{{emoji}}} La session "{{{session}}}" est passée au statut {{{status}}}
APP_SESSION_CURRENT_STATUS: |
app.session.current.status: |
{{{emoji}}} Le statut de la session **{{{session}}}** est **{{{status}}}**
**Compte WhatsApp :**
📱 **{{{name}}} ({{{id}}})**
APP_DASHBOARD_LINK: |
Visitez le [Tableau de bord WAHA]({{{url}}}) pour plus de détails.
APP_SESSION_STATUS_WORKING: |
app.session.status.working: |
✅ Cette Boîte de Réception ChatWoot est liée au **Compte WhatsApp** et prête à envoyer et recevoir des messages !
📱 **{{{name}}} ({{{id}}})**
APP_SESSION_STATUS_ERROR: |
app.session.status.error: |
⚠️ **Cette Boîte de Réception ChatWoot est liée au Compte WhatsApp mais N'EST PAS prête à envoyer et recevoir des messages !**
Veuillez vérifier l'état de la session et essayer de la redémarrer ou déconnectez-vous et scannez à nouveau le code QR.
APP_SESSION_SCAN_QR_CODE: |
app.session.scan.qr.code: |
⚠️ **Scannez le code QR sur votre téléphone pour lier le Compte WhatsApp à cette Boîte de Réception ChatWoot !**
APP_SERVER_VERSION_AND_STATUS: |
app.server.version.and.status: |
**Version :**
```
{{{version}}}
@@ -153,16 +161,16 @@ APP_SERVER_VERSION_AND_STATUS: |
{{{status}}}
```
APP_SERVER_REBOOT: |
app.server.reboot: |
Le serveur redémarre en douceur...
APP_SERVER_REBOOT_FORCE: |
app.server.reboot.force: |
Le serveur redémarre de force...
APP_LOGOUT_SUCCESS: |
app.logout.success: |
✅ Compte déconnecté avec succès !
APP_NEW_VERSION_AVAILABLE: |
waha.new.version.available: |
🔄 **Nouvelle Version Disponible !**
Version actuelle : **{{{currentVersion}}}**
@@ -170,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 Découvrez les nouveautés dans le [**🆕 Journal des modifications**]({{{changelogUrl}}})
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **Soutenez WAHA !**
Vous utilisez la version gratuite **WAHA Core**. Si vous trouvez ce projet utile, veuillez envisager de soutenir son développement.
@@ -178,3 +186,51 @@ APP_CORE_VERSION_USED: |
Votre soutien est le seul moyen pour nous de continuer à travailler sur les mises à jour, les corrections et les nouvelles fonctionnalités.
[**🎁 Soutenez-nous**]({{{supportUrl}}})
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **Contact(s) partagé(s)**
{{#contacts}}
🪪 Nom: **{{fullName}}**
{{#phoneNumbers}}
- 📞 Téléphone: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **Position partagée**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **Partage de position en direct**
Ouvrez **WhatsApp** pour voir les mises à jour en temps réel.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **Ce type de message n’est pas pris en charge dans ChatWoot.**
📱 Veuillez ouvrir **WhatsApp** pour l’afficher.
Détails: [{{{details.text}}}]({{{details.url}}})
@@ -1,7 +1,9 @@
# Hebrew translations for ChatWoot integration
APP_INBOX_CONTACT_NAME: 'אינטגרציית וואטסאפ (WAHA)'
locale.name: '🇮🇱 עברית (Hebrew)'
app.inbox.contact.name: 'אינטגרציית וואטסאפ (WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
JOB_ERROR_REPORT: |
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 מנסה שוב בעוד {{attempts.nextDelay}} שניות...
@@ -18,51 +20,52 @@ JOB_ERROR_REPORT: |
```
{{/error}}
JOB_SUCCEEDED_REPORT: |
job.report.succeeded: |
✅ **הצליח**
ניסיון: {{{attempts.current}}} / {{{attempts.max}}}
פרטים: [{{{details.text}}}]({{{details.url}}})
WHATSAPP_ERROR_SENDING_MESSAGE: לא ניתן לשלוח הודעה בוואטסאפ.
WHATSAPP_ERROR_RECEIVING_MESSAGE: לא ניתן לקבל הודעה מוואטסאפ
WHATSAPP_ERROR_REMOVING_MESSAGE: לא ניתן להסיר הודעה מוואטסאפ
APP_SCHEDULED_JOB_ERROR: המשימה המתוזמנת נכשלה בביצוע
whatsapp.message.sending.error: לא ניתן לשלוח הודעה בוואטסאפ.
whatsapp.message.receiving.error: לא ניתן לקבל הודעה מוואטסאפ
whatsapp.message.removing.error: לא ניתן להסיר הודעה מוואטסאפ
job.scheduled.error.header: המשימה המתוזמנת נכשלה בביצוע
MESSAGE_FROM_WHATSAPP: |
message.from.whatsapp: |
{{{text}}}
📱 *נשלח מוואטסאפ*
MESSAGE_FROM_API: |
message.from.api: |
{{{text}}}
📱 *נשלח דרך API*
MESSAGE_REMOVED_IN_WHATSAPP: |
message.removed.in.whatsapp: |
❌ *הודעה זו נמחקה בוואטסאפ*
MESSAGE_EDITED_IN_WHATSAPP: |
message.edited.in.whatsapp: |
{{{text}}}
✏️ *נערך בוואטסאפ*
WHATSAPP_GROUP_MESSAGE: |
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
WHATSAPP_REACTION_ADDED: |
whatsapp.reaction.added: |
*הגיב* {{{emoji}}}
WHATSAPP_REACTION_REMOVED: |
whatsapp.reaction.removed: |
*התגובה הוסרה*
WHATSAPP_CONTACT_GROUP_SUFFIX: 'קבוצה'
WHATSAPP_CONTACT_CHANNEL_SUFFIX: 'ערוץ'
whatsapp.contact.group.suffix: 'קבוצה'
whatsapp.contact.channel.suffix: 'ערוץ'
whatsapp.contact.broadcast.suffix: 'רשימת שידור'
# Aka Stories
WHATSAPP_CONTACT_STATUS_NAME: 'סטטוס'
whatsapp.contact.status.name: 'סטטוס'
APP_CONNECTED_MESSAGE: |
app.connected.message: |
🔗 **מחובר: הפעלת וואטסאפ לתיבת הדואר הנכנס של ChatWoot**
✅ הפעלת וואטסאפ **{{{name}}}** חוברה בהצלחה לתיבת הדואר הנכנס של ChatWoot.
@@ -80,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
בוא נתחיל! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **מנותק: הפעלת וואטסאפ מתיבת הדואר הנכנס של ChatWoot**
❌ הפעלת וואטסאפ **{{{name}}}** נותקה מתיבת הדואר הנכנס של ChatWoot.
@@ -89,15 +92,15 @@ APP_DISCONNECTED_MESSAGE: |
כדי להתחבר מחדש, אנא פנה למנהל המערכת שלך.
APP_UPDATED_MESSAGE: |
app.updated.message: |
🔄 **עודכן: הגדרות הפעלת וואטסאפ**
✅ ההגדרות עבור הפעלת וואטסאפ **{{{name}}}** עודכנו.
APP_COMMANDS_LIST: |
app.commands.list: |
אתה יכול לנהל **אינטגרציית וואטסאפ** על ידי שליחת אחת מהפקודות הבאות:
🖥️ הפעלות:
🖥️ **הפעלה**:
1. **status** - קבל את סטטוס ההפעלה הנוכחי.
2. **restart** - הפעל מחדש את ההפעלה - זה ינסה לחבר מחדש את חשבון הוואטסאפ שלך.
3. **start** - התחל את ההפעלה.
@@ -107,42 +110,47 @@ APP_COMMANDS_LIST: |
7. **screenshot** - קבל צילום מסך של ההפעלה (זמין רק במנוע **WEBJS**).
8. **help** - פקודות זמינות.
{{#commands.server}}
⚙️ שרת:
- **server status** - קבל מידע על גרסת השרת והסטטוס.
- **server reboot** - הפעל מחדש את השרת בצורה חלקה.
- **server reboot force** - אלץ הפעלה מחדש של השרת.
{{/commands.server}}
שלח **פקודה** (לא תלוי רישיות) או **מספר פקודה** לשיחה זו.
APP_HELP_REMINDER: |
app.commands.server.disabled: ⛔ **פקודות השרת מושבתות**.
אנא פנו למנהל המערכת שלכם.
שלחו **help** כדי לקבל את רשימת הפקודות הזמינות.
app.help.reminder: |
שלח **help** כדי לקבל את רשימת הפקודות הזמינות.
APP_SESSION_STATUS_CHANGE: |
app.session.status.change: |
{{{emoji}}} הפעלה "{{{session}}}" עברה לסטטוס {{{status}}}
APP_SESSION_CURRENT_STATUS: |
app.session.current.status: |
{{{emoji}}} סטטוס ההפעלה **{{{session}}}** הוא **{{{status}}}**
**חשבון וואטסאפ:**
📱 **{{{name}}} ({{{id}}})**
APP_DASHBOARD_LINK: |
בקר ב[לוח הבקרה של WAHA]({{{url}}}) לפרטים נוספים.
APP_SESSION_STATUS_WORKING: |
app.session.status.working: |
✅ תיבת הדואר הנכנס של ChatWoot מקושרת ל**חשבון וואטסאפ** ומוכנה לשלוח ולקבל הודעות!
📱 **{{{name}}} ({{{id}}})**
APP_SESSION_STATUS_ERROR: |
app.session.status.error: |
⚠️ **תיבת הדואר הנכנס של ChatWoot מקושרת לחשבון וואטסאפ אך אינה מוכנה לשלוח ולקבל הודעות!**
אנא בדוק את סטטוס ההפעלה ונסה להפעיל אותה מחדש או התנתק וסרוק את קוד ה-QR שוב.
APP_SESSION_SCAN_QR_CODE: |
app.session.scan.qr.code: |
⚠️ **סרוק את קוד ה-QR בטלפון שלך כדי לקשר את חשבון הוואטסאפ לתיבת הדואר הנכנס של ChatWoot!**
APP_SERVER_VERSION_AND_STATUS: |
app.server.version.and.status: |
**גרסה:**
```
{{{version}}}
@@ -153,16 +161,16 @@ APP_SERVER_VERSION_AND_STATUS: |
{{{status}}}
```
APP_SERVER_REBOOT: |
app.server.reboot: |
השרת מופעל מחדש בצורה חלקה...
APP_SERVER_REBOOT_FORCE: |
app.server.reboot.force: |
השרת מופעל מחדש בכוח...
APP_LOGOUT_SUCCESS: |
app.logout.success: |
✅ החשבון בוטל בהצלחה!
APP_NEW_VERSION_AVAILABLE: |
waha.new.version.available: |
🔄 **גרסה חדשה זמינה!**
גרסה נוכחית: **{{{currentVersion}}}**
@@ -170,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 בדוק מה חדש ב[**🆕 יומן שינויים**]({{{changelogUrl}}})
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **תמוך ב-WAHA!**
אתה משתמש בגרסת **WAHA Core** החינמית. אם אתה מוצא פרויקט זה שימושי, אנא שקול לתמוך בפיתוח שלו.
@@ -178,3 +186,51 @@ APP_CORE_VERSION_USED: |
התמיכה שלך היא הדרך היחידה שבה אנחנו יכולים להמשיך לעבוד על עדכונים, תיקונים ותכונות חדשות.
[**🎁 תמוך בנו**]({{{supportUrl}}})
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **אנשי קשר משותפים**
{{#contacts}}
🪪 שם: **{{fullName}}**
{{#phoneNumbers}}
- 📞 טלפון: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **מיקום משותף**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **משתף מיקום חי**
פתח/י את **WhatsApp** כדי לראות עדכונים בזמן אמת.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **סוג הודעה זה אינו נתמך ב-ChatWoot.**
📱 אנא פתח/י את **WhatsApp** כדי לצפות בזה.
פרטים: [{{{details.text}}}]({{{details.url}}})
@@ -1,7 +1,9 @@
# Hindi translations for ChatWoot integration
APP_INBOX_CONTACT_NAME: 'व्हाट्सएप इंटीग्रेशन (WAHA)'
locale.name: '🇮🇳 हिंदी (Hindi)'
app.inbox.contact.name: 'व्हाट्सएप इंटीग्रेशन (WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
JOB_ERROR_REPORT: |
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 {{attempts.nextDelay}} सेकंड में पुनः प्रयास कर रहा है...
@@ -18,51 +20,52 @@ JOB_ERROR_REPORT: |
```
{{/error}}
JOB_SUCCEEDED_REPORT: |
job.report.succeeded: |
✅ **सफल**
प्रयास: {{{attempts.current}}} / {{{attempts.max}}}
विवरण: [{{{details.text}}}]({{{details.url}}})
WHATSAPP_ERROR_SENDING_MESSAGE: व्हाट्सएप पर संदेश नहीं भेज सका।
WHATSAPP_ERROR_RECEIVING_MESSAGE: व्हाट्सएप से संदेश प्राप्त नहीं कर सका
WHATSAPP_ERROR_REMOVING_MESSAGE: व्हाट्सएप से संदेश हटा नहीं सका
APP_SCHEDULED_JOB_ERROR: निर्धारित कार्य निष्पादित करने में विफल रहा
whatsapp.message.sending.error: व्हाट्सएप पर संदेश नहीं भेज सका।
whatsapp.message.receiving.error: व्हाट्सएप से संदेश प्राप्त नहीं कर सका
whatsapp.message.removing.error: व्हाट्सएप से संदेश हटा नहीं सका
job.scheduled.error.header: निर्धारित कार्य निष्पादित करने में विफल रहा
MESSAGE_FROM_WHATSAPP: |
message.from.whatsapp: |
{{{text}}}
📱 *व्हाट्सएप से भेजा गया*
MESSAGE_FROM_API: |
message.from.api: |
{{{text}}}
📱 *API के माध्यम से भेजा गया*
MESSAGE_REMOVED_IN_WHATSAPP: |
message.removed.in.whatsapp: |
❌ *यह संदेश व्हाट्सएप में हटा दिया गया था*
MESSAGE_EDITED_IN_WHATSAPP: |
message.edited.in.whatsapp: |
{{{text}}}
✏️ *व्हाट्सएप में संपादित*
WHATSAPP_GROUP_MESSAGE: |
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
WHATSAPP_REACTION_ADDED: |
whatsapp.reaction.added: |
*प्रतिक्रिया दी* {{{emoji}}}
WHATSAPP_REACTION_REMOVED: |
whatsapp.reaction.removed: |
*प्रतिक्रिया हटा दी गई*
WHATSAPP_CONTACT_GROUP_SUFFIX: 'समूह'
WHATSAPP_CONTACT_CHANNEL_SUFFIX: 'चैनल'
whatsapp.contact.group.suffix: 'समूह'
whatsapp.contact.channel.suffix: 'चैनल'
whatsapp.contact.broadcast.suffix: 'प्रसारण सूची'
# Aka Stories
WHATSAPP_CONTACT_STATUS_NAME: 'स्थिति'
whatsapp.contact.status.name: 'स्थिति'
APP_CONNECTED_MESSAGE: |
app.connected.message: |
🔗 **कनेक्टेड: व्हाट्सएप सत्र से ChatWoot इनबॉक्स**
✅ व्हाट्सएप सत्र **{{{name}}}** सफलतापूर्वक इस ChatWoot इनबॉक्स से जुड़ गया है।
@@ -80,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
चलिए शुरू करते हैं! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **डिस्कनेक्टेड: व्हाट्सएप सत्र से ChatWoot इनबॉक्स**
❌ व्हाट्सएप सत्र **{{{name}}}** इस ChatWoot इनबॉक्स से डिस्कनेक्ट हो गया है।
@@ -89,15 +92,15 @@ APP_DISCONNECTED_MESSAGE: |
फिर से कनेक्ट करने के लिए, कृपया अपने प्रशासक से संपर्क करें।
APP_UPDATED_MESSAGE: |
app.updated.message: |
🔄 **अपडेटेड: व्हाट्सएप सत्र सेटिंग्स**
✅ व्हाट्सएप सत्र **{{{name}}}** की सेटिंग्स अपडेट की गई हैं।
APP_COMMANDS_LIST: |
app.commands.list: |
आप निम्न कमांड में से एक भेजकर **व्हाट्सएप इंटीग्रेशन** का प्रबंधन कर सकते हैं:
🖥️ सत्र:
🖥️ **सेशन**:
1. **status** - वर्तमान सत्र स्थिति प्राप्त करें।
2. **restart** - सत्र को पुनरारंभ करें - यह आपके व्हाट्सएप खाते को फिर से कनेक्ट करने का प्रयास करेगा।
3. **start** - सत्र शुरू करें।
@@ -107,42 +110,47 @@ APP_COMMANDS_LIST: |
7. **screenshot** - सत्र का स्क्रीनशॉट प्राप्त करें (केवल **WEBJS** इंजन में उपलब्ध)।
8. **help** - उपलब्ध कमांड।
{{#commands.server}}
⚙️ सर्वर:
- **server status** - सर्वर संस्करण और स्थिति जानकारी प्राप्त करें।
- **server reboot** - सर्वर को सुचारू रूप से पुनरारंभ करें।
- **server reboot force** - सर्वर को जबरदस्ती पुनरारंभ करें।
{{/commands.server}}
इस वार्तालाप में **कमांड** (केस इनसेंसिटिव) या **कमांड नंबर** भेजें।
APP_HELP_REMINDER: |
app.commands.server.disabled: ⛔ **सर्वर कमांड अक्षम हैं**।
कृपया अपने व्यवस्थापक से संपर्क करें।
उपलब्ध कमांड की सूची पाने के लिए **help** भेजें।
app.help.reminder: |
उपलब्ध कमांड की सूची प्राप्त करने के लिए **help** भेजें।
APP_SESSION_STATUS_CHANGE: |
app.session.status.change: |
{{{emoji}}} "{{{session}}}" सत्र {{{status}}} स्थिति में बदल गया
APP_SESSION_CURRENT_STATUS: |
app.session.current.status: |
{{{emoji}}} **{{{session}}}** सत्र की स्थिति **{{{status}}}** में है
**व्हाट्सएप खाता:**
📱 **{{{name}}} ({{{id}}})**
APP_DASHBOARD_LINK: |
अधिक जानकारी के लिए [WAHA डैशबोर्ड]({{{url}}}) पर जाएं।
APP_SESSION_STATUS_WORKING: |
app.session.status.working: |
✅ यह ChatWoot इनबॉक्स **व्हाट्सएप खाते** से जुड़ा हुआ है और संदेश भेजने और प्राप्त करने के लिए तैयार है!
📱 **{{{name}}} ({{{id}}})**
APP_SESSION_STATUS_ERROR: |
app.session.status.error: |
⚠️ **यह ChatWoot इनबॉक्स व्हाट्सएप खाते से जुड़ा हुआ है लेकिन संदेश भेजने और प्राप्त करने के लिए तैयार नहीं है!**
कृपया सत्र की स्थिति जांचें और इसे पुनरारंभ करने का प्रयास करें या लॉग आउट करें और QR कोड फिर से स्कैन करें।
APP_SESSION_SCAN_QR_CODE: |
app.session.scan.qr.code: |
⚠️ **इस ChatWoot इनबॉक्स के साथ व्हाट्सएप खाता जोड़ने के लिए अपने फोन पर QR कोड स्कैन करें!**
APP_SERVER_VERSION_AND_STATUS: |
app.server.version.and.status: |
**संस्करण:**
```
{{{version}}}
@@ -153,16 +161,16 @@ APP_SERVER_VERSION_AND_STATUS: |
{{{status}}}
```
APP_SERVER_REBOOT: |
app.server.reboot: |
सर्वर सुचारू रूप से पुनरारंभ हो रहा है...
APP_SERVER_REBOOT_FORCE: |
app.server.reboot.force: |
सर्वर जबरदस्ती पुनरारंभ हो रहा है...
APP_LOGOUT_SUCCESS: |
app.logout.success: |
✅ खाता सफलतापूर्वक अनधिकृत किया गया!
APP_NEW_VERSION_AVAILABLE: |
waha.new.version.available: |
🔄 **नया संस्करण उपलब्ध!**
वर्तमान संस्करण: **{{{currentVersion}}}**
@@ -170,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 [**🆕 चेंजलॉग**]({{{changelogUrl}}}) में क्या नया है देखें
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **WAHA का समर्थन करें!**
आप मुफ्त **WAHA Core** संस्करण का उपयोग कर रहे हैं। यदि आपको यह प्रोजेक्ट उपयोगी लगता है, तो कृपया इसके विकास का समर्थन करने पर विचार करें।
@@ -178,3 +186,51 @@ APP_CORE_VERSION_USED: |
आपका समर्थन ही एकमात्र तरीका है जिससे हम अपडेट, फिक्स और नई सुविधाओं पर काम करना जारी रख सकते हैं।
[**🎁 हमें समर्थन दें**]({{{supportUrl}}})
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **साझा संपर्क**
{{#contacts}}
🪪 नाम: **{{fullName}}**
{{#phoneNumbers}}
- 📞 फ़ोन: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **साझा स्थान**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **लाइव लोकेशन साझा की जा रही है**
रीयल-टाइम अपडेट देखने के लिए **WhatsApp** खोलें।
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **इस प्रकार का संदेश ChatWoot में समर्थित नहीं है।**
📱 इसे देखने के लिए कृपया **WhatsApp** खोलें।
विवरण: [{{{details.text}}}]({{{details.url}}})
@@ -1,7 +1,9 @@
# Indonesian translations for ChatWoot integration
APP_INBOX_CONTACT_NAME: 'Integrasi WhatsApp (WAHA)'
locale.name: '🇮🇩 Bahasa Indonesia (Indonesian)'
app.inbox.contact.name: 'Integrasi WhatsApp (WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
JOB_ERROR_REPORT: |
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 Mencoba lagi dalam {{attempts.nextDelay}} detik...
@@ -18,51 +20,52 @@ JOB_ERROR_REPORT: |
```
{{/error}}
JOB_SUCCEEDED_REPORT: |
job.report.succeeded: |
✅ **Berhasil**
Percobaan: {{{attempts.current}}} / {{{attempts.max}}}
Detail: [{{{details.text}}}]({{{details.url}}})
WHATSAPP_ERROR_SENDING_MESSAGE: Tidak dapat mengirim pesan di WhatsApp.
WHATSAPP_ERROR_RECEIVING_MESSAGE: Tidak dapat menerima pesan dari WhatsApp
WHATSAPP_ERROR_REMOVING_MESSAGE: Tidak dapat menghapus pesan dari WhatsApp
APP_SCHEDULED_JOB_ERROR: Tugas terjadwal gagal dijalankan
whatsapp.message.sending.error: Tidak dapat mengirim pesan di WhatsApp.
whatsapp.message.receiving.error: Tidak dapat menerima pesan dari WhatsApp
whatsapp.message.removing.error: Tidak dapat menghapus pesan dari WhatsApp
job.scheduled.error.header: Tugas terjadwal gagal dijalankan
MESSAGE_FROM_WHATSAPP: |
message.from.whatsapp: |
{{{text}}}
📱 *Dikirim dari WhatsApp*
MESSAGE_FROM_API: |
message.from.api: |
{{{text}}}
📱 *Dikirim melalui API*
MESSAGE_REMOVED_IN_WHATSAPP: |
message.removed.in.whatsapp: |
❌ *Pesan ini telah dihapus di WhatsApp*
MESSAGE_EDITED_IN_WHATSAPP: |
message.edited.in.whatsapp: |
{{{text}}}
✏️ *Diedit di WhatsApp*
WHATSAPP_GROUP_MESSAGE: |
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
WHATSAPP_REACTION_ADDED: |
whatsapp.reaction.added: |
*Bereaksi* {{{emoji}}}
WHATSAPP_REACTION_REMOVED: |
whatsapp.reaction.removed: |
*Reaksi dihapus*
WHATSAPP_CONTACT_GROUP_SUFFIX: 'Grup'
WHATSAPP_CONTACT_CHANNEL_SUFFIX: 'Saluran'
whatsapp.contact.group.suffix: 'Grup'
whatsapp.contact.channel.suffix: 'Saluran'
whatsapp.contact.broadcast.suffix: 'Daftar Siaran'
# Aka Stories
WHATSAPP_CONTACT_STATUS_NAME: 'Status'
whatsapp.contact.status.name: 'Status'
APP_CONNECTED_MESSAGE: |
app.connected.message: |
🔗 **TERHUBUNG: Sesi WhatsApp ke Kotak Masuk ChatWoot**
✅ Sesi WhatsApp **{{{name}}}** telah berhasil terhubung ke Kotak Masuk ChatWoot ini.
@@ -80,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
Mari kita mulai! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **TERPUTUS: Sesi WhatsApp dari Kotak Masuk ChatWoot**
❌ Sesi WhatsApp **{{{name}}}** telah terputus dari Kotak Masuk ChatWoot ini.
@@ -89,15 +92,15 @@ APP_DISCONNECTED_MESSAGE: |
Untuk menghubungkan kembali, silakan hubungi administrator Anda.
APP_UPDATED_MESSAGE: |
app.updated.message: |
🔄 **DIPERBARUI: Pengaturan Sesi WhatsApp**
✅ Pengaturan untuk sesi WhatsApp **{{{name}}}** telah diperbarui.
APP_COMMANDS_LIST: |
app.commands.list: |
Anda dapat mengelola **Integrasi WhatsApp** dengan mengirimkan salah satu perintah berikut:
🖥️ Sesi:
🖥️ **Sesi**:
1. **status** - dapatkan status sesi saat ini.
2. **restart** - mulai ulang sesi - ini akan mencoba menghubungkan kembali akun WhatsApp Anda.
3. **start** - mulai sesi.
@@ -107,42 +110,47 @@ APP_COMMANDS_LIST: |
7. **screenshot** - dapatkan tangkapan layar sesi (hanya tersedia di mesin **WEBJS**).
8. **help** - perintah yang tersedia.
{{#commands.server}}
⚙️ Server:
- **server status** - dapatkan informasi versi dan status server.
- **server reboot** - mulai ulang server dengan anggun.
- **server reboot force** - paksa mulai ulang server.
{{/commands.server}}
Kirim **perintah** (tidak peka huruf besar/kecil) atau **nomor perintah** ke percakapan ini.
APP_HELP_REMINDER: |
app.commands.server.disabled: ⛔ **Perintah server dinonaktifkan**.
Silakan hubungi administrator Anda.
Kirim **help** untuk melihat daftar perintah yang tersedia.
app.help.reminder: |
Kirim **help** untuk mendapatkan daftar perintah yang tersedia.
APP_SESSION_STATUS_CHANGE: |
app.session.status.change: |
{{{emoji}}} Sesi "{{{session}}}" beralih ke status {{{status}}}
APP_SESSION_CURRENT_STATUS: |
app.session.current.status: |
{{{emoji}}} Status sesi **{{{session}}}** adalah **{{{status}}}**
**Akun WhatsApp:**
📱 **{{{name}}} ({{{id}}})**
APP_DASHBOARD_LINK: |
Kunjungi [Dasbor WAHA]({{{url}}}) untuk detail lebih lanjut.
APP_SESSION_STATUS_WORKING: |
app.session.status.working: |
✅ Kotak Masuk ChatWoot ini terhubung ke **Akun WhatsApp** dan siap untuk mengirim dan menerima pesan!
📱 **{{{name}}} ({{{id}}})**
APP_SESSION_STATUS_ERROR: |
app.session.status.error: |
⚠️ **Kotak Masuk ChatWoot ini terhubung ke Akun WhatsApp tetapi TIDAK siap untuk mengirim dan menerima pesan!**
Silakan periksa status sesi dan coba mulai ulang atau keluar dan pindai kode QR lagi.
APP_SESSION_SCAN_QR_CODE: |
app.session.scan.qr.code: |
⚠️ **Pindai kode QR di ponsel Anda untuk menghubungkan Akun WhatsApp dengan Kotak Masuk ChatWoot ini!**
APP_SERVER_VERSION_AND_STATUS: |
app.server.version.and.status: |
**Versi:**
```
{{{version}}}
@@ -153,16 +161,16 @@ APP_SERVER_VERSION_AND_STATUS: |
{{{status}}}
```
APP_SERVER_REBOOT: |
app.server.reboot: |
Server sedang dimulai ulang dengan anggun...
APP_SERVER_REBOOT_FORCE: |
app.server.reboot.force: |
Server sedang dipaksa untuk dimulai ulang...
APP_LOGOUT_SUCCESS: |
app.logout.success: |
✅ Akun berhasil tidak diotorisasi!
APP_NEW_VERSION_AVAILABLE: |
waha.new.version.available: |
🔄 **Versi Baru Tersedia!**
Versi saat ini: **{{{currentVersion}}}**
@@ -170,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 Lihat apa yang baru di [**🆕 Changelog**]({{{changelogUrl}}})
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **Dukung WAHA!**
Anda menggunakan versi **WAHA Core** gratis. Jika Anda merasa proyek ini bermanfaat, harap pertimbangkan untuk mendukung pengembangannya.
@@ -178,3 +186,51 @@ APP_CORE_VERSION_USED: |
Dukungan Anda adalah satu-satunya cara kami dapat terus bekerja pada pembaruan, perbaikan, dan fitur baru.
[**🎁 Dukung Kami**]({{{supportUrl}}})
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **Kontak yang dibagikan**
{{#contacts}}
🪪 Nama: **{{fullName}}**
{{#phoneNumbers}}
- 📞 Telepon: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **Lokasi yang dibagikan**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **Berbagi Lokasi Langsung**
Buka **WhatsApp** untuk melihat pembaruan waktu nyata.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **Jenis pesan ini tidak didukung di ChatWoot.**
📱 Silakan buka **WhatsApp** untuk melihatnya.
Detail: [{{{details.text}}}]({{{details.url}}})
@@ -1,7 +1,9 @@
# Punjabi translations for ChatWoot integration
APP_INBOX_CONTACT_NAME: 'ਵਟਸਐਪ ਇੰਟੀਗ੍ਰੇਸ਼ਨ (WAHA)'
locale.name: '🇵🇰 ਪੰਜਾਬੀ (Punjabi)'
app.inbox.contact.name: 'ਵਟਸਐਪ ਇੰਟੀਗ੍ਰੇਸ਼ਨ (WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
JOB_ERROR_REPORT: |
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 {{attempts.nextDelay}} ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰ ਰਿਹਾ ਹੈ...
@@ -18,51 +20,52 @@ JOB_ERROR_REPORT: |
```
{{/error}}
JOB_SUCCEEDED_REPORT: |
job.report.succeeded: |
✅ **ਸਫਲ**
ਕੋਸ਼ਿਸ਼: {{{attempts.current}}} / {{{attempts.max}}}
ਵੇਰਵੇ: [{{{details.text}}}]({{{details.url}}})
WHATSAPP_ERROR_SENDING_MESSAGE: ਵਟਸਐਪ 'ਤੇ ਸੁਨੇਹਾ ਭੇਜ ਨਹੀਂ ਸਕਿਆ।
WHATSAPP_ERROR_RECEIVING_MESSAGE: ਵਟਸਐਪ ਤੋਂ ਸੁਨੇਹਾ ਪ੍ਰਾਪਤ ਨਹੀਂ ਕਰ ਸਕਿਆ
WHATSAPP_ERROR_REMOVING_MESSAGE: ਵਟਸਐਪ ਤੋਂ ਸੁਨੇਹਾ ਹਟਾ ਨਹੀਂ ਸਕਿਆ
APP_SCHEDULED_JOB_ERROR: ਨਿਰਧਾਰਿਤ ਕੰਮ ਚਲਾਉਣ ਵਿੱਚ ਅਸਫਲ ਰਿਹਾ
whatsapp.message.sending.error: ਵਟਸਐਪ 'ਤੇ ਸੁਨੇਹਾ ਭੇਜ ਨਹੀਂ ਸਕਿਆ।
whatsapp.message.receiving.error: ਵਟਸਐਪ ਤੋਂ ਸੁਨੇਹਾ ਪ੍ਰਾਪਤ ਨਹੀਂ ਕਰ ਸਕਿਆ
whatsapp.message.removing.error: ਵਟਸਐਪ ਤੋਂ ਸੁਨੇਹਾ ਹਟਾ ਨਹੀਂ ਸਕਿਆ
job.scheduled.error.header: ਨਿਰਧਾਰਿਤ ਕੰਮ ਚਲਾਉਣ ਵਿੱਚ ਅਸਫਲ ਰਿਹਾ
MESSAGE_FROM_WHATSAPP: |
message.from.whatsapp: |
{{{text}}}
📱 *ਵਟਸਐਪ ਤੋਂ ਭੇਜਿਆ ਗਿਆ*
MESSAGE_FROM_API: |
message.from.api: |
{{{text}}}
📱 *API ਰਾਹੀਂ ਭੇਜਿਆ ਗਿਆ*
MESSAGE_REMOVED_IN_WHATSAPP: |
message.removed.in.whatsapp: |
❌ *ਇਹ ਸੁਨੇਹਾ ਵਟਸਐਪ ਵਿੱਚ ਮਿਟਾ ਦਿੱਤਾ ਗਿਆ ਸੀ*
MESSAGE_EDITED_IN_WHATSAPP: |
message.edited.in.whatsapp: |
{{{text}}}
✏️ *ਵਟਸਐਪ ਵਿੱਚ ਸੰਪਾਦਿਤ ਕੀਤਾ ਗਿਆ*
WHATSAPP_GROUP_MESSAGE: |
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
WHATSAPP_REACTION_ADDED: |
whatsapp.reaction.added: |
*ਪ੍ਰਤੀਕਿਰਿਆ ਕੀਤੀ* {{{emoji}}}
WHATSAPP_REACTION_REMOVED: |
whatsapp.reaction.removed: |
*ਪ੍ਰਤੀਕਿਰਿਆ ਹਟਾਈ ਗਈ*
WHATSAPP_CONTACT_GROUP_SUFFIX: 'ਗਰੁੱਪ'
WHATSAPP_CONTACT_CHANNEL_SUFFIX: 'ਚੈਨਲ'
whatsapp.contact.group.suffix: 'ਗਰੁੱਪ'
whatsapp.contact.channel.suffix: 'ਚੈਨਲ'
whatsapp.contact.broadcast.suffix: 'ਪ੍ਰਸਾਰਣ ਸੂਚੀ'
# Aka Stories
WHATSAPP_CONTACT_STATUS_NAME: 'ਸਥਿਤੀ'
whatsapp.contact.status.name: 'ਸਥਿਤੀ'
APP_CONNECTED_MESSAGE: |
app.connected.message: |
🔗 **ਕਨੈਕਟ ਕੀਤਾ: ਵਟਸਐਪ ਸੈਸ਼ਨ ਤੋਂ ChatWoot ਇਨਬਾਕਸ**
✅ ਵਟਸਐਪ ਸੈਸ਼ਨ **{{{name}}}** ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਇਸ ChatWoot ਇਨਬਾਕਸ ਨਾਲ ਜੋੜਿਆ ਗਿਆ ਹੈ।
@@ -80,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
ਆਓ ਸ਼ੁਰੂ ਕਰੀਏ! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **ਡਿਸਕਨੈਕਟ ਕੀਤਾ: ਵਟਸਐਪ ਸੈਸ਼ਨ ਤੋਂ ChatWoot ਇਨਬਾਕਸ**
❌ ਵਟਸਐਪ ਸੈਸ਼ਨ **{{{name}}}** ਇਸ ChatWoot ਇਨਬਾਕਸ ਤੋਂ ਡਿਸਕਨੈਕਟ ਕੀਤਾ ਗਿਆ ਹੈ।
@@ -89,15 +92,15 @@ APP_DISCONNECTED_MESSAGE: |
ਮੁੜ ਕਨੈਕਟ ਕਰਨ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।
APP_UPDATED_MESSAGE: |
app.updated.message: |
🔄 **ਅਪਡੇਟ ਕੀਤਾ: ਵਟਸਐਪ ਸੈਸ਼ਨ ਸੈਟਿੰਗਾਂ**
✅ ਵਟਸਐਪ ਸੈਸ਼ਨ **{{{name}}}** ਲਈ ਸੈਟਿੰਗਾਂ ਅਪਡੇਟ ਕੀਤੀਆਂ ਗਈਆਂ ਹਨ।
APP_COMMANDS_LIST: |
app.commands.list: |
ਤੁਸੀਂ ਹੇਠਾਂ ਦਿੱਤੇ ਕਮਾਂਡਾਂ ਵਿੱਚੋਂ ਇੱਕ ਭੇਜ ਕੇ **ਵਟਸਐਪ ਇੰਟੀਗ੍ਰੇਸ਼ਨ** ਦਾ ਪ੍ਰਬੰਧਨ ਕਰ ਸਕਦੇ ਹੋ:
🖥️ ਸੈਸ਼ਨ:
🖥️ **ਸੈਸ਼ਨ**:
1. **status** - ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਸਥਿਤੀ ਪ੍ਰਾਪਤ ਕਰੋ।
2. **restart** - ਸੈਸ਼ਨ ਮੁੜ ਚਾਲੂ ਕਰੋ - ਇਹ ਤੁਹਾਡੇ ਵਟਸਐਪ ਖਾਤੇ ਨੂੰ ਮੁੜ ਕਨੈਕਟ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੇਗਾ।
3. **start** - ਸੈਸ਼ਨ ਸ਼ੁਰੂ ਕਰੋ।
@@ -107,42 +110,47 @@ APP_COMMANDS_LIST: |
7. **screenshot** - ਸੈਸ਼ਨ ਦਾ ਸਕ੍ਰੀਨਸ਼ਾਟ ਪ੍ਰਾਪਤ ਕਰੋ (ਸਿਰਫ **WEBJS** ਇੰਜਣ ਵਿੱਚ ਉਪਲਬਧ)।
8. **help** - ਉਪਲਬਧ ਕਮਾਂਡਾਂ।
{{#commands.server}}
⚙️ ਸਰਵਰ:
- **server status** - ਸਰਵਰ ਵਰਜਨ ਅਤੇ ਸਥਿਤੀ ਜਾਣਕਾਰੀ ਪ੍ਰਾਪਤ ਕਰੋ।
- **server reboot** - ਸਰਵਰ ਨੂੰ ਸੁਚਾਰੂ ਢੰਗ ਨਾਲ ਮੁੜ ਚਾਲੂ ਕਰੋ।
- **server reboot force** - ਸਰਵਰ ਨੂੰ ਜ਼ਬਰਦਸਤੀ ਮੁੜ ਚਾਲੂ ਕਰੋ।
{{/commands.server}}
ਇਸ ਗੱਲਬਾਤ ਵਿੱਚ **ਕਮਾਂਡ** (ਕੇਸ ਇਨਸੈਂਸਿਟਿਵ) ਜਾਂ **ਕਮਾਂਡ ਨੰਬਰ** ਭੇਜੋ।
APP_HELP_REMINDER: |
app.commands.server.disabled: ⛔ **ਸਰਵਰ ਕਮਾਂਡ ਅਯੋਗ ਹਨ**।
ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।
ਉਪਲਬਧ ਕਮਾਂਡਾਂ ਦੀ ਸੂਚੀ ਲਈ **help** ਭੇਜੋ।
app.help.reminder: |
ਉਪਲਬਧ ਕਮਾਂਡਾਂ ਦੀ ਸੂਚੀ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ **help** ਭੇਜੋ।
APP_SESSION_STATUS_CHANGE: |
app.session.status.change: |
{{{emoji}}} "{{{session}}}" ਸੈਸ਼ਨ {{{status}}} ਸਥਿਤੀ 'ਤੇ ਬਦਲ ਗਿਆ
APP_SESSION_CURRENT_STATUS: |
app.session.current.status: |
{{{emoji}}} **{{{session}}}** ਸੈਸ਼ਨ ਸਥਿਤੀ **{{{status}}}** ਵਿੱਚ ਹੈ
**ਵਟਸਐਪ ਖਾਤਾ:**
📱 **{{{name}}} ({{{id}}})**
APP_DASHBOARD_LINK: |
ਵਧੇਰੇ ਵੇਰਵਿਆਂ ਲਈ [WAHA ਡੈਸ਼ਬੋਰਡ]({{{url}}}) 'ਤੇ ਜਾਓ।
APP_SESSION_STATUS_WORKING: |
app.session.status.working: |
✅ ਇਹ ChatWoot ਇਨਬਾਕਸ **ਵਟਸਐਪ ਖਾਤੇ** ਨਾਲ ਜੁੜਿਆ ਹੋਇਆ ਹੈ ਅਤੇ ਸੁਨੇਹੇ ਭੇਜਣ ਅਤੇ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ ਤਿਆਰ ਹੈ!
📱 **{{{name}}} ({{{id}}})**
APP_SESSION_STATUS_ERROR: |
app.session.status.error: |
⚠️ **ਇਹ ChatWoot ਇਨਬਾਕਸ ਵਟਸਐਪ ਖਾਤੇ ਨਾਲ ਜੁੜਿਆ ਹੋਇਆ ਹੈ ਪਰ ਸੁਨੇਹੇ ਭੇਜਣ ਅਤੇ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ ਤਿਆਰ ਨਹੀਂ ਹੈ!**
ਕਿਰਪਾ ਕਰਕੇ ਸੈਸ਼ਨ ਸਥਿਤੀ ਦੀ ਜਾਂਚ ਕਰੋ ਅਤੇ ਇਸਨੂੰ ਮੁੜ ਚਾਲੂ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜਾਂ ਲੌਗਆਉਟ ਕਰੋ ਅਤੇ QR ਕੋਡ ਦੁਬਾਰਾ ਸਕੈਨ ਕਰੋ।
APP_SESSION_SCAN_QR_CODE: |
app.session.scan.qr.code: |
⚠️ **ਵਟਸਐਪ ਖਾਤੇ ਨੂੰ ਇਸ ChatWoot ਇਨਬਾਕਸ ਨਾਲ ਜੋੜਨ ਲਈ ਆਪਣੇ ਫੋਨ 'ਤੇ QR ਕੋਡ ਸਕੈਨ ਕਰੋ!**
APP_SERVER_VERSION_AND_STATUS: |
app.server.version.and.status: |
**ਵਰਜਨ:**
```
{{{version}}}
@@ -153,16 +161,16 @@ APP_SERVER_VERSION_AND_STATUS: |
{{{status}}}
```
APP_SERVER_REBOOT: |
app.server.reboot: |
ਸਰਵਰ ਸੁਚਾਰੂ ਢੰਗ ਨਾਲ ਮੁੜ ਚਾਲੂ ਹੋ ਰਿਹਾ ਹੈ...
APP_SERVER_REBOOT_FORCE: |
app.server.reboot.force: |
ਸਰਵਰ ਜ਼ਬਰਦਸਤੀ ਮੁੜ ਚਾਲੂ ਹੋ ਰਿਹਾ ਹੈ...
APP_LOGOUT_SUCCESS: |
app.logout.success: |
✅ ਖਾਤਾ ਸਫਲਤਾਪੂਰਵਕ ਅਣਅਧਿਕਾਰਤ ਕੀਤਾ ਗਿਆ!
APP_NEW_VERSION_AVAILABLE: |
waha.new.version.available: |
🔄 **ਨਵਾਂ ਵਰਜਨ ਉਪਲਬਧ!**
ਮੌਜੂਦਾ ਵਰਜਨ: **{{{currentVersion}}}**
@@ -170,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 [**🆕 ਬਦਲਾਅ ਲੌਗ**]({{{changelogUrl}}}) ਵਿੱਚ ਨਵਾਂ ਕੀ ਹੈ ਦੇਖੋ
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **WAHA ਦਾ ਸਮਰਥਨ ਕਰੋ!**
ਤੁਸੀਂ ਮੁਫਤ **WAHA Core** ਵਰਜਨ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੇ ਹੋ। ਜੇ ਤੁਹਾਨੂੰ ਇਹ ਪ੍ਰੋਜੈਕਟ ਲਾਭਦਾਇਕ ਲੱਗਦਾ ਹੈ, ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ ਇਸਦੇ ਵਿਕਾਸ ਦਾ ਸਮਰਥਨ ਕਰਨ 'ਤੇ ਵਿਚਾਰ ਕਰੋ।
@@ -178,3 +186,51 @@ APP_CORE_VERSION_USED: |
ਤੁਹਾਡਾ ਸਮਰਥਨ ਹੀ ਇੱਕੋ ਇੱਕ ਤਰੀਕਾ ਹੈ ਜਿਸ ਨਾਲ ਅਸੀਂ ਅਪਡੇਟਾਂ, ਫਿਕਸਾਂ, ਅਤੇ ਨਵੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ 'ਤੇ ਕੰਮ ਕਰਨਾ ਜਾਰੀ ਰੱਖ ਸਕਦੇ ਹਾਂ।
[**🎁 ਸਾਡਾ ਸਮਰਥਨ ਕਰੋ**]({{{supportUrl}}})
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **ਸਾਂਝੇ ਕੀਤੇ ਸੰਪਰਕ**
{{#contacts}}
🪪 ਨਾਮ: **{{fullName}}**
{{#phoneNumbers}}
- 📞 ਫ਼ੋਨ: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **ਸਾਂਝੀ ਸਥਿਤੀ**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **ਲਾਈਵ ਸਥਿਤੀ ਸਾਂਝੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ**
ਰੀਅਲ-ਟਾਈਮ ਅੱਪਡੇਟ ਦੇਖਣ ਲਈ **WhatsApp** ਖੋਲ੍ਹੋ।
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **ਇਹ ਸੁਨੇਹੇ ਦੀ ਕਿਸਮ ChatWoot ਵਿੱਚ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ।**
📱 ਇਸਨੂੰ ਦੇਖਣ ਲਈ ਕਿਰਪਾ ਕਰਕੇ **WhatsApp** ਖੋਲ੍ਹੋ।
ਵੇਰਵੇ: [{{{details.text}}}]({{{details.url}}})
@@ -1,7 +1,9 @@
# Portuguese translations for ChatWoot integration
APP_INBOX_CONTACT_NAME: 'Integração WhatsApp (WAHA)'
locale.name: '🇧🇷🇵🇹 Português (Portuguese)'
app.inbox.contact.name: 'Integração WhatsApp (WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
JOB_ERROR_REPORT: |
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 Tentando novamente em {{attempts.nextDelay}} segundos...
@@ -18,54 +20,55 @@ JOB_ERROR_REPORT: |
```
{{/error}}
JOB_SUCCEEDED_REPORT: |
job.report.succeeded: |
✅ **Sucesso**
Tentativa: {{{attempts.current}}} / {{{attempts.max}}}
Detalhes: [{{{details.text}}}]({{{details.url}}})
WHATSAPP_ERROR_SENDING_MESSAGE:
whatsapp.message.sending.error:
Não foi possível enviar uma mensagem no WhatsApp.
WHATSAPP_ERROR_RECEIVING_MESSAGE:
whatsapp.message.receiving.error:
Não foi possível receber uma mensagem do WhatsApp
WHATSAPP_ERROR_REMOVING_MESSAGE:
whatsapp.message.removing.error:
Não foi possível remover uma mensagem do WhatsApp
APP_SCHEDULED_JOB_ERROR: Falha ao executar tarefa agendada
job.scheduled.error.header: Falha ao executar tarefa agendada
MESSAGE_FROM_WHATSAPP: |
message.from.whatsapp: |
{{{text}}}
📱 *Enviado do WhatsApp*
MESSAGE_FROM_API: |
message.from.api: |
{{{text}}}
📱 *Enviado via API*
MESSAGE_REMOVED_IN_WHATSAPP: |
message.removed.in.whatsapp: |
❌ *Esta mensagem foi excluída no WhatsApp*
MESSAGE_EDITED_IN_WHATSAPP: |
message.edited.in.whatsapp: |
{{{text}}}
✏️ *Editado no WhatsApp*
WHATSAPP_GROUP_MESSAGE: |
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
WHATSAPP_REACTION_ADDED: |
whatsapp.reaction.added: |
*Reagiu com* {{{emoji}}}
WHATSAPP_REACTION_REMOVED: |
whatsapp.reaction.removed: |
*Reação removida*
WHATSAPP_CONTACT_GROUP_SUFFIX: 'Grupo'
WHATSAPP_CONTACT_CHANNEL_SUFFIX: 'Canal'
whatsapp.contact.group.suffix: 'Grupo'
whatsapp.contact.channel.suffix: 'Canal'
whatsapp.contact.broadcast.suffix: 'Lista de transmissão'
# Aka Stories
WHATSAPP_CONTACT_STATUS_NAME: 'Status'
whatsapp.contact.status.name: 'Status'
APP_CONNECTED_MESSAGE: |
app.connected.message: |
🔗 **CONECTADO: Sessão do WhatsApp para Caixa de Entrada do ChatWoot**
✅ A sessão do WhatsApp **{{{name}}}** foi conectada com sucesso a esta Caixa de Entrada do ChatWoot.
@@ -83,7 +86,7 @@ APP_CONNECTED_MESSAGE: |
Vamos começar! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **DESCONECTADO: Sessão do WhatsApp da Caixa de Entrada do ChatWoot**
❌ A sessão do WhatsApp **{{{name}}}** foi desconectada desta Caixa de Entrada do ChatWoot.
@@ -92,15 +95,15 @@ APP_DISCONNECTED_MESSAGE: |
Para reconectar, entre em contato com seu administrador.
APP_UPDATED_MESSAGE: |
app.updated.message: |
🔄 **ATUALIZADO: Configurações da Sessão do WhatsApp**
✅ As configurações para a sessão do WhatsApp **{{{name}}}** foram atualizadas.
APP_COMMANDS_LIST: |
app.commands.list: |
Você pode gerenciar a **Integração WhatsApp** enviando um dos seguintes comandos:
🖥️ Sessões:
🖥️ **Sessão**:
1. **status** - obter o status atual da sessão.
2. **restart** - reiniciar a sessão - isso tentará reconectar sua conta do WhatsApp.
3. **start** - iniciar a sessão.
@@ -110,42 +113,47 @@ APP_COMMANDS_LIST: |
7. **screenshot** - obter uma captura de tela da sessão (disponível apenas no motor **WEBJS**).
8. **help** - comandos disponíveis.
{{#commands.server}}
⚙️ Servidor:
- **server status** - obter informações sobre a versão e status do servidor.
- **server reboot** - reiniciar o servidor suavemente.
- **server reboot force** - forçar a reinicialização do servidor.
{{/commands.server}}
Envie um **comando** (não sensível a maiúsculas/minúsculas) ou um **número de comando** para esta conversa.
APP_HELP_REMINDER: |
app.commands.server.disabled: ⛔ **Os comandos do servidor estão desativados**.
Por favor, contate o seu administrador.
Envie **help** para obter a lista de comandos disponíveis.
APP_SESSION_STATUS_CHANGE: |
app.help.reminder: |
Envie **help** para obter a lista de comandos disponíveis.
app.session.status.change: |
{{{emoji}}} A sessão "{{{session}}}" mudou para o status {{{status}}}
APP_SESSION_CURRENT_STATUS: |
app.session.current.status: |
{{{emoji}}} O status da sessão **{{{session}}}** é **{{{status}}}**
**Conta do WhatsApp:**
📱 **{{{name}}} ({{{id}}})**
APP_DASHBOARD_LINK: |
Visite o [Painel WAHA]({{{url}}}) para mais detalhes.
APP_SESSION_STATUS_WORKING: |
app.session.status.working: |
✅ Esta Caixa de Entrada do ChatWoot está vinculada à **Conta do WhatsApp** e pronta para enviar e receber mensagens!
📱 **{{{name}}} ({{{id}}})**
APP_SESSION_STATUS_ERROR: |
app.session.status.error: |
⚠️ **Esta Caixa de Entrada do ChatWoot está vinculada à Conta do WhatsApp, mas NÃO está pronta para enviar e receber mensagens!**
Por favor, verifique o status da sessão e tente reiniciá-la ou saia e escaneie o código QR novamente.
APP_SESSION_SCAN_QR_CODE: |
app.session.scan.qr.code: |
⚠️ **Escaneie o código QR no seu telefone para vincular a Conta do WhatsApp a esta Caixa de Entrada do ChatWoot!**
APP_SERVER_VERSION_AND_STATUS: |
app.server.version.and.status: |
**Versão:**
```
{{{version}}}
@@ -156,16 +164,16 @@ APP_SERVER_VERSION_AND_STATUS: |
{{{status}}}
```
APP_SERVER_REBOOT: |
app.server.reboot: |
O servidor está reiniciando suavemente...
APP_SERVER_REBOOT_FORCE: |
app.server.reboot.force: |
O servidor está reiniciando forçadamente...
APP_LOGOUT_SUCCESS: |
app.logout.success: |
✅ Conta desautorizada com sucesso!
APP_NEW_VERSION_AVAILABLE: |
waha.new.version.available: |
🔄 **Nova Versão Disponível!**
Versão atual: **{{{currentVersion}}}**
@@ -173,7 +181,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 Confira o que há de novo no [**🆕 Registro de Alterações**]({{{changelogUrl}}})
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **Apoie o WAHA!**
Você está usando a versão gratuita **WAHA Core**. Se você acha este projeto útil, considere apoiar seu desenvolvimento.
@@ -181,3 +189,51 @@ APP_CORE_VERSION_USED: |
Seu apoio é a única maneira de continuarmos trabalhando em atualizações, correções e novos recursos.
[**🎁 Apoie-nos**]({{{supportUrl}}})
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **Contato(s) compartilhado(s)**
{{#contacts}}
🪪 Nome: **{{fullName}}**
{{#phoneNumbers}}
- 📞 Telefone: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **Localização compartilhada**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **Compartilhando localização ao vivo**
Abra o **WhatsApp** para ver as atualizações em tempo real.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **Esse tipo de mensagem não é suportado no ChatWoot.**
📱 Por favor, abra o **WhatsApp** para visualizá-la.
Detalhes: [{{{details.text}}}]({{{details.url}}})
@@ -1,7 +1,9 @@
# Russian translations for ChatWoot integration
APP_INBOX_CONTACT_NAME: 'WhatsApp Интеграция (WAHA)'
locale.name: '🇷🇺 Русский (Russian)'
app.inbox.contact.name: 'WhatsApp Интеграция (WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
JOB_ERROR_REPORT: |
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 Повторная попытка через {{attempts.nextDelay}} секунд...
@@ -18,51 +20,52 @@ JOB_ERROR_REPORT: |
```
{{/error}}
JOB_SUCCEEDED_REPORT: |
job.report.succeeded: |
✅ **Успешно**
Попытка: {{{attempts.current}}} / {{{attempts.max}}}
Подробности: [{{{details.text}}}]({{{details.url}}})
WHATSAPP_ERROR_SENDING_MESSAGE: Не удалось отправить сообщение в WhatsApp.
WHATSAPP_ERROR_RECEIVING_MESSAGE: Не удалось получить сообщение из WhatsApp
WHATSAPP_ERROR_REMOVING_MESSAGE: Не удалось удалить сообщение из WhatsApp
APP_SCHEDULED_JOB_ERROR: Запланированная задача не выполнена
whatsapp.message.sending.error: Не удалось отправить сообщение в WhatsApp.
whatsapp.message.receiving.error: Не удалось получить сообщение из WhatsApp
whatsapp.message.removing.error: Не удалось удалить сообщение из WhatsApp
job.scheduled.error.header: Запланированная задача не выполнена
MESSAGE_FROM_WHATSAPP: |
message.from.whatsapp: |
{{{text}}}
📱 *Отправлено из WhatsApp*
MESSAGE_FROM_API: |
message.from.api: |
{{{text}}}
📱 *Отправлено через API*
MESSAGE_REMOVED_IN_WHATSAPP: |
message.removed.in.whatsapp: |
❌ *Это сообщение было удалено в WhatsApp*
MESSAGE_EDITED_IN_WHATSAPP: |
message.edited.in.whatsapp: |
{{{text}}}
✏️ *Отредактировано в WhatsApp*
WHATSAPP_GROUP_MESSAGE: |
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
WHATSAPP_REACTION_ADDED: |
whatsapp.reaction.added: |
*Реакция* {{{emoji}}}
WHATSAPP_REACTION_REMOVED: |
whatsapp.reaction.removed: |
*Реакция удалена*
WHATSAPP_CONTACT_GROUP_SUFFIX: 'Группа'
WHATSAPP_CONTACT_CHANNEL_SUFFIX: 'Канал'
whatsapp.contact.group.suffix: 'Группа'
whatsapp.contact.channel.suffix: 'Канал'
whatsapp.contact.broadcast.suffix: 'Список рассылки'
# Aka Stories
WHATSAPP_CONTACT_STATUS_NAME: 'Статус'
whatsapp.contact.status.name: 'Статус'
APP_CONNECTED_MESSAGE: |
app.connected.message: |
🔗 **ПОДКЛЮЧЕНО: Сессия WhatsApp к ChatWoot Inbox**
✅ Сессия WhatsApp **{{{name}}}** была успешно подключена к этому ChatWoot Inbox.
@@ -80,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
Давайте начнем! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **ОТКЛЮЧЕНО: Сессия WhatsApp от ChatWoot Inbox**
❌ Сессия WhatsApp **{{{name}}}** была отключена от этого ChatWoot Inbox.
@@ -89,15 +92,15 @@ APP_DISCONNECTED_MESSAGE: |
Для переподключения, пожалуйста, обратитесь к вашему администратору.
APP_UPDATED_MESSAGE: |
app.updated.message: |
🔄 **ОБНОВЛЕНО: Настройки сессии WhatsApp**
✅ Настройки для сессии WhatsApp **{{{name}}}** были обновлены.
APP_COMMANDS_LIST: |
app.commands.list: |
Вы можете управлять **WhatsApp Интеграцией**, отправив одну из следующих команд:
🖥️ Сессии:
🖥️ **Сессия**:
1. **status** - получить текущий статус сессии.
2. **restart** - перезапустить сессию - это попытается переподключить ваш аккаунт WhatsApp.
3. **start** - запустить сессию.
@@ -107,42 +110,47 @@ APP_COMMANDS_LIST: |
7. **screenshot** - получить скриншот сессии (доступно только в движке **WEBJS**).
8. **help** - доступные команды.
{{#commands.server}}
⚙️ Сервер:
- **server status** - получить информацию о версии и статусе сервера.
- **server reboot** - корректно перезагрузить сервер.
- **server reboot force** - принудительно перезагрузить сервер.
{{/commands.server}}
Отправьте **команду** (без учета регистра) или **номер команды** в этот разговор.
APP_HELP_REMINDER: |
app.commands.server.disabled: ⛔ **Команды сервера отключены**.
Пожалуйста, свяжитесь с вашим администратором.
Отправьте **help**, чтобы получить список доступных команд.
APP_SESSION_STATUS_CHANGE: |
app.help.reminder: |
Отправьте **help**, чтобы получить список доступных команд.
app.session.status.change: |
{{{emoji}}} Сессия "{{{session}}}" переключилась в статус {{{status}}}
APP_SESSION_CURRENT_STATUS: |
app.session.current.status: |
{{{emoji}}} Статус сессии **{{{session}}}** - **{{{status}}}**
**Аккаунт WhatsApp:**
📱 **{{{name}}} ({{{id}}})**
APP_DASHBOARD_LINK: |
Посетите [WAHA Dashboard]({{{url}}}) для получения дополнительной информации.
APP_SESSION_STATUS_WORKING: |
app.session.status.working: |
✅ Этот ChatWoot Inbox связан с **аккаунтом WhatsApp** и готов отправлять и получать сообщения!
📱 **{{{name}}} ({{{id}}})**
APP_SESSION_STATUS_ERROR: |
app.session.status.error: |
⚠️ **Этот ChatWoot Inbox связан с аккаунтом WhatsApp, но НЕ готов отправлять и получать сообщения!**
Пожалуйста, проверьте статус сессии и попробуйте перезапустить её или выйдите и отсканируйте QR-код снова.
APP_SESSION_SCAN_QR_CODE: |
app.session.scan.qr.code: |
⚠️ **Отсканируйте QR-код на вашем телефоне, чтобы связать аккаунт WhatsApp с этим ChatWoot Inbox!**
APP_SERVER_VERSION_AND_STATUS: |
app.server.version.and.status: |
**Версия:**
```
{{{version}}}
@@ -153,16 +161,16 @@ APP_SERVER_VERSION_AND_STATUS: |
{{{status}}}
```
APP_SERVER_REBOOT: |
app.server.reboot: |
Сервер корректно перезагружается...
APP_SERVER_REBOOT_FORCE: |
app.server.reboot.force: |
Сервер принудительно перезагружается...
APP_LOGOUT_SUCCESS: |
app.logout.success: |
✅ Аккаунт успешно деавторизован!
APP_NEW_VERSION_AVAILABLE: |
waha.new.version.available: |
🔄 **Доступна новая версия!**
Текущая версия: **{{{currentVersion}}}**
@@ -170,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 Узнайте, что нового в [**🆕 Списке изменений**]({{{changelogUrl}}})
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **Поддержите WAHA!**
Вы используете бесплатную версию **WAHA Core**. Если вы находите этот проект полезным, пожалуйста, рассмотрите возможность поддержать его разработку.
@@ -178,3 +186,51 @@ APP_CORE_VERSION_USED: |
Ваша поддержка - единственный способ, которым мы можем продолжать работать над обновлениями, исправлениями и новыми функциями.
[**🎁 Поддержать нас**]({{{supportUrl}}})
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **Переданные контакты**
{{#contacts}}
🪪 Имя: **{{fullName}}**
{{#phoneNumbers}}
- 📞 Телефон: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **Переданное местоположение**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **Делится геопозицией в реальном времени**
Откройте **WhatsApp**, чтобы увидеть обновления в реальном времени.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **Этот тип сообщения не поддерживается в ChatWoot.**
📱 Пожалуйста, откройте **WhatsApp**, чтобы просмотреть.
Подробности: [{{{details.text}}}]({{{details.url}}})
@@ -1,7 +1,9 @@
# Turkish translations for ChatWoot integration
APP_INBOX_CONTACT_NAME: 'WhatsApp Entegrasyonu (WAHA)'
locale.name: '🇹🇷 Türkçe (Turkish)'
app.inbox.contact.name: 'WhatsApp Entegrasyonu (WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
JOB_ERROR_REPORT: |
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 {{attempts.nextDelay}} saniye içinde tekrar deneniyor...
@@ -18,51 +20,52 @@ JOB_ERROR_REPORT: |
```
{{/error}}
JOB_SUCCEEDED_REPORT: |
job.report.succeeded: |
✅ **Başarılı**
Deneme: {{{attempts.current}}} / {{{attempts.max}}}
Detaylar: [{{{details.text}}}]({{{details.url}}})
WHATSAPP_ERROR_SENDING_MESSAGE: WhatsApp'ta mesaj gönderilemedi.
WHATSAPP_ERROR_RECEIVING_MESSAGE: WhatsApp'tan mesaj alınamadı
WHATSAPP_ERROR_REMOVING_MESSAGE: WhatsApp'tan mesaj kaldırılamadı
APP_SCHEDULED_JOB_ERROR: Zamanlanmış görev yürütülemedi
whatsapp.message.sending.error: WhatsApp'ta mesaj gönderilemedi.
whatsapp.message.receiving.error: WhatsApp'tan mesaj alınamadı
whatsapp.message.removing.error: WhatsApp'tan mesaj kaldırılamadı
job.scheduled.error.header: Zamanlanmış görev yürütülemedi
MESSAGE_FROM_WHATSAPP: |
message.from.whatsapp: |
{{{text}}}
📱 *WhatsApp'tan gönderildi*
MESSAGE_FROM_API: |
message.from.api: |
{{{text}}}
📱 *API aracılığıyla gönderildi*
MESSAGE_REMOVED_IN_WHATSAPP: |
message.removed.in.whatsapp: |
❌ *Bu mesaj WhatsApp'ta silindi*
MESSAGE_EDITED_IN_WHATSAPP: |
message.edited.in.whatsapp: |
{{{text}}}
✏️ *WhatsApp'ta düzenlendi*
WHATSAPP_GROUP_MESSAGE: |
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
WHATSAPP_REACTION_ADDED: |
whatsapp.reaction.added: |
*Tepki verdi* {{{emoji}}}
WHATSAPP_REACTION_REMOVED: |
whatsapp.reaction.removed: |
*Tepki kaldırıldı*
WHATSAPP_CONTACT_GROUP_SUFFIX: 'Grup'
WHATSAPP_CONTACT_CHANNEL_SUFFIX: 'Kanal'
whatsapp.contact.group.suffix: 'Grup'
whatsapp.contact.channel.suffix: 'Kanal'
whatsapp.contact.broadcast.suffix: 'Yayın Listesi'
# Aka Stories
WHATSAPP_CONTACT_STATUS_NAME: 'Durum'
whatsapp.contact.status.name: 'Durum'
APP_CONNECTED_MESSAGE: |
app.connected.message: |
🔗 **BAĞLANDI: WhatsApp Oturumu ChatWoot Gelen Kutusuna**
✅ WhatsApp oturumu **{{{name}}}** bu ChatWoot Gelen Kutusuna başarıyla bağlandı.
@@ -80,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
Hadi başlayalım! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **BAĞLANTI KESİLDİ: WhatsApp Oturumu ChatWoot Gelen Kutusundan**
❌ WhatsApp oturumu **{{{name}}}** bu ChatWoot Gelen Kutusundan bağlantısı kesildi.
@@ -89,15 +92,15 @@ APP_DISCONNECTED_MESSAGE: |
Yeniden bağlanmak için, lütfen yöneticinizle iletişime geçin.
APP_UPDATED_MESSAGE: |
app.updated.message: |
🔄 **GÜNCELLENDİ: WhatsApp Oturum Ayarları**
✅ WhatsApp oturumu **{{{name}}}** için ayarlar güncellendi.
APP_COMMANDS_LIST: |
app.commands.list: |
Aşağıdaki komutlardan birini göndererek **WhatsApp Entegrasyonu**nu yönetebilirsiniz:
🖥️ Oturumlar:
🖥️ **Oturum**:
1. **status** - mevcut oturum durumunu alın.
2. **restart** - oturumu yeniden başlatın - bu, WhatsApp hesabınızı yeniden bağlamaya çalışacaktır.
3. **start** - oturumu başlatın.
@@ -107,42 +110,47 @@ APP_COMMANDS_LIST: |
7. **screenshot** - oturumun ekran görüntüsünü alın (yalnızca **WEBJS** motorunda kullanılabilir).
8. **help** - kullanılabilir komutlar.
{{#commands.server}}
⚙️ Sunucu:
- **server status** - sunucu sürümü ve durum bilgilerini alın.
- **server reboot** - sunucuyu nazikçe yeniden başlatın.
- **server reboot force** - sunucuyu zorla yeniden başlatın.
{{/commands.server}}
Bu konuşmaya bir **komut** (büyük/küçük harf duyarsız) veya **komut numarası** gönderin.
APP_HELP_REMINDER: |
app.commands.server.disabled: ⛔ **Sunucu komutları devre dışı**.
Lütfen yöneticinizle iletişime geçin.
Kullanılabilir komutların listesini almak için **help** gönderin.
APP_SESSION_STATUS_CHANGE: |
app.help.reminder: |
Kullanılabilir komutların listesini almak için **help** gönderin.
app.session.status.change: |
{{{emoji}}} "{{{session}}}" oturumu {{{status}}} durumuna geçti
APP_SESSION_CURRENT_STATUS: |
app.session.current.status: |
{{{emoji}}} **{{{session}}}** oturum durumu **{{{status}}}** içinde
**WhatsApp Hesabı:**
📱 **{{{name}}} ({{{id}}})**
APP_DASHBOARD_LINK: |
Daha fazla ayrıntı için [WAHA Kontrol Paneli]({{{url}}}) ziyaret edin.
APP_SESSION_STATUS_WORKING: |
app.session.status.working: |
✅ Bu ChatWoot Gelen Kutusu **WhatsApp Hesabına** bağlı ve mesaj gönderip almaya hazır!
📱 **{{{name}}} ({{{id}}})**
APP_SESSION_STATUS_ERROR: |
app.session.status.error: |
⚠️ **Bu ChatWoot Gelen Kutusu WhatsApp Hesabına bağlı ancak mesaj gönderip almaya hazır DEĞİL!**
Lütfen oturum durumunu kontrol edin ve yeniden başlatmayı deneyin veya çıkış yapın ve QR kodu tekrar tarayın.
APP_SESSION_SCAN_QR_CODE: |
app.session.scan.qr.code: |
⚠️ **WhatsApp Hesabını bu ChatWoot Gelen Kutusuna bağlamak için telefonunuzda QR kodu tarayın!**
APP_SERVER_VERSION_AND_STATUS: |
app.server.version.and.status: |
**Sürüm:**
```
{{{version}}}
@@ -153,16 +161,16 @@ APP_SERVER_VERSION_AND_STATUS: |
{{{status}}}
```
APP_SERVER_REBOOT: |
app.server.reboot: |
Sunucu nazikçe yeniden başlatılıyor...
APP_SERVER_REBOOT_FORCE: |
app.server.reboot.force: |
Sunucu zorla yeniden başlatılıyor...
APP_LOGOUT_SUCCESS: |
app.logout.success: |
✅ Hesap başarıyla yetkilendirilmedi!
APP_NEW_VERSION_AVAILABLE: |
waha.new.version.available: |
🔄 **Yeni Sürüm Mevcut!**
Mevcut sürüm: **{{{currentVersion}}}**
@@ -170,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 [**🆕 Değişiklik Günlüğü**]({{{changelogUrl}}}) içinde yenilikleri kontrol edin
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **WAHA'yı Destekleyin!**
Ücretsiz **WAHA Core** sürümünü kullanıyorsunuz. Bu projeyi faydalı buluyorsanız, lütfen geliştirilmesini desteklemeyi düşünün.
@@ -178,3 +186,51 @@ APP_CORE_VERSION_USED: |
Desteğiniz, güncellemeler, düzeltmeler ve yeni özellikler üzerinde çalışmaya devam edebilmemizin tek yoludur.
[**🎁 Bizi Destekleyin**]({{{supportUrl}}})
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **Paylaşılan Kişi(ler)**
{{#contacts}}
🪪 İsim: **{{fullName}}**
{{#phoneNumbers}}
- 📞 Telefon: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **Paylaşılan Konum**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **Canlı Konum Paylaşılıyor**
Gerçek zamanlı güncellemeleri görmek için **WhatsApp’ı** açın.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **Bu mesaj türü ChatWoot’ta desteklenmiyor.**
📱 Görüntülemek için lütfen **WhatsApp’ı** açın.
Ayrıntılar: [{{{details.text}}}]({{{details.url}}})
@@ -1,7 +1,9 @@
# Ukrainian translations for ChatWoot integration
APP_INBOX_CONTACT_NAME: 'Інтеграція WhatsApp (WAHA)'
locale.name: '🇺🇦 Українська (Ukrainian)'
app.inbox.contact.name: 'Інтеграція WhatsApp (WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
JOB_ERROR_REPORT: |
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 Повторна спроба через {{attempts.nextDelay}} секунд...
@@ -18,51 +20,52 @@ JOB_ERROR_REPORT: |
```
{{/error}}
JOB_SUCCEEDED_REPORT: |
job.report.succeeded: |
✅ **Успішно**
Спроба: {{{attempts.current}}} / {{{attempts.max}}}
Деталі: [{{{details.text}}}]({{{details.url}}})
WHATSAPP_ERROR_SENDING_MESSAGE: Не вдалося надіслати повідомлення у WhatsApp.
WHATSAPP_ERROR_RECEIVING_MESSAGE: Не вдалося отримати повідомлення з WhatsApp
WHATSAPP_ERROR_REMOVING_MESSAGE: Не вдалося видалити повідомлення з WhatsApp
APP_SCHEDULED_JOB_ERROR: Заплановане завдання не вдалося виконати
whatsapp.message.sending.error: Не вдалося надіслати повідомлення у WhatsApp.
whatsapp.message.receiving.error: Не вдалося отримати повідомлення з WhatsApp
whatsapp.message.removing.error: Не вдалося видалити повідомлення з WhatsApp
job.scheduled.error.header: Заплановане завдання не вдалося виконати
MESSAGE_FROM_WHATSAPP: |
message.from.whatsapp: |
{{{text}}}
📱 *Надіслано з WhatsApp*
MESSAGE_FROM_API: |
message.from.api: |
{{{text}}}
📱 *Надіслано через API*
MESSAGE_REMOVED_IN_WHATSAPP: |
message.removed.in.whatsapp: |
❌ *Це повідомлення було видалено у WhatsApp*
MESSAGE_EDITED_IN_WHATSAPP: |
message.edited.in.whatsapp: |
{{{text}}}
✏️ *Відредаговано у WhatsApp*
WHATSAPP_GROUP_MESSAGE: |
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
WHATSAPP_REACTION_ADDED: |
whatsapp.reaction.added: |
*Реакція* {{{emoji}}}
WHATSAPP_REACTION_REMOVED: |
whatsapp.reaction.removed: |
*Реакцію видалено*
WHATSAPP_CONTACT_GROUP_SUFFIX: 'Група'
WHATSAPP_CONTACT_CHANNEL_SUFFIX: 'Канал'
whatsapp.contact.group.suffix: 'Група'
whatsapp.contact.channel.suffix: 'Канал'
whatsapp.contact.broadcast.suffix: 'Список розсилки'
# Aka Stories
WHATSAPP_CONTACT_STATUS_NAME: 'Статус'
whatsapp.contact.status.name: 'Статус'
APP_CONNECTED_MESSAGE: |
app.connected.message: |
🔗 **ПІДКЛЮЧЕНО: Сесія WhatsApp до Вхідних ChatWoot**
✅ Сесія WhatsApp **{{{name}}}** була успішно підключена до цих Вхідних ChatWoot.
@@ -80,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
Давайте почнемо! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **ВІДКЛЮЧЕНО: Сесія WhatsApp від Вхідних ChatWoot**
❌ Сесія WhatsApp **{{{name}}}** була відключена від цих Вхідних ChatWoot.
@@ -89,15 +92,15 @@ APP_DISCONNECTED_MESSAGE: |
Щоб повторно підключитися, зверніться до свого адміністратора.
APP_UPDATED_MESSAGE: |
app.updated.message: |
🔄 **ОНОВЛЕНО: Налаштування сесії WhatsApp**
✅ Налаштування для сесії WhatsApp **{{{name}}}** були оновлені.
APP_COMMANDS_LIST: |
app.commands.list: |
Ви можете керувати **Інтеграцією WhatsApp**, надіславши одну з наступних команд:
🖥️ Сесії:
🖥️ **Сесія**:
1. **status** - отримати поточний статус сесії.
2. **restart** - перезапустити сесію - це спробує повторно підключити ваш обліковий запис WhatsApp.
3. **start** - запустити сесію.
@@ -107,42 +110,47 @@ APP_COMMANDS_LIST: |
7. **screenshot** - отримати знімок екрану сесії (доступно лише в двигуні **WEBJS**).
8. **help** - доступні команди.
{{#commands.server}}
⚙️ Сервер:
- **server status** - отримати інформацію про версію та статус сервера.
- **server reboot** - перезавантажити сервер плавно.
- **server reboot force** - примусово перезавантажити сервер.
{{/commands.server}}
Надішліть **команду** (без урахування регістру) або **номер команди** у цю розмову.
APP_HELP_REMINDER: |
app.commands.server.disabled: ⛔ **Команди сервера вимкнені**.
Будь ласка, зв’яжіться з вашим адміністратором.
Надішліть **help**, щоб отримати список доступних команд.
APP_SESSION_STATUS_CHANGE: |
app.help.reminder: |
Надішліть **help**, щоб отримати список доступних команд.
app.session.status.change: |
{{{emoji}}} Сесія "{{{session}}}" перейшла у статус {{{status}}}
APP_SESSION_CURRENT_STATUS: |
app.session.current.status: |
{{{emoji}}} Статус сесії **{{{session}}}** - **{{{status}}}**
**Обліковий запис WhatsApp:**
📱 **{{{name}}} ({{{id}}})**
APP_DASHBOARD_LINK: |
Відвідайте [Панель керування WAHA]({{{url}}}) для отримання додаткової інформації.
APP_SESSION_STATUS_WORKING: |
app.session.status.working: |
✅ Ці Вхідні ChatWoot пов'язані з **Обліковим записом WhatsApp** і готові надсилати та отримувати повідомлення!
📱 **{{{name}}} ({{{id}}})**
APP_SESSION_STATUS_ERROR: |
app.session.status.error: |
⚠️ **Ці Вхідні ChatWoot пов'язані з Обліковим записом WhatsApp, але НЕ готові надсилати та отримувати повідомлення!**
Будь ласка, перевірте статус сесії та спробуйте перезапустити її або вийдіть і відскануйте QR-код знову.
APP_SESSION_SCAN_QR_CODE: |
app.session.scan.qr.code: |
⚠️ **Відскануйте QR-код на вашому телефоні, щоб пов'язати Обліковий запис WhatsApp з цими Вхідними ChatWoot!**
APP_SERVER_VERSION_AND_STATUS: |
app.server.version.and.status: |
**Версія:**
```
{{{version}}}
@@ -153,16 +161,16 @@ APP_SERVER_VERSION_AND_STATUS: |
{{{status}}}
```
APP_SERVER_REBOOT: |
app.server.reboot: |
Сервер плавно перезавантажується...
APP_SERVER_REBOOT_FORCE: |
app.server.reboot.force: |
Сервер примусово перезавантажується...
APP_LOGOUT_SUCCESS: |
app.logout.success: |
✅ Обліковий запис успішно деавторизовано!
APP_NEW_VERSION_AVAILABLE: |
waha.new.version.available: |
🔄 **Доступна нова версія!**
Поточна версія: **{{{currentVersion}}}**
@@ -170,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 Перевірте, що нового у [**🆕 Журналі змін**]({{{changelogUrl}}})
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **Підтримайте WAHA!**
Ви використовуєте безкоштовну версію **WAHA Core**. Якщо ви вважаєте цей проект корисним, будь ласка, розгляньте можливість підтримати його розробку.
@@ -178,3 +186,51 @@ APP_CORE_VERSION_USED: |
Ваша підтримка - єдиний спосіб, яким ми можемо продовжувати працювати над оновленнями, виправленнями та новими функціями.
[**🎁 Підтримайте нас**]({{{supportUrl}}})
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **Надісланий контакт(и)**
{{#contacts}}
🪪 Ім’я: **{{fullName}}**
{{#phoneNumbers}}
- 📞 Телефон: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **Надіслана локація**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **Надсилання геолокації в реальному часі**
Відкрийте **WhatsApp**, щоб побачити оновлення в реальному часі.
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **Цей тип повідомлення не підтримується в ChatWoot.**
📱 Будь ласка, відкрийте **WhatsApp**, щоб переглянути.
Подробиці: [{{{details.text}}}]({{{details.url}}})
+236
View File
@@ -0,0 +1,236 @@
# Urdu translations for ChatWoot integration
locale.name: '🇵🇰 اردو (Urdu)'
app.inbox.contact.name: 'واٹس ایپ انٹیگریشن (WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 {{attempts.nextDelay}} سیکنڈ میں دوبارہ کوشش کر رہا ہے...
{{/attempts.nextDelay}}
{{^attempts.nextDelay}}
❌ مزید کوششیں نہیں بچی ہیں۔ براہ کرم بعد میں دوبارہ کوشش کریں۔
{{/attempts.nextDelay}}
کوشش: {{{attempts.current}}} / {{{attempts.max}}}
تفصیلات: [{{{details.text}}}]({{{details.url}}})
{{#error}}
```
{{{error}}}
```
{{/error}}
job.report.succeeded: |
✅ **کامیاب ہوا**
کوشش: {{{attempts.current}}} / {{{attempts.max}}}
تفصیلات: [{{{details.text}}}]({{{details.url}}})
whatsapp.message.sending.error: واٹس ایپ پر پیغام نہیں بھیج سکا۔
whatsapp.message.receiving.error: واٹس ایپ سے پیغام موصول نہیں کر سکا
whatsapp.message.removing.error: واٹس ایپ سے پیغام ہٹا نہیں سکا
job.scheduled.error.header: شیڈول کردہ کام انجام دینے میں ناکام رہا
message.from.whatsapp: |
{{{text}}}
📱 *واٹس ایپ سے بھیجا گیا*
message.from.api: |
{{{text}}}
📱 *API کے ذریعے بھیجا گیا*
message.removed.in.whatsapp: |
❌ *یہ پیغام واٹس ایپ میں حذف کر دیا گیا تھا*
message.edited.in.whatsapp: |
{{{text}}}
✏️ *واٹس ایپ میں ترمیم کی گئی*
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
whatsapp.reaction.added: |
*ردعمل دیا* {{{emoji}}}
whatsapp.reaction.removed: |
*ردعمل ہٹا دیا گیا*
whatsapp.contact.group.suffix: 'گروپ'
whatsapp.contact.channel.suffix: 'چینل'
whatsapp.contact.broadcast.suffix: 'براڈکاسٹ فہرست'
# Aka Stories
whatsapp.contact.status.name: 'سٹیٹس'
app.connected.message: |
🔗 **منسلک: واٹس ایپ سیشن ChatWoot انبکس سے**
✅ واٹس ایپ سیشن **{{{name}}}** کامیابی سے اس ChatWoot انبکس سے منسلک ہو گیا ہے۔
یہ انبکس اب آپ کے **واٹس ایپ اکاؤنٹ** سے منسلک ہے۔ آپ یہاں سے براہ راست پیغامات بھیج اور موصول کر سکتے ہیں — بالکل واٹس ایپ استعمال کرنے کی طرح، لیکن ChatWoot کے اندر۔
آپ اس انبکس سے منسلک واٹس ایپ سیشن کا انتظام بھی کر سکتے ہیں۔ دستیاب اقدامات دیکھنے کے لیے، اس چیٹ میں **help** بھیجیں۔
📋 عام اقدامات جو آپ کر سکتے ہیں:
- چیک کریں کہ آیا واٹس ایپ سیشن فعال ہے
- واٹس ایپ سیشن کو دوبارہ شروع کریں یا روکیں
- لاگ آؤٹ کریں اور نیا QR کوڈ اسکین کر کے دوبارہ منسلک ہوں
اگر سب کچھ کام کر رہا ہے، تو آپ آزادانہ طور پر چیٹ کر سکیں گے۔ اگر آپ کو انتباہات نظر آتے ہیں، تو آپ کو اپنے واٹس ایپ اکاؤنٹ کو دوبارہ منسلک کرنے کی ضرورت ہو سکتی ہے۔
آئیے شروع کریں! 🚀
app.disconnected.message: |
🔌 **منقطع: واٹس ایپ سیشن ChatWoot انبکس سے**
❌ واٹس ایپ سیشن **{{{name}}}** اس ChatWoot انبکس سے منقطع ہو گیا ہے۔
آپ اب واٹس ایپ سے پیغامات بھیجنے یا موصول کرنے کے قابل نہیں ہوں گے۔
دوبارہ منسلک ہونے کے لیے، براہ کرم اپنے منتظم سے رابطہ کریں۔
app.updated.message: |
🔄 **اپڈیٹ شدہ: واٹس ایپ سیشن کی ترتیبات**
✅ واٹس ایپ سیشن **{{{name}}}** کی ترتیبات اپڈیٹ کر دی گئی ہیں۔
app.commands.list: |
آپ درج ذیل کمانڈز میں سے ایک بھیج کر **واٹس ایپ انٹیگریشن** کا انتظام کر سکتے ہیں:
🖥️ **سیشن**:
1. **status** - موجودہ سیشن کی حالت حاصل کریں۔
2. **restart** - سیشن کو دوبارہ شروع کریں - یہ آپ کے واٹس ایپ اکاؤنٹ کو دوبارہ منسلک کرنے کی کوشش کرے گا۔
3. **start** - سیشن شروع کریں۔
4. **stop** - سیشن روکیں۔
5. **logout** - سیشن سے لاگ آؤٹ کریں، تاکہ آپ QR کوڈ اسکین کر سکیں اور سیشن کی مرمت کر سکیں۔
6. **qr** - سیشن کے لیے QR کوڈ حاصل کریں۔
7. **screenshot** - سیشن کا اسکرین شاٹ حاصل کریں (صرف **WEBJS** انجن میں دستیاب)۔
8. **help** - دستیاب کمانڈز۔
{{#commands.server}}
⚙️ سرور:
- **server status** - سرور ورژن اور حالت کی معلومات حاصل کریں۔
- **server reboot** - سرور کو آرام سے دوبارہ شروع کریں۔
- **server reboot force** - سرور کو زبردستی دوبارہ شروع کریں۔
{{/commands.server}}
اس گفتگو میں **کمانڈ** (کیس سے غیر حساس) یا **کمانڈ نمبر** بھیجیں۔
app.commands.server.disabled: ⛔ **سرور کمانڈز غیر فعال ہیں**۔
براہ کرم اپنے منتظم سے رابطہ کریں۔
دستیاب کمانڈز کی فہرست حاصل کرنے کے لیے **help** بھیجیں۔
app.help.reminder: |
دستیاب کمانڈز کی فہرست حاصل کرنے کے لیے **help** بھیجیں۔
app.session.status.change: |
{{{emoji}}} "{{{session}}}" سیشن {{{status}}} حالت میں تبدیل ہو گیا
app.session.current.status: |
{{{emoji}}} **{{{session}}}** سیشن کی حالت **{{{status}}}** میں ہے
**واٹس ایپ اکاؤنٹ:**
📱 **{{{name}}} ({{{id}}})**
app.session.status.working: |
✅ یہ ChatWoot انبکس **واٹس ایپ اکاؤنٹ** سے منسلک ہے اور پیغامات بھیجنے اور موصول کرنے کے لیے تیار ہے!
📱 **{{{name}}} ({{{id}}})**
app.session.status.error: |
⚠️ **یہ ChatWoot انبکس واٹس ایپ اکاؤنٹ سے منسلک ہے لیکن پیغامات بھیجنے اور موصول کرنے کے لیے تیار نہیں ہے!**
براہ کرم سیشن کی حالت چیک کریں اور اسے دوبارہ شروع کرنے کی کوشش کریں یا لاگ آؤٹ کریں اور QR کوڈ دوبارہ اسکین کریں۔
app.session.scan.qr.code: |
⚠️ **اس ChatWoot انبکس کے ساتھ واٹس ایپ اکاؤنٹ کو منسلک کرنے کے لیے اپنے فون پر QR کوڈ اسکین کریں!**
app.server.version.and.status: |
**ورژن:**
```
{{{version}}}
```
**حالت:**
```
{{{status}}}
```
app.server.reboot: |
سرور آرام سے دوبارہ شروع ہو رہا ہے...
app.server.reboot.force: |
سرور زبردستی دوبارہ شروع ہو رہا ہے...
app.logout.success: |
✅ اکاؤنٹ کامیابی سے غیر مجاز ہو گیا!
waha.new.version.available: |
🔄 **نیا ورژن دستیاب ہے!**
موجودہ ورژن: **{{{currentVersion}}}**
نیا ورژن: **{{{newVersion}}}**
👉 [**🆕 تبدیلیوں کی فہرست**]({{{changelogUrl}}}) میں نیا کیا ہے دیکھیں
waha.core.version.used: |
❤️ **WAHA کی حمایت کریں!**
آپ مفت **WAHA Core** ورژن استعمال کر رہے ہیں۔ اگر آپ کو یہ پروجیکٹ مفید لگتا ہے، تو براہ کرم اس کی ترقی کی حمایت کرنے پر غور کریں۔
آپ کی حمایت ہی وہ واحد طریقہ ہے جس سے ہم اپڈیٹس، فکسز، اور نئی خصوصیات پر کام جاری رکھ سکتے ہیں۔
[**🎁 ہماری حمایت کریں**]({{{supportUrl}}})
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **شیئر کیے گئے رابطہ(رابطے)**
{{#contacts}}
🪪 نام: **{{fullName}}**
{{#phoneNumbers}}
- 📞 فون: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **شیئر کی گئی لوکیشن**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **لائیو لوکیشن شیئر کی جا رہی ہے**
حقیقی وقت کی تازہ کاریوں کے لیے **WhatsApp** کھولیں۔
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **ChatWoot میں اس قسم کا پیغام معاونت یافتہ نہیں ہے۔**
📱 اسے دیکھنے کے لیے براہ کرم **WhatsApp** کھولیں۔
تفصیلات: [{{{details.text}}}]({{{details.url}}})
+238
View File
@@ -0,0 +1,238 @@
# Simplified Chinese translations for ChatWoot integration
locale.name: '🇨🇳 中文(Simplified Chinese)'
app.inbox.contact.name: 'WhatsApp 集成(WAHA)'
app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png'
job.report.error: |
⚠️ **{{{header}}}**
{{#attempts.nextDelay}}
🔄 将在 {{attempts.nextDelay}} 秒后重试...
{{/attempts.nextDelay}}
{{^attempts.nextDelay}}
❌ 没有更多重试机会。请稍后再试。
{{/attempts.nextDelay}}
尝试:{{{attempts.current}}} / {{{attempts.max}}}
详情:[{{{details.text}}}]({{{details.url}}})
{{#error}}
```
{{{error}}}
```
{{/error}}
job.report.succeeded: |
✅ **成功**
尝试:{{{attempts.current}}} / {{{attempts.max}}}
详情:[{{{details.text}}}]({{{details.url}}})
whatsapp.message.sending.error: 无法在 WhatsApp 中发送消息。
whatsapp.message.receiving.error: 无法接收来自 WhatsApp 的消息
whatsapp.message.removing.error: 无法从 WhatsApp 中删除消息
job.scheduled.error.header: 计划任务执行失败
message.from.whatsapp: |
{{{text}}}
📱 *通过 WhatsApp 发送*
message.from.api: |
{{{text}}}
📱 *通过 API 发送*
message.removed.in.whatsapp: |
❌ *此消息已在 WhatsApp 中被删除*
message.edited.in.whatsapp: |
{{{text}}}
✏️ *已在 WhatsApp 中编辑*
whatsapp.group.message: |
{{{text}}}
👥 *{{{participant}}}*
whatsapp.reaction.added: |
*已反应* {{{emoji}}}
whatsapp.reaction.removed: |
*已移除反应*
whatsapp.contact.group.suffix: '群组'
whatsapp.contact.channel.suffix: '频道'
whatsapp.contact.broadcast.suffix: '广播列表'
# Aka Stories
whatsapp.contact.status.name: '状态'
app.connected.message: |
🔗 **已连接:WhatsApp 会话到 ChatWoot 收件箱**
✅ WhatsApp 会话 **{{{name}}}** 已成功连接到此 ChatWoot 收件箱。
此收件箱现已链接到你的**WhatsApp 账号**。你可以在这里直接收发消息——就像使用 WhatsApp 一样,但在 ChatWoot 内完成。
你也可以管理与此收件箱关联的 WhatsApp 会话。要查看可用操作,请向此对话发送 **help**。
📋 你可以执行的常见操作:
- 检查 WhatsApp 会话是否活动
- 重启或停止 WhatsApp 会话
- 登出并通过扫描新的二维码重新连接
如果一切正常,你就可以自由聊天。如果你看到警告,可能需要重新连接你的 WhatsApp 账号。
开始吧!🚀
app.disconnected.message: |
🔌 **已断开:从 ChatWoot 收件箱断开 WhatsApp 会话**
❌ WhatsApp 会话 **{{{name}}}** 已与此 ChatWoot 收件箱断开。
你将无法再从 WhatsApp 发送或接收消息。
如需重新连接,请联系你的管理员。
app.updated.message: |
🔄 **已更新:WhatsApp 会话设置**
✅ WhatsApp 会话 **{{{name}}}** 的设置已更新。
app.commands.list: |
你可以通过发送以下命令来管理**WhatsApp 集成**:
🖥️ **会话**:
1. **status** - 获取当前会话状态。
2. **restart** - 重启会话——将尝试重新连接你的 WhatsApp 账号。
3. **start** - 启动会话。
4. **stop** - 停止会话。
5. **logout** - 登出会话,以便你可以扫描二维码并修复会话。
6. **qr** - 获取该会话的二维码。
7. **screenshot** - 获取会话截图(仅在 **WEBJS** 引擎中可用)。
8. **help** - 查看可用命令。
{{#commands.server}}
{{#commands.server}}
⚙️ 服务器:
- **server status** - 获取服务器版本和状态信息。
- **server reboot** - 优雅地重启服务器。
- **server reboot force** - 强制重启服务器。
{{/commands.server}}
{{/commands.server}}
向此对话发送**命令**(不区分大小写)或**命令编号**。
app.commands.server.disabled: ⛔ **服务器命令已禁用**。
请联系你的管理员。
发送 **help** 获取可用命令列表。
app.help.reminder: |
发送 **help** 获取可用命令列表。
app.session.status.change: |
{{{emoji}}} "{{{session}}}" 会话已切换为 {{{status}}} 状态
app.session.current.status: |
{{{emoji}}} **{{{session}}}** 会话状态为 **{{{status}}}**
**WhatsApp 账号:**
📱 **{{{name}}} ({{{id}}})**
app.session.status.working: |
✅ 此 ChatWoot 收件箱已链接到**WhatsApp 账号**,可以正常收发消息!
📱 **{{{name}}} ({{{id}}})**
app.session.status.error: |
⚠️ **此 ChatWoot 收件箱已链接到 WhatsApp 账号,但尚未准备好收发消息!**
请检查会话状态,尝试重启,或登出后重新扫描二维码。
app.session.scan.qr.code: |
⚠️ **请在手机上扫描二维码,将 WhatsApp 账号与此 ChatWoot 收件箱关联!**
app.server.version.and.status: |
**版本:**
```
{{{version}}}
```
**状态:**
```
{{{status}}}
```
app.server.reboot: |
服务器正在优雅重启...
app.server.reboot.force: |
服务器正在强制重启...
app.logout.success: |
✅ 账号已成功取消授权!
waha.new.version.available: |
🔄 **有新版本可用!**
当前版本:**{{{currentVersion}}}**
新版本:**{{{newVersion}}}**
👉 查看[**🆕 更新日志**]({{{changelogUrl}}})
waha.core.version.used: |
❤️ **支持 WAHA!**
你正在使用免费的 **WAHA Core** 版本。如果你觉得该项目有帮助,请考虑支持其开发。
你的支持是我们持续进行更新、修复和新增功能的唯一动力。
[**🎁 支持我们**]({{{supportUrl}}})
chatwoot.to.whatsapp.message.text: |
{{{ content }}}
chatwoot.to.whatsapp.message.media.caption: |
{{#singleAttachment}}
{{#content}}
{{{ content }}}
{{/content}}
{{/singleAttachment}}
whatsapp.to.chatwoot.message: |
{{{payload.body}}}
whatsapp.to.chatwoot.message.contacts: |
👤 **共享联系人**
{{#contacts}}
🪪 姓名: **{{fullName}}**
{{#phoneNumbers}}
- 📞 电话: **{{.}}**
{{/phoneNumbers}}
{{/contacts}}
whatsapp.to.chatwoot.message.location: |
{{#message}}
{{#locationMessage}}
📍 **共享位置**
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/locationMessage}}
{{#liveLocationMessage}}
📍📡 **正在共享实时位置**
打开 **WhatsApp** 查看实时更新。
https://www.google.com/maps?q={{degreesLatitude}},{{degreesLongitude}}
{{#caption}}{{caption}}{{/caption}}
{{/liveLocationMessage}}
{{/message}}
whatsapp.to.chatwoot.message.unsupported: |
⚠️ **ChatWoot 不支持此消息类型。**
📱 请打开 **WhatsApp** 查看。
详情: [{{{details.text}}}]({{{details.url}}})
+189
View File
@@ -0,0 +1,189 @@
import { ChatWootCommandsConfig } from '@waha/apps/chatwoot/dto/config.dto';
import { WAMessage } from '@waha/structures/responses.dto';
import { proto } from '@adiwajshing/baileys';
import { SimpleVCardInfo } from '@waha/core/vcard';
export enum TKey {
LOCALE_NAME = 'locale.name',
//
// Jobs
//
JOB_REPORT_ERROR = 'job.report.error',
JOB_REPORT_SUCCEEDED = 'job.report.succeeded',
JOB_SCHEDULED_ERROR_HEADER = 'job.scheduled.error.header',
//
// WhatsApp Message Status
//
WHATSAPP_MESSAGE_SENDING_ERROR = 'whatsapp.message.sending.error',
WHATSAPP_MESSAGE_RECEIVING_ERROR = 'whatsapp.message.receiving.error',
WHATSAPP_MESSAGE_REMOVING_ERROR = 'whatsapp.message.removing.error',
//
// WhatsApp Messages
//
MESSAGE_FROM_WHATSAPP = 'message.from.whatsapp',
MESSAGE_FROM_API = 'message.from.api',
MESSAGE_REMOVED_IN_WHATSAPP = 'message.removed.in.whatsapp',
MESSAGE_EDITED_IN_WHATSAPP = 'message.edited.in.whatsapp',
WHATSAPP_GROUP_MESSAGE = 'whatsapp.group.message',
WHATSAPP_REACTION_ADDED = 'whatsapp.reaction.added',
WHATSAPP_REACTION_REMOVED = 'whatsapp.reaction.removed',
//
// Contact Suffixes
//
WHATSAPP_CONTACT_GROUP_SUFFIX = 'whatsapp.contact.group.suffix',
WHATSAPP_CONTACT_CHANNEL_SUFFIX = 'whatsapp.contact.channel.suffix',
WHATSAPP_CONTACT_BROADCAST_SUFFIX = 'whatsapp.contact.broadcast.suffix',
WHATSAPP_CONTACT_STATUS_NAME = 'whatsapp.contact.status.name',
//
// ChatWoot to WhatsApp
//
CW_TO_WA_MESSAGE_TEXT = 'chatwoot.to.whatsapp.message.text',
CW_TO_WA_MESSAGE_MEDIA_CAPTION = 'chatwoot.to.whatsapp.message.media.caption',
//
// WhatsApp to ChatWoot
//
WA_TO_CW_MESSAGE = 'whatsapp.to.chatwoot.message',
WA_TO_CW_MESSAGE_CONTACTS = 'whatsapp.to.chatwoot.message.contacts',
WA_TO_CW_MESSAGE_LOCATION = 'whatsapp.to.chatwoot.message.location',
WA_TO_CW_MESSAGE_UNSUPPORTED = 'whatsapp.to.chatwoot.message.unsupported',
//
// App Inbox
//
APP_INBOX_CONTACT_NAME = 'app.inbox.contact.name',
APP_INBOX_CONTACT_AVATAR_URL = 'app.inbox.contact.avatar.url',
//
// App Status
//
APP_CONNECTED_MESSAGE = 'app.connected.message',
APP_DISCONNECTED_MESSAGE = 'app.disconnected.message',
APP_UPDATED_MESSAGE = 'app.updated.message',
//
// App Session Status
//
APP_SESSION_STATUS_CHANGE = 'app.session.status.change',
APP_SESSION_CURRENT_STATUS = 'app.session.current.status',
APP_SESSION_STATUS_WORKING = 'app.session.status.working',
APP_SESSION_STATUS_ERROR = 'app.session.status.error',
APP_SESSION_SCAN_QR_CODE = 'app.session.scan.qr.code',
//
// App Commands
//
APP_COMMANDS_LIST = 'app.commands.list',
APP_COMMANDS_SERVER_DISABLED = 'app.commands.server.disabled',
APP_HELP_REMINDER = 'app.help.reminder',
APP_SERVER_VERSION_AND_STATUS = 'app.server.version.and.status',
APP_SERVER_REBOOT = 'app.server.reboot',
APP_SERVER_REBOOT_FORCE = 'app.server.reboot.force',
APP_LOGOUT_SUCCESS = 'app.logout.success',
//
// App Inbox
//
WAHA_NEW_VERSION_AVAILABLE = 'waha.new.version.available',
WAHA_CORE_VERSION_USED = 'waha.core.version.used',
}
// Define payload types
interface Link {
text: string;
url: string;
}
export type TemplatePayloads = {
[TKey.LOCALE_NAME]: void;
[TKey.APP_INBOX_CONTACT_NAME]: void;
[TKey.APP_INBOX_CONTACT_AVATAR_URL]: void;
[TKey.MESSAGE_FROM_WHATSAPP]: { text: string };
[TKey.MESSAGE_FROM_API]: { text: string };
[TKey.MESSAGE_REMOVED_IN_WHATSAPP]: void;
[TKey.MESSAGE_EDITED_IN_WHATSAPP]: { text: string };
[TKey.WHATSAPP_GROUP_MESSAGE]: { text: string; participant: string };
[TKey.WHATSAPP_REACTION_ADDED]: { emoji: string };
[TKey.WHATSAPP_REACTION_REMOVED]: void;
[TKey.WHATSAPP_MESSAGE_SENDING_ERROR]: void;
[TKey.WHATSAPP_MESSAGE_RECEIVING_ERROR]: void;
[TKey.WHATSAPP_MESSAGE_REMOVING_ERROR]: void;
[TKey.WHATSAPP_CONTACT_GROUP_SUFFIX]: void;
[TKey.WHATSAPP_CONTACT_CHANNEL_SUFFIX]: void;
[TKey.WHATSAPP_CONTACT_BROADCAST_SUFFIX]: void;
[TKey.WHATSAPP_CONTACT_STATUS_NAME]: void;
[TKey.CW_TO_WA_MESSAGE_TEXT]: {
content: string;
chatwoot: any;
};
[TKey.CW_TO_WA_MESSAGE_MEDIA_CAPTION]: {
content: string;
chatwoot: any;
singleAttachment: boolean;
};
[TKey.WA_TO_CW_MESSAGE]: {
payload: WAMessage;
};
[TKey.WA_TO_CW_MESSAGE_CONTACTS]: { contacts: SimpleVCardInfo[] };
[TKey.WA_TO_CW_MESSAGE_LOCATION]: { payload: any; message: proto.Message };
[TKey.WA_TO_CW_MESSAGE_UNSUPPORTED]: { details: Link };
[TKey.JOB_SCHEDULED_ERROR_HEADER]: void;
[TKey.JOB_REPORT_ERROR]: {
header: string;
error: string;
details: Link;
attempts: {
current: number;
max: number;
nextDelay?: number;
};
};
[TKey.JOB_REPORT_SUCCEEDED]: {
details: Link;
attempts: {
current: number;
max: number;
};
};
[TKey.APP_CONNECTED_MESSAGE]: { name: string };
[TKey.APP_DISCONNECTED_MESSAGE]: { name: string };
[TKey.APP_UPDATED_MESSAGE]: { name: string };
[TKey.APP_SESSION_STATUS_CHANGE]: {
emoji: string;
session: string;
status: string;
};
[TKey.APP_SESSION_CURRENT_STATUS]: {
emoji: string;
session: string;
status: string;
name: string;
id: string;
};
[TKey.APP_SESSION_STATUS_WORKING]: { name: string; id: string };
[TKey.APP_SESSION_STATUS_ERROR]: void;
[TKey.APP_SESSION_SCAN_QR_CODE]: void;
[TKey.APP_HELP_REMINDER]: void;
[TKey.APP_COMMANDS_LIST]: { commands: ChatWootCommandsConfig };
[TKey.APP_COMMANDS_SERVER_DISABLED]: void;
[TKey.APP_SERVER_VERSION_AND_STATUS]: {
version: string;
status: string;
};
[TKey.APP_SERVER_REBOOT]: void;
[TKey.APP_SERVER_REBOOT_FORCE]: void;
[TKey.APP_LOGOUT_SUCCESS]: void;
[TKey.WAHA_NEW_VERSION_AVAILABLE]: {
currentVersion: string;
newVersion: string;
changelogUrl: string;
};
[TKey.WAHA_CORE_VERSION_USED]: {
supportUrl: string;
};
};
-233
View File
@@ -1,233 +0,0 @@
import * as fs from 'fs-extra';
import * as lodash from 'lodash';
import * as Mustache from 'mustache';
import * as path from 'path';
import * as yaml from 'yaml';
export enum LocaleCode {
EN_US = 'en-US',
AR_AE = 'ar-AE',
BN_BD = 'bn-BD',
PT_BR = 'pt-BR',
DE_DE = 'de-DE',
ES_ES = 'es-ES',
FR_FR = 'fr-FR',
ID_ID = 'id-ID',
HE_IL = 'he-IL',
HI_IN = 'hi-IN',
FA_IR = 'fa-IR',
PA_PK = 'pa-PK',
RU_RU = 'ru-RU',
TR_TR = 'tr-TR',
UK_UA = 'uk-UA',
}
export const LOCALES = [
LocaleCode.EN_US,
LocaleCode.AR_AE,
LocaleCode.BN_BD,
LocaleCode.PT_BR,
LocaleCode.DE_DE,
LocaleCode.ES_ES,
LocaleCode.FR_FR,
LocaleCode.ID_ID,
LocaleCode.HE_IL,
LocaleCode.HI_IN,
LocaleCode.FA_IR,
LocaleCode.PA_PK,
LocaleCode.RU_RU,
LocaleCode.TR_TR,
LocaleCode.UK_UA,
];
export const LOCALE_NAMES: Record<LocaleCode, string> = {
[LocaleCode.EN_US]: '🇺🇸 English (English)',
[LocaleCode.AR_AE]: '🇦🇪 العربية (Arabic)',
[LocaleCode.BN_BD]: '🇧🇩 বাংলা (Bengali)',
[LocaleCode.PT_BR]: '🇧🇷🇵🇹 Português (Portuguese)',
[LocaleCode.DE_DE]: '🇩🇪 Deutsch (German)',
[LocaleCode.ES_ES]: '🇪🇸 Español (Spanish)',
[LocaleCode.FR_FR]: '🇫🇷 Français (French)',
[LocaleCode.ID_ID]: '🇮🇩 Bahasa Indonesia (Indonesian)',
[LocaleCode.HE_IL]: '🇮🇱 עברית (Hebrew)',
[LocaleCode.HI_IN]: '🇮🇳 हिंदी (Hindi)',
[LocaleCode.FA_IR]: '🇮🇷 فارسی (Persian)',
[LocaleCode.PA_PK]: '🇵🇰 ਪੰਜਾਬੀ (Punjabi)',
[LocaleCode.RU_RU]: '🇷🇺 Русский (Russian)',
[LocaleCode.TR_TR]: '🇹🇷 Türkçe (Turkish)',
[LocaleCode.UK_UA]: '🇺🇦 Українська (Ukrainian)',
};
export const DEFAULT_LOCALE = 'en-US';
function loadTemplateStrings(locale: string) {
const filename = `${locale}.yaml`;
const yamlPath = path.join(__dirname, 'i18n', filename);
const content = fs.readFileSync(yamlPath, 'utf-8');
return yaml.parse(content);
}
export interface Link {
text: string;
url: string;
}
function loadLocales(locales: string[]) {
const defaults = loadTemplateStrings(DEFAULT_LOCALE);
const result: Record<string, Record<string, string>> = {};
for (const locale of locales) {
const strings = loadTemplateStrings(locale);
result[locale] = lodash.merge({}, defaults, strings);
}
return result;
}
let locales: Record<string, Record<string, string>> = {};
try {
locales = loadLocales(LOCALES);
} catch (error) {
console.error('Error loading locales:', error);
process.exit(1);
}
export enum TKey {
WHATSAPP_ERROR_SENDING_MESSAGE = 'WHATSAPP_ERROR_SENDING_MESSAGE',
WHATSAPP_ERROR_RECEIVING_MESSAGE = 'WHATSAPP_ERROR_RECEIVING_MESSAGE',
WHATSAPP_ERROR_REMOVING_MESSAGE = 'WHATSAPP_ERROR_REMOVING_MESSAGE',
JOB_ERROR_REPORT = 'JOB_ERROR_REPORT',
JOB_SUCCEEDED_REPORT = 'JOB_SUCCEEDED_REPORT',
MESSAGE_FROM_WHATSAPP = 'MESSAGE_FROM_WHATSAPP',
MESSAGE_FROM_API = 'MESSAGE_FROM_API',
MESSAGE_REMOVED_IN_WHATSAPP = 'MESSAGE_REMOVED_IN_WHATSAPP',
MESSAGE_EDITED_IN_WHATSAPP = 'MESSAGE_EDITED_IN_WHATSAPP',
WHATSAPP_GROUP_MESSAGE = 'WHATSAPP_GROUP_MESSAGE',
WHATSAPP_REACTION_ADDED = 'WHATSAPP_REACTION_ADDED',
WHATSAPP_REACTION_REMOVED = 'WHATSAPP_REACTION_REMOVED',
//
// Contact Suffixes
//
WHATSAPP_CONTACT_GROUP_SUFFIX = 'WHATSAPP_CONTACT_GROUP_SUFFIX',
WHATSAPP_CONTACT_CHANNEL_SUFFIX = 'WHATSAPP_CONTACT_CHANNEL_SUFFIX',
WHATSAPP_CONTACT_STATUS_NAME = 'WHATSAPP_CONTACT_STATUS_NAME',
//
// App Inbox
//
APP_INBOX_CONTACT_NAME = 'APP_INBOX_CONTACT_NAME',
APP_INBOX_CONTACT_AVATAR_URL = 'APP_INBOX_CONTACT_AVATAR_URL',
APP_CONNECTED_MESSAGE = 'APP_CONNECTED_MESSAGE',
APP_DISCONNECTED_MESSAGE = 'APP_DISCONNECTED_MESSAGE',
APP_UPDATED_MESSAGE = 'APP_UPDATED_MESSAGE',
APP_COMMANDS_LIST = 'APP_COMMANDS_LIST',
APP_HELP_REMINDER = 'APP_HELP_REMINDER',
APP_SESSION_STATUS_CHANGE = 'APP_SESSION_STATUS_CHANGE',
APP_SESSION_CURRENT_STATUS = 'APP_SESSION_CURRENT_STATUS',
APP_DASHBOARD_LINK = 'APP_DASHBOARD_LINK',
APP_SESSION_STATUS_WORKING = 'APP_SESSION_STATUS_WORKING',
APP_SESSION_STATUS_ERROR = 'APP_SESSION_STATUS_ERROR',
APP_SESSION_SCAN_QR_CODE = 'APP_SESSION_SCAN_QR_CODE',
APP_SERVER_VERSION_AND_STATUS = 'APP_SERVER_VERSION_AND_STATUS',
APP_SERVER_REBOOT = 'APP_SERVER_REBOOT',
APP_SERVER_REBOOT_FORCE = 'APP_SERVER_REBOOT_FORCE',
APP_LOGOUT_SUCCESS = 'APP_LOGOUT_SUCCESS',
APP_NEW_VERSION_AVAILABLE = 'APP_NEW_VERSION_AVAILABLE',
APP_CORE_VERSION_USED = 'APP_CORE_VERSION_USED',
APP_SCHEDULED_JOB_ERROR = 'APP_SCHEDULED_JOB_ERROR',
}
// Define payload types
type TemplatePayloads = {
[TKey.APP_INBOX_CONTACT_NAME]: void;
[TKey.APP_INBOX_CONTACT_AVATAR_URL]: void;
[TKey.MESSAGE_FROM_WHATSAPP]: { text: string };
[TKey.MESSAGE_FROM_API]: { text: string };
[TKey.MESSAGE_REMOVED_IN_WHATSAPP]: void;
[TKey.MESSAGE_EDITED_IN_WHATSAPP]: { text: string };
[TKey.WHATSAPP_GROUP_MESSAGE]: { text: string; participant: string };
[TKey.WHATSAPP_REACTION_ADDED]: { emoji: string };
[TKey.WHATSAPP_REACTION_REMOVED]: void;
[TKey.WHATSAPP_ERROR_SENDING_MESSAGE]: void;
[TKey.WHATSAPP_ERROR_RECEIVING_MESSAGE]: void;
[TKey.WHATSAPP_ERROR_REMOVING_MESSAGE]: void;
[TKey.WHATSAPP_CONTACT_GROUP_SUFFIX]: void;
[TKey.WHATSAPP_CONTACT_CHANNEL_SUFFIX]: void;
[TKey.WHATSAPP_CONTACT_STATUS_NAME]: void;
[TKey.APP_SCHEDULED_JOB_ERROR]: void;
[TKey.JOB_ERROR_REPORT]: {
header: string;
error: string;
details: Link;
attempts: {
current: number;
max: number;
nextDelay?: number;
};
};
[TKey.JOB_SUCCEEDED_REPORT]: {
details: Link;
attempts: {
current: number;
max: number;
};
};
[TKey.APP_CONNECTED_MESSAGE]: { name: string };
[TKey.APP_DISCONNECTED_MESSAGE]: { name: string };
[TKey.APP_UPDATED_MESSAGE]: { name: string };
[TKey.APP_SESSION_STATUS_CHANGE]: {
emoji: string;
session: string;
status: string;
};
[TKey.APP_SESSION_CURRENT_STATUS]: {
emoji: string;
session: string;
status: string;
name: string;
id: string;
};
[TKey.APP_DASHBOARD_LINK]: { url: string };
[TKey.APP_SESSION_STATUS_WORKING]: { name: string; id: string };
[TKey.APP_SESSION_STATUS_ERROR]: void;
[TKey.APP_SESSION_SCAN_QR_CODE]: void;
[TKey.APP_HELP_REMINDER]: void;
[TKey.APP_COMMANDS_LIST]: void;
[TKey.APP_SERVER_VERSION_AND_STATUS]: { version: string; status: string };
[TKey.APP_SERVER_REBOOT]: void;
[TKey.APP_SERVER_REBOOT_FORCE]: void;
[TKey.APP_LOGOUT_SUCCESS]: void;
[TKey.APP_NEW_VERSION_AVAILABLE]: {
currentVersion: string;
newVersion: string;
changelogUrl: string;
};
[TKey.APP_CORE_VERSION_USED]: {
supportUrl: string;
};
};
export class Locale {
private readonly strings: Record<string, string>;
constructor(locale: string = DEFAULT_LOCALE) {
locale = locale || DEFAULT_LOCALE;
if (!locales[locale]) {
throw new Error(`Locale '${locale}' not found`);
}
this.strings = locales[locale];
}
key<K extends TKey>(key: K): Template<K> {
return new Template(this.strings[key] || key);
}
}
export class Template<K extends TKey> {
constructor(private readonly template: string) {}
render(data: TemplatePayloads[K]): string {
return Mustache.render(this.template, data);
}
r(data: TemplatePayloads[K]): string {
return this.render(data);
}
}
@@ -1,4 +1,4 @@
import { Injectable } from '@nestjs/common';
import { Injectable, UnprocessableEntityException } from '@nestjs/common';
import { IAppService } from '@waha/apps/app_sdk/services/IAppService';
import { CacheForConfig } from '@waha/apps/chatwoot/cache/ConversationCache';
import { CHATWOOT_CUSTOM_ATTRIBUTES } from '@waha/apps/chatwoot/const';
@@ -10,7 +10,10 @@ import { WhatsappSession } from '@waha/core/abc/session.abc';
import { InjectPinoLogger, PinoLogger } from 'nestjs-pino';
import { DIContainer } from '../di/DIContainer';
import { TKey } from '../locale';
import { TKey } from '@waha/apps/chatwoot/i18n/templates';
import { session } from 'passport';
import { i18n } from '@waha/apps/chatwoot/i18n';
@Injectable()
export class ChatWootAppService implements IAppService {
@@ -21,11 +24,37 @@ export class ChatWootAppService implements IAppService {
protected logger: PinoLogger,
) {}
validate(app: App<ChatWootAppConfig>): void {
if (!i18n.has(app.config.locale)) {
throw new UnprocessableEntityException(
`Unknown '${app.config.locale}' locale in 'config.local'`,
);
}
}
async beforeCreated(app: App<ChatWootAppConfig>) {
await this.setupCustomAttributes(app);
await this.sendConnectedMessage(app);
}
async beforeEnabled(
savedApp: App<ChatWootAppConfig>,
newApp: App<ChatWootAppConfig>,
): Promise<void> {
// Enabling -> behave like created
await this.beforeCreated(newApp);
return;
}
async beforeDisabled(
savedApp: App<ChatWootAppConfig>,
newApp: App<ChatWootAppConfig>,
): Promise<void> {
// Disabling -> behave like deleted
await this.beforeDeleted(savedApp);
return;
}
async beforeUpdated(
savedApp: App<ChatWootAppConfig>,
newApp: App<ChatWootAppConfig>,
+1 -1
View File
@@ -20,7 +20,7 @@ export function MarkdownToWhatsApp(text: string): string {
// Links: [text](url) → text (url)
.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, '$1 ($2)')
// Lists: -, +, * → * item
.replace(/^[\-\+\*] (.*)/gm, '* $1')
.replace(/^[-+*] (.*)/gm, '* $1')
);
}
+21
View File
@@ -1,6 +1,7 @@
import { Injectable, Logger, OnApplicationBootstrap } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { GlobalWebhookConfigConfig } from '@waha/core/config/GlobalWebhookConfig';
import { IgnoreJidConfig } from '@waha/core/utils/jids';
import { parseBool } from './helpers';
import { WebhookConfig } from './structures/webhooks.config.dto';
@@ -178,6 +179,26 @@ export class WhatsappConfigService implements OnApplicationBootstrap {
return parseBool(value);
}
/**
* Global default "ignore settings" for chats.
* If not defined, defaults to false (do not ignore anything).
*/
getIgnoreChatsConfig(): IgnoreJidConfig {
const status = parseBool(
this.configService.get('WAHA_SESSION_CONFIG_IGNORE_STATUS', 'false'),
);
const groups = parseBool(
this.configService.get('WAHA_SESSION_CONFIG_IGNORE_GROUPS', 'false'),
);
const channels = parseBool(
this.configService.get('WAHA_SESSION_CONFIG_IGNORE_CHANNELS', 'false'),
);
const broadcast = parseBool(
this.configService.get('WAHA_SESSION_CONFIG_IGNORE_BROADCAST', 'false'),
);
return { status, groups, channels, broadcast };
}
onApplicationBootstrap() {
const error = this.webhookConfig.validateConfig();
if (error) {
+7
View File
@@ -0,0 +1,7 @@
import { WAHAEngine } from '@waha/structures/enums.dto';
export function getEngineName(): string {
// Load engine name from WHATSAPP_DEFAULT_ENGINE environment variable
// If not set - use WEBJS
return process.env.WHATSAPP_DEFAULT_ENGINE || WAHAEngine.WEBJS;
}
+8
View File
@@ -14,9 +14,11 @@ import { GowsEngineConfigService } from '@waha/core/config/GowsEngineConfigServi
import { GowsBootstrap } from '@waha/core/engines/gows/GowsBootstrap';
import { ISessionMeRepository } from '@waha/core/storage/ISessionMeRepository';
import { ISessionWorkerRepository } from '@waha/core/storage/ISessionWorkerRepository';
import { IgnoreJidConfig } from '@waha/core/utils/jids';
import { WAHAWebhook } from '@waha/structures/webhooks.dto';
import { waitUntil } from '@waha/utils/promiseTimeout';
import { VERSION } from '@waha/version';
import * as lodash from 'lodash';
import { PinoLogger } from 'nestjs-pino';
import { merge, Observable, of } from 'rxjs';
@@ -235,6 +237,12 @@ export abstract class SessionManager
}
return new NoopEngineBootstrap();
}
protected ignoreChatsConfig(config: SessionConfig) {
const ignore: IgnoreJidConfig = this.config.getIgnoreChatsConfig();
// Given the default, overwrite from the config if any
return lodash.defaults({}, config?.ignore, ignore);
}
}
export function populateSessionInfo(
+27 -5
View File
@@ -4,7 +4,11 @@ import {
IMediaConverter,
} from '@waha/core/media/IConverter';
import { MessagesForRead } from '@waha/core/utils/convertors';
import { isJidNewsletter } from '@waha/core/utils/jids';
import {
IgnoreJidConfig,
isJidNewsletter,
JidFilter,
} from '@waha/core/utils/jids';
import {
Channel,
ChannelListResult,
@@ -25,6 +29,7 @@ import {
ReadChatMessagesResponse,
} from '@waha/structures/chats.dto';
import { SendButtonsRequest } from '@waha/structures/chatting.buttons.dto';
import { SendListRequest } from '@waha/structures/chatting.list.dto';
import { BinaryFile, RemoteFile } from '@waha/structures/files.dto';
import { Label, LabelDTO, LabelID } from '@waha/structures/labels.dto';
import { LidToPhoneNumber } from '@waha/structures/lids.dto';
@@ -69,6 +74,7 @@ import {
MessageLinkPreviewRequest,
MessageLocationRequest,
MessagePollRequest,
MessagePollVoteRequest,
MessageReactionRequest,
MessageReplyRequest,
MessageStarRequest,
@@ -88,10 +94,7 @@ import {
WAHAPresenceStatus,
WAHASessionStatus,
} from '../../structures/enums.dto';
import {
EventCancelRequest,
EventMessageRequest,
} from '../../structures/events.dto';
import { EventMessageRequest } from '../../structures/events.dto';
import {
CreateGroupRequest,
GroupField,
@@ -151,8 +154,11 @@ export interface SessionParams {
loggerBuilder: LoggerBuilder;
sessionStore: DataStore;
proxyConfig?: ProxyConfig;
// Raw unchanged SessionConfig
sessionConfig?: SessionConfig;
engineConfig?: any;
// Ignore settings
ignore: IgnoreJidConfig;
}
export abstract class WhatsappSession {
@@ -167,6 +173,7 @@ export abstract class WhatsappSession {
public sessionConfig?: SessionConfig;
protected engineConfig?: any;
protected unpairing: boolean = false;
protected jids: JidFilter;
private _status: WAHASessionStatus;
private shouldPrintQR: boolean;
@@ -193,6 +200,7 @@ export abstract class WhatsappSession {
mediaManager,
sessionConfig,
engineConfig,
ignore,
}: SessionParams) {
this.status$ = new BehaviorSubject(null);
@@ -258,6 +266,11 @@ export abstract class WhatsappSession {
this.sessionConfig = sessionConfig;
this.engineConfig = engineConfig;
this.shouldPrintQR = printQR;
this.logger.info(
{ ignore: ignore },
'The session ignores the following chat ids',
);
this.jids = new JidFilter(ignore);
}
public getEventObservable(event: WAHAEvents) {
@@ -328,6 +341,7 @@ export abstract class WhatsappSession {
'--safebrowsing-disable-auto-update',
'--use-mock-keychain',
'--window-size=1280,720',
'--disable-blink-features=AutomationControlled',
//
// Cache options
//
@@ -451,6 +465,10 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
sendPollVote(request: MessagePollVoteRequest) {
throw new NotImplementedByEngineError();
}
abstract sendLocation(request: MessageLocationRequest);
sendLinkPreview(request: MessageLinkPreviewRequest) {
@@ -479,6 +497,10 @@ export abstract class WhatsappSession {
throw new NotImplementedByEngineError();
}
sendList(request: SendListRequest): Promise<any> {
throw new NotImplementedByEngineError();
}
sendButtonsReply(request: MessageButtonReply) {
throw new NotImplementedByEngineError();
}
+1
View File
@@ -6,6 +6,7 @@ import { HeaderAPIKeyStrategy } from 'passport-headerapikey';
@Injectable()
export class ApiKeyStrategy extends PassportStrategy(HeaderAPIKeyStrategy) {
constructor(private auth: IApiKeyAuth) {
// @ts-ignore
super({ header: 'X-Api-Key', prefix: '' }, true, (apikey, done) => {
const isValid = this.auth.isValid(apikey);
return done(isValid);
+1 -3
View File
@@ -32,9 +32,7 @@ export class HashAuth implements IApiKeyAuth {
constructor(
private hash: string,
private algorithm: string,
) {
this.algorithm = algorithm;
}
) {}
isValid(plain: string): boolean {
if (!plain) {
@@ -19,6 +19,7 @@ export class WebJSEngineConfigService {
return {
webVersion: webVersion,
cacheType: this.getCacheType(),
puppeteerArgs: this.getPuppeterArgs(),
};
}
@@ -34,4 +35,12 @@ export class WebJSEngineConfigService {
return cacheType;
}
getPuppeterArgs(): string[] {
const args = this.configService.get<string>('WAHA_WEBJS_PUPPETER_ARGS', '');
return args
.split(' ')
.map((arg) => arg.trim())
.filter(Boolean);
}
}
File diff suppressed because it is too large. Load diff
@@ -48,6 +48,17 @@ function deserialize_messages_ChatPresenceRequest(buffer_arg) {
return gows_pb.ChatPresenceRequest.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_messages_ChatUnreadRequest(arg) {
if (!(arg instanceof gows_pb.ChatUnreadRequest)) {
throw new Error('Expected argument of type messages.ChatUnreadRequest');
}
return Buffer.from(arg.serializeBinary());
}
function deserialize_messages_ChatUnreadRequest(buffer_arg) {
return gows_pb.ChatUnreadRequest.deserializeBinary(new Uint8Array(buffer_arg));
}
function serialize_messages_CheckPhonesRequest(arg) {
if (!(arg instanceof gows_pb.CheckPhonesRequest)) {
throw new Error('Expected argument of type messages.CheckPhonesRequest');
@@ -1053,6 +1064,17 @@ getProfilePicture: {
responseSerialize: serialize_messages_CheckPhonesResponse,
responseDeserialize: deserialize_messages_CheckPhonesResponse,
},
markChatUnread: {
path: '/messages.MessageService/MarkChatUnread',
requestStream: false,
responseStream: false,
requestType: gows_pb.ChatUnreadRequest,
responseType: gows_pb.Empty,
requestSerialize: serialize_messages_ChatUnreadRequest,
requestDeserialize: deserialize_messages_ChatUnreadRequest,
responseSerialize: serialize_messages_Empty,
responseDeserialize: deserialize_messages_Empty,
},
//
// Message
//
File diff suppressed because it is too large. Load diff
+178 -43
View File
@@ -1,11 +1,11 @@
import {
aggregateMessageKeysNotFromMe,
Contact,
getContentType,
getUrlFromDirectPath,
isJidGroup,
jidNormalizedUser,
normalizeMessageContent,
proto,
WAMessageKey,
} from '@adiwajshing/baileys';
import { isJidBroadcast } from '@adiwajshing/baileys/lib/WABinary/jid-utils';
@@ -35,6 +35,7 @@ import {
} from '@waha/core/engines/gows/helpers';
import { GowsAuthFactoryCore } from '@waha/core/engines/gows/store/GowsAuthFactoryCore';
import {
extractBody,
getDestination,
toCusFormat,
} from '@waha/core/engines/noweb/session.noweb.core';
@@ -80,6 +81,7 @@ import {
MessageLinkCustomPreviewRequest,
MessageLocationRequest,
MessagePollRequest,
MessagePollVoteRequest,
MessageReactionRequest,
MessageReplyRequest,
MessageTextRequest,
@@ -87,6 +89,7 @@ import {
SendSeenRequest,
WANumberExistResult,
} from '@waha/structures/chatting.dto';
import { SendListRequest } from '@waha/structures/chatting.list.dto';
import { ContactQuery, ContactUpdateBody } from '@waha/structures/contacts.dto';
import {
ACK_UNKNOWN,
@@ -96,7 +99,6 @@ import {
WAMessageAck,
} from '@waha/structures/enums.dto';
import {
EventCancelRequest,
EventMessageRequest,
EventResponse,
EventResponsePayload,
@@ -153,7 +155,7 @@ import * as gows from './types';
import { MessageStatus } from './types';
import MessageServiceClient = messages.MessageServiceClient;
import { isFromFullSync } from '@waha/core/engines/gows/appstate';
import { toVcard } from '@waha/core/helpers';
import { toVcardV3 } from '@waha/core/vcard';
import { AckToStatus } from '@waha/core/utils/acks';
import { ParseEventResponseType } from '@waha/core/utils/events';
import { DistinctAck } from '@waha/core/utils/reactive';
@@ -168,6 +170,7 @@ import {
isLabelChatAddedEvent,
isLabelUpsertEvent,
} from './labels.gows';
import IMessageKey = proto.IMessageKey;
enum WhatsMeowEvent {
CONNECTED = 'gows.ConnectedEventData',
@@ -251,6 +254,12 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
proxy: new messages.SessionProxyConfig({
url: this.getProxyUrl(this.proxyConfig),
}),
ignore: new messages.SessionIgnoreJidsConfig({
status: this.jids.ignore.status,
groups: this.jids.ignore.groups,
newsletters: this.jids.ignore.channels,
broadcast: this.jids.ignore.broadcast,
}),
}),
});
@@ -307,6 +316,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
this.me = {
id: toCusFormat(jidNormalizedUser(data.ID)),
pushName: data.PushName,
lid: jidNormalizedUser(data.LID),
};
// @ts-ignore
this.me.jid = data.ID;
@@ -387,7 +397,11 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
const all$ = this.all$;
this.events2.get(WAHAEvents.ENGINE_EVENT).switch(all$);
const messages$ = all$.pipe(onlyEvent(WhatsMeowEvent.MESSAGE));
const messages$ = all$.pipe(
onlyEvent(WhatsMeowEvent.MESSAGE),
filter((msg: any) => this.jids.include(msg?.Info?.Chat)),
share(),
);
let [messagesFromMe$, messagesFromOthers$] = partition(messages$, isMine);
messagesFromMe$ = messagesFromMe$.pipe(
@@ -444,7 +458,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
const waMessage = await this.toWAMessage(message);
// Extract the body from editedMessage using extractBody function
const body =
this.extractBody(message.Message.protocolMessage.editedMessage) || '';
extractBody(message.Message.protocolMessage.editedMessage) || '';
// Extract the original message ID from protocolMessage.key
const editedMessageId = message.Message.protocolMessage.key?.ID;
return {
@@ -457,7 +471,10 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
);
this.events2.get(WAHAEvents.MESSAGE_EDITED).switch(messagesEdited$);
const receipt$ = all$.pipe(onlyEvent(WhatsMeowEvent.RECEIPT));
const receipt$ = all$.pipe(
onlyEvent(WhatsMeowEvent.RECEIPT),
filter((r: any) => this.jids.include(r?.Chat)),
);
const messageAck$ = receipt$.pipe(
mergeMap(this.receiptToMessageAck.bind(this)),
DistinctAck(),
@@ -472,9 +489,13 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
const presence$ = all$.pipe(
onlyEvent(WhatsMeowEvent.PRESENCE),
filter((event: any) => this.jids.include(event?.From)),
filter((event) => !isJidGroup(event.From)),
);
const chatPresence$ = all$.pipe(onlyEvent(WhatsMeowEvent.CHAT_PRESENCE));
const chatPresence$ = all$.pipe(
onlyEvent(WhatsMeowEvent.CHAT_PRESENCE),
filter((event: any) => this.jids.include(event?.Chat)),
);
const presenceUpdates$ = merge(presence$, chatPresence$).pipe(
map((event) => this.toWahaPresences(event.From || event.Chat, [event])),
);
@@ -534,8 +555,10 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
//
const pollVoteEvent$ = all$.pipe(
onlyEvent(WhatsMeowEvent.POLL_VOTE_EVENT),
filter((event: any) => this.jids.include(event?.Info?.Chat)),
map(this.toPollVotePayload.bind(this)),
filter(Boolean),
share(),
);
// Split into successful and failed responses
@@ -552,6 +575,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
//
const eventMessageResponse$ = all$.pipe(
onlyEvent(WhatsMeowEvent.EVENT_MESSAGE_RESPONSE),
filter((event: any) => this.jids.include(event?.Info?.Chat)),
map(this.toEventResponsePayload.bind(this)),
filter(Boolean),
);
@@ -763,7 +787,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
async sendContactVCard(request: MessageContactVcardRequest) {
const jid = toJID(this.ensureSuffix(request.chatId));
const contacts = request.contacts.map((el) => ({ vcard: toVcard(el) }));
const contacts = request.contacts.map((el) => ({ vcard: toVcardV3(el) }));
const message = new messages.MessageRequest({
jid: jid,
session: this.session,
@@ -792,6 +816,14 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
return this.messageResponse(jid, data);
}
sendPollVote(request: MessagePollVoteRequest) {
throw new AvailableInPlusVersion('Poll voting');
}
sendList(request: SendListRequest): Promise<any> {
throw new AvailableInPlusVersion();
}
public async deleteMessage(chatId: string, messageId: string) {
const jid = toJID(this.ensureSuffix(chatId));
const key = parseMessageIdSerialized(messageId);
@@ -881,8 +913,21 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
};
}
sendLocation(request: MessageLocationRequest) {
throw new Error('Method not implemented.');
async sendLocation(request: MessageLocationRequest) {
const jid = toJID(this.ensureSuffix(request.chatId));
const message = new messages.MessageRequest({
jid: jid,
session: this.session,
replyTo: getMessageIdFromSerialized(request.reply_to),
location: new messages.Location({
name: request.title,
degreesLatitude: request.latitude,
degreesLongitude: request.longitude,
}),
});
const response = await promisify(this.client.SendMessage)(message);
const data = response.toObject();
return this.messageResponse(jid, data);
}
forwardMessage(request: MessageForwardRequest): Promise<WAMessage> {
@@ -1815,6 +1860,17 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
return labels.map(this.toLabel);
}
public async chatsUnreadChat(chatId: string): Promise<any> {
const jid = toJID(this.ensureSuffix(chatId));
const request = new messages.ChatUnreadRequest({
session: this.session,
jid: jid,
read: false,
});
await promisify(this.client.MarkChatUnread)(request);
return { success: true };
}
public async putLabelsToChat(chatId: string, labels: LabelID[]) {
const jid = toJID(chatId);
const labelsIds = labels.map((label) => label.id);
@@ -1844,7 +1900,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
// END - Methods for API
//
protected async processIncomingMessage(message, downloadMedia = true) {
protected shouldProcessIncomingMessage(message): boolean {
// if there is no text or media message
if (!message) return;
if (!message.Message) return;
@@ -1858,32 +1914,58 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
if (message.Message.protocolMessage) return;
const normalizedContent = normalizeMessageContent(message.Message);
const hasSomeContent = !!getContentType(normalizedContent);
const contentType = getContentType(normalizedContent);
// Ignore device sent message
if (contentType == 'deviceSentMessage') {
return;
}
const hasSomeContent = !!contentType;
if (!hasSomeContent) {
// Ignore key distribution messages
if (message?.Message?.senderKeyDistributionMessage) return;
}
if (downloadMedia) {
try {
message = await this.downloadMedia(message);
} catch (e) {
this.logger.error('Failed when tried to download media for a message');
this.logger.error(e, e.stack);
}
}
return this.toWAMessage(message);
return true;
}
protected downloadMedia(message) {
protected async processIncomingMessage(message, downloadMedia = true) {
// Filter
if (!this.shouldProcessIncomingMessage(message)) {
return null;
}
// Convert
const wamessage = this.toWAMessage(message);
// Media
if (downloadMedia) {
const media = await this.downloadMediaSafe(message);
wamessage.media = media;
}
return wamessage;
}
protected async downloadMediaSafe(message) {
try {
return await this.downloadMedia(message);
} catch (e) {
this.logger.error('Failed when tried to download media for a message');
this.logger.error(e, e.stack);
return null;
}
}
protected async downloadMedia(message) {
const processor = new GOWSEngineMediaProcessor(this);
return this.mediaManager.processMedia(processor, message, this.name);
const media = await this.mediaManager.processMedia(
processor,
message,
this.name,
);
return media;
}
protected toWAMessage(message): WAMessage {
const fromToParticipant = getFromToParticipant(message);
const id = buildMessageId(message);
const body = this.extractBody(message.Message);
const body = extractBody(message.Message);
const replyTo = this.extractReplyTo(message.Message);
let ack = statusToAck(message.Status);
if (ack === WAMessageAck.ERROR) {
@@ -1905,7 +1987,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
participant: toCusFormat(fromToParticipant.participant),
// Media
hasMedia: Boolean(mediaContent),
media: message.media || null,
media: null,
mediaUrl: message.media?.url,
// @ts-ignore
ack: ack,
@@ -1919,12 +2001,19 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
private toPollVotePayload(event: any): PollVotePayload {
// Extract event creation message key from the message
const creationKey = event.Message?.pollUpdateMessage.pollCreationMessageKey;
const key: WAMessageKey = {
const pollKey: IMessageKey = {
remoteJid: creationKey.remoteJID,
fromMe: creationKey.fromMe,
id: creationKey.ID,
participant: creationKey.participant,
};
const voteKey: IMessageKey = {
id: event.Info.ID,
remoteJid: event.Info.Chat,
participant: event.IsGroup ? event.Info.Sender : null,
fromMe: event.IsFromMe,
};
const key = fixPollCreationKey(voteKey, pollKey, this.me);
const fromToParticipant = getFromToParticipant(event);
const pollCreationKey = getDestination(key);
return {
@@ -1984,21 +2073,6 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
return sentDeviceId === myDeviceId ? MessageSource.API : MessageSource.APP;
}
private extractBody(message) {
if (!message) {
return null;
}
let body = message.Conversation || message.conversation;
if (!body) {
body = message.extendedTextMessage?.text;
}
if (!body) {
const media = extractMediaContent(message);
body = media?.caption;
}
return body;
}
protected extractReplyTo(message): ReplyToMessage | null {
const msgType = getContentType(message);
const contextInfo = message[msgType]?.contextInfo;
@@ -2009,7 +2083,7 @@ export class WhatsappSessionGoWSCore extends WhatsappSession {
if (!quotedMessage) {
return null;
}
const body = this.extractBody(quotedMessage);
const body = extractBody(quotedMessage);
return {
id: contextInfo.stanzaID,
participant: toCusFormat(contextInfo.participant),
@@ -2191,6 +2265,7 @@ function getFromToParticipant(message) {
from: info.Chat,
to: info.IsGroup ? info.Sender : null,
participant: info.IsGroup ? info.Sender : null,
fromMe: info.IsFromMe,
};
}
@@ -2243,3 +2318,63 @@ export function getMessageIdFromSerialized(serialized: string): string | null {
const key = parseMessageIdSerialized(serialized, true);
return key.id;
}
/**
* Poll creation keys are a bit tricky — they contain the data that the receiver uses.
* When recipients respond to a poll, they send back THEIR `message.id`.
*
* For example,
* - If we send a poll and in our system the ID is "true_{chatId}_{ID}",
* - The corresponding `pollCreationKey` will be "false_{ourId}_{ID}" —
* essentially the opposite of our message ID.
*
* The function inspects the key and,
* if it originates from us (based on `remoteJid` or `participant`),
* adjusts it to the correct value.
*/
function fixPollCreationKey(
vote: IMessageKey,
poll: IMessageKey,
me: MeInfo,
): IMessageKey {
// If the vote is from me, the pollCreationKey is already in my perspective
if (vote?.fromMe) {
return poll;
}
if (!me) {
return poll;
}
if (!poll) {
return poll;
}
// DM - my poll, not my vote
if (
toCusFormat(poll.remoteJid) == toCusFormat(me.id) ||
toCusFormat(poll.remoteJid) == toCusFormat(me.lid)
) {
return {
id: poll.id,
remoteJid: vote.remoteJid,
fromMe: true,
participant: undefined,
};
}
// Many Participants Chat - my poll, not my vote
if (
toCusFormat(poll.participant) == toCusFormat(me.id) ||
toCusFormat(poll.participant) == toCusFormat(me.lid)
) {
return {
id: poll.id,
remoteJid: vote.remoteJid,
fromMe: true,
participant: me.id,
};
}
// Other
return poll;
}
+90 -24
View File
@@ -16,7 +16,6 @@ import makeWASocket, {
jidNormalizedUser,
makeCacheableSignalKeyStore,
MiscMessageGenerationOptions,
NewsletterMetadata,
normalizeMessageContent,
PresenceData,
proto,
@@ -66,7 +65,7 @@ import {
AvailableInPlusVersion,
NotImplementedByEngineError,
} from '@waha/core/exceptions';
import { toVcard } from '@waha/core/helpers';
import { toVcardV3 } from '@waha/core/vcard';
import { createAgentProxy } from '@waha/core/helpers.proxy';
import { IMediaEngineProcessor } from '@waha/core/media/IMediaEngineProcessor';
import { QR } from '@waha/core/QR';
@@ -121,6 +120,7 @@ import {
SendSeenRequest,
WANumberExistResult,
} from '@waha/structures/chatting.dto';
import { SendListRequest } from '@waha/structures/chatting.list.dto';
import {
ContactQuery,
ContactRequest,
@@ -148,6 +148,7 @@ import {
LabelID,
} from '@waha/structures/labels.dto';
import { LidToPhoneNumber } from '@waha/structures/lids.dto';
import { WAMedia } from '@waha/structures/media.dto';
import { ReplyToMessage } from '@waha/structures/message.dto';
import { PaginationParams } from '@waha/structures/pagination.dto';
import {
@@ -384,6 +385,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
this.store = new NowebPersistentStore(
this.loggerBuilder.child({ name: NowebPersistentStore.name }),
storage,
this.jids,
);
await this.store.init();
}
@@ -735,6 +737,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return {
id: toCusFormat(meId),
pushName: me.name,
lid: me.lid,
};
}
@@ -879,7 +882,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
async sendContactVCard(request: MessageContactVcardRequest) {
const chatId = toJID(this.ensureSuffix(request.chatId));
const contacts = request.contacts.map((el) => ({ vcard: toVcard(el) }));
const contacts = request.contacts.map((el) => ({ vcard: toVcardV3(el) }));
const options = await this.getMessageOptions(request);
const msg = { contacts: { contacts: contacts } };
return await this.sock.sendMessage(chatId, msg, options);
@@ -952,15 +955,21 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
);
}
sendList(request: SendListRequest): Promise<any> {
throw new AvailableInPlusVersion();
}
async sendLocation(request: MessageLocationRequest) {
const chatId = toJID(this.ensureSuffix(request.chatId));
const msg = {
location: {
name: request.title || null,
degreesLatitude: request.latitude,
degreesLongitude: request.longitude,
},
};
const options = await this.getMessageOptions(request);
return await this.sock.sendMessage(request.chatId, msg, options);
return await this.sock.sendMessage(chatId, msg, options);
}
async forwardMessage(request: MessageForwardRequest): Promise<WAMessage> {
@@ -1768,7 +1777,10 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
public async channelsList(query: ListChannelsQuery): Promise<Channel[]> {
const newsletters = await this.sock.newsletterSubscribed();
let channels = newsletters.map(toNewsletterMetadata).map(this.toChannel);
let channels = newsletters
.map(toNewsletterMetadata)
.filter(Boolean)
.map(this.toChannel);
if (query.role) {
// @ts-ignore
channels = channels.filter((channel) => channel.role === query.role);
@@ -1835,6 +1847,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
const messagesUpsert$ = fromEvent(this.sock.ev, 'messages.upsert').pipe(
map((event: BaileysEventMap['messages.upsert']) => event.messages),
mergeAll(),
filter((msg) => this.jids.include(msg.key.remoteJid)),
share(),
);
let [messagesFromMe$, messagesFromOthers$] = partition(
@@ -1861,7 +1874,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
proto.Message.ProtocolMessage.Type.REVOKE,
),
mergeMap(async (message): Promise<WAMessageRevokedBody> => {
const afterMessage = await this.toWAMessage(message);
const afterMessage = this.toWAMessage(message);
// Extract the revoked message ID from protocolMessage.key
const revokedMessageId = message.message.protocolMessage.key?.id;
return {
@@ -1885,7 +1898,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
message.message?.protocolMessage?.editedMessage !== undefined,
),
mergeMap(async (message): Promise<WAMessageEditedBody> => {
const waMessage = await this.toWAMessage(message);
const waMessage = this.toWAMessage(message);
// Extract the body from editedMessage using extractBody function
const body =
extractBody(message.message.protocolMessage.editedMessage) || '';
@@ -1919,6 +1932,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
).pipe(
// @ts-ignore
mergeAll(),
filter((update) => this.jids.include(update.key.remoteJid)),
share(),
);
const messageAckDirect$ = messageUpdates$.pipe(
@@ -1930,6 +1944,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
fromEvent(this.sock.ev, 'message-receipt.update').pipe(
// @ts-ignore
mergeAll(),
filter((update) => this.jids.include(update.key.remoteJid)),
share(),
);
@@ -1997,6 +2012,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
this.events2.get(WAHAEvents.PRESENCE_UPDATE).switch(
fromEvent(this.sock.ev, 'presence.update').pipe(
filter((presence: any) => this.jids.include(presence.id)),
map((data: any) => this.toWahaPresences(data.id, data.presences)),
share(),
),
@@ -2027,7 +2043,13 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
//
// @ts-ignore
const calls$: Observable<WACallEvent[]> = fromEvent(this.sock.ev, 'call');
const call$ = calls$.pipe(mergeMap(identity), share());
const call$ = calls$.pipe(
mergeMap(identity),
filter((call: WACallEvent) =>
this.jids.include(call.groupJid || call.chatId),
),
share(),
);
this.events2.get(WAHAEvents.CALL_RECEIVED).switch(
call$.pipe(
filter((call: WACallEvent) => call.status === 'offer'),
@@ -2160,7 +2182,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return reaction;
}
protected async processIncomingMessage(message, downloadMedia = true) {
shouldProcessIncomingMessage(message): boolean {
// if there is no text or media message
if (!message) return;
if (!message.message) return;
@@ -2189,32 +2211,51 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return;
const normalizedContent = normalizeMessageContent(message.message);
const hasSomeContent = !!getContentType(normalizedContent);
const contentType = getContentType(normalizedContent);
// Ignore device sent message
if (contentType == 'deviceSentMessage') {
return;
}
const hasSomeContent = !!contentType;
if (!hasSomeContent) {
// Ignore key distribution messages
if (message?.message?.senderKeyDistributionMessage) return;
}
return true;
}
if (downloadMedia) {
try {
message = await this.downloadMedia(message);
} catch (e) {
this.logger.error('Failed when tried to download media for a message');
this.logger.error(e, e.stack);
}
protected async processIncomingMessage(
message,
downloadMedia: boolean,
): Promise<WAMessage | null> {
// Filter
if (!this.shouldProcessIncomingMessage(message)) {
return null;
}
// Convert
const wamessage = this.toWAMessageSafe(message);
if (!wamessage) {
return null;
}
// Media
if (downloadMedia) {
const media = await this.downloadMediaSafe(message);
wamessage.media = media;
}
return wamessage;
}
protected toWAMessageSafe(message): WAMessage | null {
try {
return await this.toWAMessage(message);
return this.toWAMessage(message);
} catch (error) {
this.logger.error('Failed to process incoming message');
this.logger.error(error);
console.trace(error);
return null;
}
}
protected toWAMessage(message): Promise<WAMessage> {
protected toWAMessage(message): WAMessage {
const fromToParticipant = getFromToParticipant(message.key);
const id = buildMessageId(message.key);
const body = extractBody(message.message);
@@ -2222,7 +2263,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);
return Promise.resolve({
return {
id: id,
timestamp: ensureNumber(message.messageTimestamp),
from: toCusFormat(fromToParticipant.from),
@@ -2233,7 +2274,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
participant: toCusFormat(fromToParticipant.participant),
// Media
hasMedia: Boolean(mediaContent),
media: message.media || null,
media: null,
mediaUrl: message.media?.url,
// @ts-ignore
ack: ack,
@@ -2243,7 +2284,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
vCards: message.vCards,
replyTo: replyTo,
_data: message,
});
};
}
protected extractReplyTo(message): ReplyToMessage | null {
@@ -2435,7 +2476,17 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return { id: chatId, presences: presences };
}
protected downloadMedia(message) {
protected async downloadMediaSafe(message): Promise<WAMedia | null> {
try {
return await this.downloadMedia(message);
} catch (e) {
this.logger.error('Failed when tried to download media for a message');
this.logger.error(e, e.stack);
}
return null;
}
protected async downloadMedia(message): Promise<WAMedia | null> {
const processor = new NOWEBEngineMediaProcessor(this, this.loggerBuilder);
return this.mediaManager.processMedia(processor, message, this.name);
}
@@ -2699,5 +2750,20 @@ export function extractBody(message): string | null {
if (!body) {
body = content.buttonsResponseMessage?.selectedDisplayText;
}
// List message
if (!body) {
const type = getContentType(content);
if (type == 'listMessage') {
const list = content.listMessage;
const parts = [list.title, list.description, list.footerText];
body = parts.filter(Boolean).join('\n');
} else if (type === 'listResponseMessage') {
const response = content.listResponseMessage;
const parts = [response.title, response.description];
body = parts.filter(Boolean).join('\n');
}
}
return body;
}
@@ -14,6 +14,7 @@ import makeWASocket, {
updateMessageWithReceipt,
WAMessage,
} from '@adiwajshing/baileys';
import { WACallEvent } from '@adiwajshing/baileys/lib/Types/Call';
import { GroupMetadata } from '@adiwajshing/baileys/lib/Types/GroupMetadata';
import { Label } from '@adiwajshing/baileys/lib/Types/Label';
import {
@@ -24,6 +25,7 @@ import { isLidUser } from '@adiwajshing/baileys/lib/WABinary/jid-utils';
import { IGroupRepository } from '@waha/core/engines/noweb/store/IGroupRepository';
import { ILabelAssociationRepository } from '@waha/core/engines/noweb/store/ILabelAssociationsRepository';
import { ILabelsRepository } from '@waha/core/engines/noweb/store/ILabelsRepository';
import { JidFilter } from '@waha/core/utils/jids';
import {
GetChatMessagesFilter,
OverviewFilter,
@@ -39,6 +41,7 @@ import { waitUntil } from '@waha/utils/promiseTimeout';
import * as lodash from 'lodash';
import { toNumber } from 'lodash';
import { Logger } from 'pino';
import { filter } from 'rxjs';
import { IChatRepository } from './IChatRepository';
import { IContactRepository } from './IContactRepository';
@@ -82,6 +85,7 @@ export class NowebPersistentStore implements INowebStore {
constructor(
private logger: Logger,
public storage: INowebStorage,
private jids: JidFilter,
) {
this.socket = null;
this.chatRepo = storage.getChatRepository();
@@ -247,6 +251,7 @@ export class NowebPersistentStore implements INowebStore {
private async syncMessagesHistory(messages) {
const realMessages = messages.filter(isRealMessage);
messages = messages.filter((msg) => this.jids.include(msg.key.remoteJid));
await this.messagesRepo.upsert(realMessages);
this.logger.info(
`history sync - '${messages.length}' got messages, '${realMessages.length}' real messages`,
@@ -254,11 +259,13 @@ export class NowebPersistentStore implements INowebStore {
}
private async onMessagesUpsert(update) {
const { messages, type } = update;
const type = update.type;
if (type !== 'notify' && type !== 'append') {
this.logger.debug(`unexpected type for messages.upsert: '${type}'`);
return;
}
let messages = update.messages;
messages = messages.filter((msg) => this.jids.include(msg.key.remoteJid));
const realMessages = messages.filter(isRealMessage);
await this.messagesRepo.upsert(realMessages);
this.logger.debug(
@@ -270,6 +277,9 @@ export class NowebPersistentStore implements INowebStore {
for (const update of updates) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const jid = jidNormalizedUser(update.key.remoteJid!);
if (!this.jids.include(jid)) {
continue;
}
if (!update.key.id) {
continue;
}
@@ -333,12 +343,16 @@ export class NowebPersistentStore implements INowebStore {
delete chat['messages'];
chat.conversationTimestamp = toNumber(chat.conversationTimestamp) || null;
}
chats = chats.filter((chat) => this.jids.include(chat.id));
await this.chatRepo.upsertMany(chats);
this.logger.info(`store sync - '${chats.length}' synced chats`);
}
private async onGroupUpsert(groups: GroupMetadata[]) {
for (const group of groups) {
if (!this.jids.include(group.id)) {
continue;
}
await this.groupRepo.save(group);
}
this.logger.info(`store sync - '${groups.length}' synced groups`);
@@ -346,6 +360,9 @@ export class NowebPersistentStore implements INowebStore {
private async onGroupUpdate(groups: Partial<GroupMetadata>[]) {
for (const update of groups) {
if (!this.jids.include(update.id)) {
continue;
}
let group = await this.groupRepo.getById(update.id);
group = Object.assign(group || {}, update) as GroupMetadata;
await this.groupRepo.save(group);
@@ -356,6 +373,9 @@ export class NowebPersistentStore implements INowebStore {
private async onGroupParticipantsUpdate(data) {
const id: string = data.id;
if (!this.jids.include(id)) {
return;
}
const participants: string[] = data.participants;
const action: ParticipantAction = data.action;
@@ -419,6 +439,9 @@ export class NowebPersistentStore implements INowebStore {
private async onChatUpdate(updates: ChatUpdate[]) {
for (const update of updates) {
if (!this.jids.include(update.id)) {
continue;
}
const chat = (await this.chatRepo.getById(update.id)) || ({} as Chat);
Object.assign(chat, update);
chat.conversationTimestamp = toNumber(chat.conversationTimestamp) || null;
@@ -447,6 +470,9 @@ export class NowebPersistentStore implements INowebStore {
const ids = contacts.map((c) => c.id);
const contactById = await this.contactRepo.getEntitiesByIds(ids);
for (const update of contacts) {
if (!this.jids.include(update.id)) {
continue;
}
const contact = contactById.get(update.id) || {};
// remove undefined from data
Object.keys(update).forEach(
@@ -460,6 +486,9 @@ export class NowebPersistentStore implements INowebStore {
private async onContactUpdate(updates: Partial<Contact>[]) {
for (const update of updates) {
if (!this.jids.include(update.id)) {
continue;
}
let contact = await this.contactRepo.getById(update.id);
if (!contact) {
@@ -493,6 +522,9 @@ export class NowebPersistentStore implements INowebStore {
private async onMessageReaction(reactions) {
for (const { key, reaction } of reactions) {
if (!this.jids.include(key.remoteJid)) {
continue;
}
const msg = await this.messagesRepo.getByJidById(key.remoteJid, key.id);
if (!msg) {
this.logger.warn(
@@ -509,6 +541,9 @@ export class NowebPersistentStore implements INowebStore {
private async onMessageReceiptUpdate(updates) {
for (const { key, receipt } of updates) {
if (!this.jids.include(key.remoteJid)) {
continue;
}
const msg = await this.messagesRepo.getByJidById(key.remoteJid, key.id);
if (!msg) {
this.logger.warn(
@@ -544,6 +579,9 @@ export class NowebPersistentStore implements INowebStore {
}
private async onPresenceUpdate({ id, presences: update }) {
if (!this.jids.include(id)) {
return;
}
this.presences[id] = this.presences[id] || {};
Object.assign(this.presences[id], update);
}
@@ -446,7 +446,7 @@ export default (config: BaileysInMemoryStoreConfig) => {
) => {
const list = assertMessageList(jid);
const mode = !cursor || 'before' in cursor ? 'before' : 'after';
const cursorKey = !!cursor
const cursorKey = cursor
? 'before' in cursor
? cursor.before
: cursor.after
+30 -1
View File
@@ -30,7 +30,10 @@ export class WebjsClientCore extends Client {
public events = new EventEmitter();
private wpage: WPage = null;
constructor(options) {
constructor(
options,
protected tags: boolean,
) {
super(options);
// Wait until it's READY and inject more utils
this.on(Events.READY, async () => {
@@ -86,6 +89,32 @@ export class WebjsClientCore extends Client {
return;
},
);
if (this.tags) {
await this.attachTagsEvents();
}
}
async attachTagsEvents() {
await this.pupPage.evaluate(() => {
// @ts-ignore
if (window.decodeStanzaBack) {
return;
}
const tags = ['receipt', 'presence', 'chatstate'];
// @ts-ignore
window.decodeStanzaBack = window.Store.SocketWap.decodeStanza;
// @ts-ignore
window.Store.SocketWap.decodeStanza = async (...args) => {
// @ts-ignore
const result = await window.decodeStanzaBack(...args);
if (tags.includes(result?.tag)) {
// @ts-ignore
setTimeout(() => window.onTag(result), 0);
}
return result;
};
});
}
async destroy() {
+3 -3
View File
@@ -49,7 +49,7 @@ export function TagReceiptNodeToReceiptEvent(
const { attrs, content } = node;
const status = getStatusFromReceiptType(attrs.type);
if (status == null) {
return null;
return [];
}
const from = jidNormalizedUser(jid(attrs.from));
@@ -118,14 +118,14 @@ function handleGroupedReceipts(
fromMe: boolean,
isLid: boolean,
me: Me,
): ReceiptEvent[] | null {
): ReceiptEvent[] {
const { content } = node;
if (!Array.isArray(content)) {
return [];
}
const participantsTags = content.filter((c) => c.tag === 'participants');
if (participantsTags.length === 0) {
return null;
return [];
}
const receiptEvents: ReceiptEvent[] = [];
+50 -13
View File
@@ -103,6 +103,7 @@ import {
} from '@waha/structures/groups.dto';
import { Label, LabelDTO, LabelID } from '@waha/structures/labels.dto';
import { LidToPhoneNumber } from '@waha/structures/lids.dto';
import { WAMedia } from '@waha/structures/media.dto';
import { ReplyToMessage } from '@waha/structures/message.dto';
import { PaginationParams, SortOrder } from '@waha/structures/pagination.dto';
import {
@@ -152,6 +153,7 @@ import { WAJSPresenceChatStateType, WebJSPresence } from './types';
export interface WebJSConfig {
webVersion?: string;
cacheType: 'local' | 'none';
puppeteerArgs: string[];
}
export class WhatsappSessionWebJSCore extends WhatsappSession {
@@ -203,6 +205,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
this.logger.info(`Using web version: '${webVersion}'`);
}
const args = this.getBrowserArgsForPuppeteer();
args.push(...this.engineConfig.puppeteerArgs);
// add at the start
args.unshift(`--a-waha-timestamp=${new Date()}`);
args.unshift(`--a-waha-session=${this.name}`);
@@ -235,7 +238,14 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
rmMaxRetries: undefined,
});
this.addProxyConfig(clientOptions);
return new WebjsClientCore(clientOptions);
return new WebjsClientCore(clientOptions, this.getWebjsTagsFlag());
}
protected getWebjsTagsFlag() {
// Emit 'tag:*' events only when explicitly enabled in session config.
// This flag is required for presence.update and message.ack events.
// Disabled by default for performance and stability reasons.
return !!this.sessionConfig?.webjs?.tagsEventsOn;
}
private restartClient() {
@@ -783,7 +793,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
chat.id._serialized,
false,
);
const lastMessage = !!chat.lastMessage
const lastMessage = chat.lastMessage
? this.toWAMessage(chat.lastMessage)
: null;
return {
@@ -1439,6 +1449,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
//
const messageReceived$ = fromEvent(this.whatsapp, Events.MESSAGE_RECEIVED);
const messagesFromOthers$ = messageReceived$.pipe(
filter((msg: Message) => this.jids.include(msg?.id?.remote)),
mergeMap((msg: any) => this.processIncomingMessage(msg, true)),
share(),
);
@@ -1446,6 +1457,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
const messageCreate$ = fromEvent(this.whatsapp, Events.MESSAGE_CREATE);
const messagesFromAll$ = messageCreate$.pipe(
filter((msg: Message) => this.jids.include(msg?.id?.remote)),
mergeMap((msg: any) => this.processIncomingMessage(msg, true)),
share(),
);
@@ -1456,6 +1468,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
Events.MESSAGE_CIPHERTEXT,
);
const messagesWaiting$ = messageCiphertext$.pipe(
filter((msg: Message) => this.jids.include(msg?.id?.remote)),
mergeMap((msg: any) => this.processIncomingMessage(msg, false)),
share(),
);
@@ -1469,6 +1482,9 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
},
);
const messagesRevoked$ = messageRevoked$.pipe(
filter((evt: any) =>
this.jids.include(evt?.after?.id?.remote || evt?.before?.id?.remote),
),
map((event): WAMessageRevokedBody => {
const afterMessage = event.after ? this.toWAMessage(event.after) : null;
const beforeMessage = event.before
@@ -1487,6 +1503,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
const messageReaction$ = fromEvent(this.whatsapp, 'message_reaction');
const messagesReaction$ = messageReaction$.pipe(
filter((reaction: Reaction) => this.jids.include(reaction?.id?.remote)),
map(this.processMessageReaction.bind(this)),
filter(Boolean),
);
@@ -1500,6 +1517,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
},
);
const messagesEdit$ = messageEdit$.pipe(
filter((event: any) => this.jids.include(event?.message?.id?.remote)),
map((event): WAMessageEditedBody => {
const message = this.toWAMessage(event.message);
return {
@@ -1523,6 +1541,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
map((event) => event.message),
map<any, WAMessage>(this.toWAMessage.bind(this)),
filter((ack) => !isJidGroup(ack.to) && !isJidStatusBroadcast(ack.to)),
filter((ack) => this.jids.include(ack.to)),
);
const tagReceiptNode$ = fromEvent(this.whatsapp, Events.TAG_RECEIPT);
const messageAckGroups$ = tagReceiptNode$.pipe(
@@ -1532,6 +1551,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
filter(Boolean),
mergeMap(this.TagReceiptToMessageAck.bind(this)),
filter((ack) => isJidGroup(ack.to) || isJidStatusBroadcast(ack.to)),
filter((ack) => this.jids.include(ack.to)),
);
const messageAckAll$ = merge(messagesAckDM$, messageAckGroups$);
@@ -1552,11 +1572,13 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
const presences$ = tagPresenceNode$.pipe(
map(TagPresenceToPresence),
filter(Boolean),
filter((presence: any) => this.jids.include(presence.id)),
);
const tagChatstateNode$ = fromEvent(this.whatsapp, 'tag:chatstate');
const chatstatePresences$ = tagChatstateNode$.pipe(
map(TagChatstateToPresence),
filter(Boolean),
filter((presence: any) => this.jids.include(presence.id)),
);
const presenceUpdate$ = merge(presences$, chatstatePresences$);
this.events2.get(WAHAEvents.PRESENCE_UPDATE).switch(presenceUpdate$);
@@ -1625,6 +1647,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
},
);
const chatsArchived$ = chatArchived$.pipe(
filter((event: any) => this.jids.include(event?.chat?.id?._serialized)),
map((event) => {
return {
id: event.chat.id._serialized,
@@ -1640,6 +1663,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
//
const call$ = fromEvent(this.whatsapp, 'call');
const calls$ = call$.pipe(
filter((call: Call) => this.jids.include((call as any)?.from)),
map((call: Call) => {
return {
id: call.id,
@@ -1657,15 +1681,14 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
message: Message,
downloadMedia = true,
) {
// Convert
const wamessage = this.toWAMessage(message);
// Media
if (downloadMedia) {
try {
message = await this.downloadMedia(message);
} catch (e) {
this.logger.error('Failed when tried to download media for a message');
this.logger.error(e, e.stack);
}
const media = await this.downloadMediaSafe(message);
wamessage.media = media;
}
return this.toWAMessage(message);
return wamessage;
}
private processMessageReaction(reaction: Reaction): WAMessageReaction {
@@ -1737,8 +1760,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
// Media
// @ts-ignore
hasMedia: Boolean(message.hasMedia),
// @ts-ignore
media: message.media || null,
media: null,
// @ts-ignore
mediaUrl: message.media?.url,
// @ts-ignore
@@ -1781,9 +1803,24 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
return contact;
}
protected downloadMedia(message: Message) {
protected async downloadMediaSafe(message): Promise<WAMedia | null> {
try {
return await this.downloadMedia(message);
} catch (e) {
this.logger.error('Failed when tried to download media for a message');
this.logger.error(e, e.stack);
}
return null;
}
protected async downloadMedia(message: Message) {
const processor = new WEBJSEngineMediaProcessor();
return this.mediaManager.processMedia(processor, message, this.name);
const media = await this.mediaManager.processMedia(
processor,
message,
this.name,
);
return media;
}
protected getMessageOptions(request: any): any {
+5 -2
View File
@@ -2,12 +2,15 @@ import {
NotImplementedException,
UnprocessableEntityException,
} from '@nestjs/common';
import { getEngineName } from '@waha/config';
export const DOCS_URL = 'https://waha.devlike.pro/';
const engine = getEngineName();
export class NotImplementedByEngineError extends NotImplementedException {
constructor(msg = '') {
let error = 'The method is not implemented by the engine.';
let error = `The method is not implemented by '${engine}' engine. Check the docs and try another engine: ${DOCS_URL}`;
if (msg) {
error = `${msg} ${error}`;
}
@@ -18,7 +21,7 @@ export class NotImplementedByEngineError extends NotImplementedException {
export class AvailableInPlusVersion extends UnprocessableEntityException {
constructor(feature: string = 'The feature') {
super(
`${feature} is available only in Plus version. Check this out: ${DOCS_URL}`,
`${feature} is available only in Plus version for '${engine}' engine. Check this out: ${DOCS_URL}`,
);
}
}
-24
View File
@@ -1,24 +0,0 @@
import { Contact, VCardContact } from '@waha/structures/chatting.dto';
export function toVcard(data: Contact | VCardContact): string {
if (data.vcard) {
return data.vcard;
}
const contact: Contact = data as any;
const parts = [];
parts.push('BEGIN:VCARD');
parts.push('VERSION:3.0');
parts.push(`FN:${contact.fullName}`);
if (contact.organization) {
parts.push(`ORG:${contact.organization};`);
}
if (contact.whatsappId) {
parts.push(
`TEL;type=CELL;type=VOICE;waid=${contact.whatsappId}:${contact.phoneNumber}`,
);
} else {
parts.push(`TEL;type=CELL;type=VOICE:${contact.phoneNumber}`);
}
parts.push('END:VCARD');
return parts.join('\n');
}
+1
View File
@@ -200,6 +200,7 @@ export class SessionManagerCore extends SessionManager implements OnModuleInit {
sessionStore: this.store,
proxyConfig: proxyConfig,
sessionConfig: this.sessionConfig,
ignore: this.ignoreChatsConfig(this.sessionConfig),
};
if (this.EngineClass === WhatsappSessionWebJSCore) {
sessionConfig.engineConfig = this.webjsEngineConfigService.getConfig();
+2 -1
View File
@@ -1,4 +1,5 @@
import { IMediaEngineProcessor } from '@waha/core/media/IMediaEngineProcessor';
import { WAMedia } from '@waha/structures/media.dto';
/**
* General interface for MediaManager - one that handles the logic
@@ -9,7 +10,7 @@ interface IMediaManager {
processor: IMediaEngineProcessor<Message>,
message: Message,
session: string,
): Promise<Message>;
): Promise<WAMedia | null>;
close(): void;
}
+4 -6
View File
@@ -102,19 +102,19 @@ export class MediaManager implements IMediaManager {
processor: IMediaEngineProcessor<Message>,
message: Message,
session: string,
): Promise<Message> {
): Promise<WAMedia | null> {
let messageId: string;
try {
messageId = processor.getMessageId(message);
if (!processor.hasMedia(message)) {
return message;
return null;
}
} catch (error) {
this.log.error(
error,
`Error checking if message has media for message '${messageId}'`,
);
return message;
return null;
}
let media: WAMedia = {
@@ -134,9 +134,7 @@ export class MediaManager implements IMediaManager {
// @ts-ignore
media.error.details = `${err.stack}`;
}
// @ts-ignore
message.media = media;
return message;
return media;
}
private async fetchMedia(
+34 -2
View File
@@ -1,11 +1,15 @@
import { isJidGroup, isJidMetaIa } from '@adiwajshing/baileys';
import {
isJidGroup,
isJidMetaIa,
isJidStatusBroadcast,
} from '@adiwajshing/baileys';
import {
isJidBroadcast,
isLidUser,
} from '@adiwajshing/baileys/lib/WABinary/jid-utils';
export function isJidNewsletter(jid: string) {
return jid.endsWith('@newsletter');
return jid?.endsWith('@newsletter');
}
export function isJidCus(jid: string) {
@@ -35,3 +39,31 @@ export function toJID(chatId) {
const number = chatId.split('@')[0];
return number + '@s.whatsapp.net';
}
export interface IgnoreJidConfig {
status: boolean;
groups: boolean;
channels: boolean;
broadcast: boolean;
}
export class JidFilter {
constructor(public ignore: IgnoreJidConfig) {}
include(jid: string): boolean {
if (this.ignore.status && isJidStatusBroadcast(jid)) {
return false;
} else if (
this.ignore.broadcast &&
!isJidStatusBroadcast(jid) &&
isJidBroadcast(jid)
) {
return false;
} else if (this.ignore.groups && isJidGroup(jid)) {
return false;
} else if (this.ignore.channels && isJidNewsletter(jid)) {
return false;
}
return true;
}
}
+59
View File
@@ -0,0 +1,59 @@
import { Contact, VCardContact } from '@waha/structures/chatting.dto';
import * as ICAL from 'ical.js';
export function toVcardV3(data: Contact | VCardContact): string {
if (data.vcard) {
return data.vcard;
}
const contact: Contact = data as any;
const parts = [];
parts.push('BEGIN:VCARD');
parts.push('VERSION:3.0');
parts.push(`FN:${contact.fullName}`);
if (contact.organization) {
parts.push(`ORG:${contact.organization};`);
}
if (contact.whatsappId) {
parts.push(
`TEL;type=CELL;type=VOICE;waid=${contact.whatsappId}:${contact.phoneNumber}`,
);
} else {
parts.push(`TEL;type=CELL;type=VOICE:${contact.phoneNumber}`);
}
parts.push('END:VCARD');
return parts.join('\n');
}
export interface SimpleVCardInfo {
fullName: string;
phoneNumbers: string[];
}
export function parseVCardV3(vcardText: string): SimpleVCardInfo {
const parsed = (ICAL as any).parse(vcardText);
const comp = new (ICAL as any).Component(parsed);
// FN preferred, fallback to N
let fullName = comp.getFirstPropertyValue('fn') as string | undefined;
if (!fullName) {
const n = comp.getFirstPropertyValue('n') as string | undefined;
if (n) {
const [family, given, additional, prefix, suffix] = ('' + n).split(';');
fullName = [prefix, given, additional, family, suffix]
.filter(Boolean)
.join(' ')
.trim();
}
}
// Collect all TEL values
const phoneNumbers = comp
.getAllProperties('tel')
.map((p) => String(p.getFirstValue() ?? ''))
.filter((n) => n.length > 0);
return {
fullName: fullName || '',
phoneNumbers: phoneNumbers,
};
}
+6 -1
View File
@@ -18,7 +18,12 @@ export function serializeError(err: unknown) {
const properties = Object.getOwnPropertyNames(err);
// getOwnPropertyNames does not get 'name', like "ReferenceError"
properties.push('name');
return JSON.parse(JSON.stringify(err, properties));
const result = JSON.parse(JSON.stringify(err, properties));
// Put the "stack" field at the end, too wordy
const stack = result.stack;
delete result.stack;
result.stack = stack;
return result;
}
@Catch()
+33
View File
@@ -8,8 +8,10 @@ import { IsFileType } from '@waha/nestjs/validation/IsFileType';
import { GetChatMessagesQuery } from '@waha/structures/chats.dto';
import { plainToInstance, Transform, Type } from 'class-transformer';
import {
IsArray,
IsBoolean,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
IsUrl,
@@ -456,3 +458,34 @@ export class NewMessageIDResponse {
})
id: string;
}
export class MessagePollVoteRequest extends ChatRequest {
@ApiProperty({
description:
'The ID of the poll message. Format: {fromMe}_{chatID}_{messageId}[_{participant}] or just ID for GOWS',
example: 'false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA',
required: true,
})
@IsString()
@IsNotEmpty()
pollMessageId: string;
@ApiProperty({
description:
'Only for Channels - server message id (if known); if omitted, API may look it up in the storage',
required: false,
example: null,
})
@IsOptional()
@IsNumber()
pollServerId?: number;
@ApiProperty({
description: 'Poll options you are voting for',
example: 'Awesome!',
isArray: true,
})
@IsArray()
@IsString({ each: true })
votes: string[];
}
+141
View File
@@ -0,0 +1,141 @@
import { ApiExtraModels, ApiProperty } from '@nestjs/swagger';
import { ChatRequest } from '@waha/structures/chatting.dto';
import { ChatIdProperty } from '@waha/structures/properties.dto';
import { Type } from 'class-transformer';
import {
ArrayMinSize,
IsArray,
IsNotEmpty,
IsOptional,
IsString,
ValidateNested,
} from 'class-validator';
class Row {
@ApiProperty({ example: 'Option 1' })
@IsString()
@IsNotEmpty()
title: string;
@ApiProperty({ example: 'Description of option 1', required: false })
@IsOptional()
@IsString()
description?: string;
@ApiProperty({ example: 'option1' })
@IsString()
@IsNotEmpty()
rowId: string;
}
class Section {
@ApiProperty({ example: 'Menu' })
@IsString()
@IsNotEmpty()
title: string;
@ValidateNested({ each: true })
@Type(() => Row)
@IsArray()
@ArrayMinSize(1)
@ApiProperty({
example: [
{ title: 'Option 1', rowId: 'option1', description: 'First option' },
{ title: 'Option 2', rowId: 'option2', description: 'Second option' },
],
})
rows: Row[];
}
export class SendListMessage {
@ApiProperty({ example: 'Example List' })
@IsString()
@IsNotEmpty()
title: string;
@ApiProperty({ example: 'Choose one of the options', required: false })
@IsOptional()
@IsString()
description?: string;
@ApiProperty({ example: 'Footer note', required: false })
@IsOptional()
@IsString()
footer?: string;
@ApiProperty({ example: 'Select' })
@IsString()
@IsNotEmpty()
button: string;
@ValidateNested({ each: true })
@Type(() => Section)
@IsArray()
@ArrayMinSize(1)
@ApiProperty({
example: [
{
title: 'Section 1',
rows: [
{ title: 'Option 1', rowId: 'option1', description: 'Description 1' },
{ title: 'Option 2', rowId: 'option2', description: 'Description 2' },
],
},
],
})
sections: Section[];
}
@ApiExtraModels(SendListMessage)
export class SendListRequest extends ChatRequest {
@ChatIdProperty()
@IsString()
chatId: string;
@ValidateNested()
@Type(() => SendListMessage)
@ApiProperty({
type: SendListMessage,
example: {
title: 'Simple Menu',
description: 'Please choose an option',
footer: 'Thank you!',
button: 'Choose',
sections: [
{
title: 'Main',
rows: [
{
title: 'Option 1',
rowId: 'option1',
description: null,
},
{
title: 'Option 2',
rowId: 'option2',
description: null,
},
{
title: 'Option 3',
rowId: 'option3',
description: null,
},
],
},
],
},
})
message: SendListMessage;
@ApiProperty({
description:
'The ID of the message to reply to - false_11111111111@c.us_AAAAAAAAAAAAAAAAAAAA',
example: null,
required: false,
})
@IsOptional()
@IsString()
reply_to?: string;
}
export { Row, Section };
+75
View File
@@ -7,6 +7,7 @@ import {
IsBoolean,
IsOptional,
IsString,
Matches,
MaxLength,
ValidateNested,
} from 'class-validator';
@@ -88,6 +89,49 @@ export class NowebConfig {
markOnline: boolean = true;
}
export class WebjsConfig {
@ApiProperty({
description:
"Enable emission of special 'tag:*' engine events required for presence.update and message.ack.\n" +
'WARNING: Enabling this may have performance and stability impact. Disabled by default.',
required: false,
default: false,
})
@IsBoolean()
@IsOptional()
tagsEventsOn?: boolean = false;
}
export class IgnoreConfig {
@ApiProperty({
description: 'Ignore a status@broadcast (stories) events',
})
@IsBoolean()
@IsOptional()
status?: boolean;
@ApiProperty({
description: 'Ignore groups events',
})
@IsBoolean()
@IsOptional()
groups?: boolean;
@ApiProperty({
description: 'Ignore channels events',
})
@IsBoolean()
@IsOptional()
channels?: boolean;
@ApiProperty({
description: 'Ignore broadcast events (broadcast list and status)',
})
@IsBoolean()
@IsOptional()
broadcast?: boolean;
}
export class SessionConfig {
@ValidateNested({ each: true })
@Type(() => WebhookConfig)
@@ -124,6 +168,19 @@ export class SessionConfig {
@IsOptional()
debug?: boolean;
@ApiProperty({
example: {
status: null,
groups: null,
channels: null,
},
description: 'Ignore some events related to specific chats',
})
@ValidateNested()
@Type(() => IgnoreConfig)
@IsOptional()
ignore?: IgnoreConfig;
@ApiProperty({
example: {
store: {
@@ -136,6 +193,15 @@ export class SessionConfig {
@Type(() => NowebConfig)
@IsOptional()
noweb?: NowebConfig;
@ApiProperty({
description: 'WebJS-specific settings.',
required: false,
})
@ValidateNested()
@Type(() => WebjsConfig)
@IsOptional()
webjs?: WebjsConfig;
}
export class SessionDTO {
@@ -154,6 +220,11 @@ export class MeInfo {
@ChatIdProperty()
id: string;
@ApiProperty({
example: '123123@lid',
})
lid?: string;
pushName: string;
}
@@ -179,6 +250,10 @@ export class SessionCreateRequest {
@IsString()
@IsOptional()
@MaxLength(DB_NAME_LIMIT - DB_NAME_MAX_PREFIX_LEN)
@Matches(/^[a-zA-Z0-9_-]*$/, {
message:
'Session name can only contain alphanumeric characters, hyphens, and underscores (a-z, A-Z, 0-9, -, _) or be empty',
})
name: string | undefined;
@ValidateNested()
+5 -6
View File
@@ -1,4 +1,6 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
import { getEngineName } from '@waha/config';
import { getBrowserExecutablePath } from './core/abc/session.abc';
import { WAHAEngine } from './structures/enums.dto';
import { WAHAEnvironment } from './structures/environment.dto';
@@ -26,14 +28,9 @@ export function getWAHAVersion(): WAHAVersion {
return WAHAVersion.CORE;
}
export function getEngineName(): string {
// Load engine name from WHATSAPP_DEFAULT_ENGINE environment variable
// If not set - use WEBJS
return process.env.WHATSAPP_DEFAULT_ENGINE || WAHAEngine.WEBJS;
}
export const VERSION: WAHAEnvironment = {
version: '2025.7.10',
version: '2025.9.3',
engine: getEngineName(),
tier: getWAHAVersion(),
browser:
@@ -41,3 +38,5 @@ export const VERSION: WAHAEnvironment = {
};
export const IsChrome = VERSION.browser?.includes('chrome');
export { getEngineName };
Loaded 100 of 102 files, more files were not shown because too many files have changed in this diff. Show more