Compare commits

...
63 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
99 changed files with 6765 additions and 2705 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);
}
@@ -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 Array.from(LOCALE_NAMES.entries()).map(([locale, name]) => ({
name,
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 { 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,6 +52,14 @@ 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 = FindChatID(sender);
-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';
+1 -1
View File
@@ -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
+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,8 +1,9 @@
# Arabic translations for ChatWoot integration
LOCALE_NAME: '🇦🇪 العربية (Arabic)'
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}} ثانية...
@@ -19,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 هذا.
@@ -81,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
لنبدأ! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **غير متصل: جلسة واتساب من صندوق وارد ChatWoot**
❌ تم قطع اتصال جلسة واتساب **{{{name}}}** من صندوق وارد ChatWoot هذا.
@@ -90,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** - بدء الجلسة.
@@ -108,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}}}
@@ -154,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}}}**
@@ -171,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 تحقق مما هو جديد في [**🆕 سجل التغييرات**]({{{changelogUrl}}})
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **ادعم WAHA!**
أنت تستخدم إصدار **WAHA Core** المجاني. إذا وجدت هذا المشروع مفيدًا، فيرجى التفكير في دعم تطويره.
@@ -179,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,8 +1,9 @@
# Bengali translations for ChatWoot integration
LOCALE_NAME: '🇧🇩 বাংলা (Bengali)'
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}} সেকেন্ডের মধ্যে পুনরায় চেষ্টা করা হচ্ছে...
@@ -19,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 ইনবক্সের সাথে সংযুক্ত করা হয়েছে।
@@ -81,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
চলুন শুরু করা যাক! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **সংযোগ বিচ্ছিন্ন: হোয়াটসঅ্যাপ সেশন থেকে ChatWoot ইনবক্স**
❌ হোয়াটসঅ্যাপ সেশন **{{{name}}}** এই ChatWoot ইনবক্স থেকে সংযোগ বিচ্ছিন্ন করা হয়েছে।
@@ -90,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** - সেশন শুরু করুন।
@@ -108,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}}}
@@ -154,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}}}**
@@ -171,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 [**🆕 পরিবর্তন লগ**]({{{changelogUrl}}}) এ নতুন কি আছে তা দেখুন
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **WAHA সমর্থন করুন!**
আপনি বিনামূল্যের **WAHA Core** সংস্করণ ব্যবহার করছেন। যদি আপনি এই প্রকল্পটি উপযোগী মনে করেন, তবে এর বিকাশকে সমর্থন করার কথা বিবেচনা করুন।
@@ -179,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,8 +1,9 @@
# German translations for ChatWoot integration
LOCALE_NAME: '🇩🇪 Deutsch (German)'
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...
@@ -19,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.
@@ -84,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.
@@ -93,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.
@@ -111,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}}}
@@ -157,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}}}**
@@ -174,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.
@@ -182,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,9 +1,10 @@
# English translations for ChatWoot integration
LOCALE_NAME: '🇺🇸 English (English)'
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...
@@ -20,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.
@@ -82,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.
@@ -91,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.
@@ -109,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}}}
@@ -155,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}}}**
@@ -172,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,8 +1,9 @@
# Spanish translations for ChatWoot integration
LOCALE_NAME: '🇪🇸 Español (Spanish)'
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...
@@ -19,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.
@@ -81,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.
@@ -90,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.
@@ -108,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}}}
@@ -154,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}}}**
@@ -171,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.
@@ -179,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,8 +1,9 @@
# Persian translations for ChatWoot integration
LOCALE_NAME: '🇮🇷 فارسی (Persian)'
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}} ثانیه...
@@ -19,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 متصل شده است.
@@ -81,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
بیایید شروع کنیم! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **قطع اتصال: جلسه واتساپ از صندوق ورودی ChatWoot**
❌ جلسه واتساپ **{{{name}}}** از این صندوق ورودی ChatWoot قطع شده است.
@@ -90,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** - شروع جلسه.
@@ -108,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}}}
@@ -154,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}}}**
@@ -171,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 موارد جدید را در [**🆕 تغییرات**]({{{changelogUrl}}}) مشاهده کنید
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **از WAHA پشتیبانی کنید!**
شما از نسخه رایگان **WAHA Core** استفاده می‌کنید. اگر این پروژه را مفید می‌دانید، لطفاً حمایت از توسعه آن را در نظر بگیرید.
@@ -179,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,8 +1,9 @@
# French translations for ChatWoot integration
LOCALE_NAME: '🇫🇷 Français (French)'
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...
@@ -19,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.
@@ -81,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.
@@ -90,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.
@@ -108,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}}}
@@ -154,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}}}**
@@ -171,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.
@@ -179,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,8 +1,9 @@
# Hebrew translations for ChatWoot integration
LOCALE_NAME: '🇮🇱 עברית (Hebrew)'
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}} שניות...
@@ -19,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.
@@ -81,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
בוא נתחיל! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **מנותק: הפעלת וואטסאפ מתיבת הדואר הנכנס של ChatWoot**
❌ הפעלת וואטסאפ **{{{name}}}** נותקה מתיבת הדואר הנכנס של ChatWoot.
@@ -90,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** - התחל את ההפעלה.
@@ -108,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}}}
@@ -154,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}}}**
@@ -171,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 בדוק מה חדש ב[**🆕 יומן שינויים**]({{{changelogUrl}}})
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **תמוך ב-WAHA!**
אתה משתמש בגרסת **WAHA Core** החינמית. אם אתה מוצא פרויקט זה שימושי, אנא שקול לתמוך בפיתוח שלו.
@@ -179,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,8 +1,9 @@
# Hindi translations for ChatWoot integration
LOCALE_NAME: '🇮🇳 हिंदी (Hindi)'
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}} सेकंड में पुनः प्रयास कर रहा है...
@@ -19,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 इनबॉक्स से जुड़ गया है।
@@ -81,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
चलिए शुरू करते हैं! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **डिस्कनेक्टेड: व्हाट्सएप सत्र से ChatWoot इनबॉक्स**
❌ व्हाट्सएप सत्र **{{{name}}}** इस ChatWoot इनबॉक्स से डिस्कनेक्ट हो गया है।
@@ -90,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** - सत्र शुरू करें।
@@ -108,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}}}
@@ -154,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}}}**
@@ -171,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 [**🆕 चेंजलॉग**]({{{changelogUrl}}}) में क्या नया है देखें
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **WAHA का समर्थन करें!**
आप मुफ्त **WAHA Core** संस्करण का उपयोग कर रहे हैं। यदि आपको यह प्रोजेक्ट उपयोगी लगता है, तो कृपया इसके विकास का समर्थन करने पर विचार करें।
@@ -179,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,8 +1,9 @@
# Indonesian translations for ChatWoot integration
LOCALE_NAME: '🇮🇩 Bahasa Indonesia (Indonesian)'
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...
@@ -19,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.
@@ -81,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.
@@ -90,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.
@@ -108,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}}}
@@ -154,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}}}**
@@ -171,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.
@@ -179,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,8 +1,9 @@
# Punjabi translations for ChatWoot integration
LOCALE_NAME: '🇵🇰 ਪੰਜਾਬੀ (Punjabi)'
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}} ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰ ਰਿਹਾ ਹੈ...
@@ -19,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 ਇਨਬਾਕਸ ਨਾਲ ਜੋੜਿਆ ਗਿਆ ਹੈ।
@@ -81,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
ਆਓ ਸ਼ੁਰੂ ਕਰੀਏ! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **ਡਿਸਕਨੈਕਟ ਕੀਤਾ: ਵਟਸਐਪ ਸੈਸ਼ਨ ਤੋਂ ChatWoot ਇਨਬਾਕਸ**
❌ ਵਟਸਐਪ ਸੈਸ਼ਨ **{{{name}}}** ਇਸ ChatWoot ਇਨਬਾਕਸ ਤੋਂ ਡਿਸਕਨੈਕਟ ਕੀਤਾ ਗਿਆ ਹੈ।
@@ -90,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** - ਸੈਸ਼ਨ ਸ਼ੁਰੂ ਕਰੋ।
@@ -108,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}}}
@@ -154,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}}}**
@@ -171,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 [**🆕 ਬਦਲਾਅ ਲੌਗ**]({{{changelogUrl}}}) ਵਿੱਚ ਨਵਾਂ ਕੀ ਹੈ ਦੇਖੋ
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **WAHA ਦਾ ਸਮਰਥਨ ਕਰੋ!**
ਤੁਸੀਂ ਮੁਫਤ **WAHA Core** ਵਰਜਨ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੇ ਹੋ। ਜੇ ਤੁਹਾਨੂੰ ਇਹ ਪ੍ਰੋਜੈਕਟ ਲਾਭਦਾਇਕ ਲੱਗਦਾ ਹੈ, ਤਾਂ ਕਿਰਪਾ ਕਰਕੇ ਇਸਦੇ ਵਿਕਾਸ ਦਾ ਸਮਰਥਨ ਕਰਨ 'ਤੇ ਵਿਚਾਰ ਕਰੋ।
@@ -179,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,8 +1,9 @@
# Portuguese translations for ChatWoot integration
LOCALE_NAME: '🇧🇷🇵🇹 Português (Portuguese)'
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...
@@ -19,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.
@@ -84,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.
@@ -93,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.
@@ -111,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}}}
@@ -157,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}}}**
@@ -174,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.
@@ -182,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,8 +1,9 @@
# Russian translations for ChatWoot integration
LOCALE_NAME: '🇷🇺 Русский (Russian)'
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}} секунд...
@@ -19,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.
@@ -81,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
Давайте начнем! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **ОТКЛЮЧЕНО: Сессия WhatsApp от ChatWoot Inbox**
❌ Сессия WhatsApp **{{{name}}}** была отключена от этого ChatWoot Inbox.
@@ -90,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** - запустить сессию.
@@ -108,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}}}
@@ -154,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}}}**
@@ -171,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 Узнайте, что нового в [**🆕 Списке изменений**]({{{changelogUrl}}})
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **Поддержите WAHA!**
Вы используете бесплатную версию **WAHA Core**. Если вы находите этот проект полезным, пожалуйста, рассмотрите возможность поддержать его разработку.
@@ -179,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,8 +1,9 @@
# Turkish translations for ChatWoot integration
LOCALE_NAME: '🇹🇷 Türkçe (Turkish)'
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...
@@ -19,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ı.
@@ -81,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.
@@ -90,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.
@@ -108,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}}}
@@ -154,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}}}**
@@ -171,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.
@@ -179,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,8 +1,9 @@
# Ukrainian translations for ChatWoot integration
LOCALE_NAME: '🇺🇦 Українська (Ukrainian)'
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}} секунд...
@@ -19,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.
@@ -81,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
Давайте почнемо! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **ВІДКЛЮЧЕНО: Сесія WhatsApp від Вхідних ChatWoot**
❌ Сесія WhatsApp **{{{name}}}** була відключена від цих Вхідних ChatWoot.
@@ -90,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** - запустити сесію.
@@ -108,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}}}
@@ -154,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}}}**
@@ -171,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 Перевірте, що нового у [**🆕 Журналі змін**]({{{changelogUrl}}})
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **Підтримайте WAHA!**
Ви використовуєте безкоштовну версію **WAHA Core**. Якщо ви вважаєте цей проект корисним, будь ласка, розгляньте можливість підтримати його розробку.
@@ -179,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,9 +1,9 @@
# 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'
locale.name: '🇵🇰 اردو (Urdu)'
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}} سیکنڈ میں دوبارہ کوشش کر رہا ہے...
@@ -20,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 انبکس سے منسلک ہو گیا ہے۔
@@ -82,7 +83,7 @@ APP_CONNECTED_MESSAGE: |
آئیے شروع کریں! 🚀
APP_DISCONNECTED_MESSAGE: |
app.disconnected.message: |
🔌 **منقطع: واٹس ایپ سیشن ChatWoot انبکس سے**
❌ واٹس ایپ سیشن **{{{name}}}** اس ChatWoot انبکس سے منقطع ہو گیا ہے۔
@@ -91,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** - سیشن شروع کریں۔
@@ -109,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: |
⚠️ **اس ChatWoot انبکس کے ساتھ واٹس ایپ اکاؤنٹ کو منسلک کرنے کے لیے اپنے فون پر QR کوڈ اسکین کریں!**
APP_SERVER_VERSION_AND_STATUS: |
app.server.version.and.status: |
**ورژن:**
```
{{{version}}}
@@ -155,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}}}**
@@ -172,7 +178,7 @@ APP_NEW_VERSION_AVAILABLE: |
👉 [**🆕 تبدیلیوں کی فہرست**]({{{changelogUrl}}}) میں نیا کیا ہے دیکھیں
APP_CORE_VERSION_USED: |
waha.core.version.used: |
❤️ **WAHA کی حمایت کریں!**
آپ مفت **WAHA Core** ورژن استعمال کر رہے ہیں۔ اگر آپ کو یہ پروجیکٹ مفید لگتا ہے، تو براہ کرم اس کی ترقی کی حمایت کرنے پر غور کریں۔
@@ -180,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}}})
+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;
};
};
-223
View File
@@ -1,223 +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',
UR_PK = 'ur-PK',
}
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,
LocaleCode.UR_PK,
];
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>> = {};
export const LOCALE_NAMES: Map<LocaleCode, string> = new Map();
try {
locales = loadLocales(LOCALES);
for (const [locale, strings] of Object.entries(locales)) {
LOCALE_NAMES.set(locale as LocaleCode, strings['LOCALE_NAME']);
}
} 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;
}
+86 -23
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> {
@@ -1838,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(
@@ -1864,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 {
@@ -1888,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) || '';
@@ -1922,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(
@@ -1933,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(),
);
@@ -2000,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(),
),
@@ -2030,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'),
@@ -2163,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;
@@ -2192,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);
@@ -2225,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),
@@ -2236,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,
@@ -2246,7 +2284,7 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
vCards: message.vCards,
replyTo: replyTo,
_data: message,
});
};
}
protected extractReplyTo(message): ReplyToMessage | null {
@@ -2438,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);
}
@@ -2702,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() {
+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 };
+70
View File
@@ -89,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)
@@ -125,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: {
@@ -137,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 {
@@ -155,6 +220,11 @@ export class MeInfo {
@ChatIdProperty()
id: string;
@ApiProperty({
example: '123123@lid',
})
lid?: string;
pushName: string;
}
+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.8.1',
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 };
+2 -2
View File
@@ -2,11 +2,11 @@
"waha": {
"gows": {
"repo": "devlikeapro/gows",
"ref": "v1.0.11"
"ref": "v1.0.14"
},
"dashboard": {
"repo": "devlikeapro/dashboard",
"ref": "59ebfe14fc89b42f77dd9da173d3e7e2983beb21"
"ref": "042f5bd9cc14440fde062d313bdb8e1bdc511a50"
}
}
}
+221 -1554
View File
File diff suppressed because it is too large. Load diff